diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-13 04:40:56 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-13 05:03:10 +0200 |
| commit | 2272598d420cf078aaa3928246bfa3a414f6eddb (patch) | |
| tree | b52460a2e3576c9e517ef8a9dd86959a68f31b69 /apps/web/src/core | |
| parent | 5a2aaf3601b6b050603f51e487a31fb07862a21f (diff) | |
| download | opencut-2272598d420cf078aaa3928246bfa3a414f6eddb.tar.gz | |
feat: clipboard with keyframe paste
Made-with: Cursor
Diffstat (limited to 'apps/web/src/core')
| -rw-r--r-- | apps/web/src/core/index.ts | 4 | ||||
| -rw-r--r-- | apps/web/src/core/managers/clipboard-manager.ts | 81 |
2 files changed, 85 insertions, 0 deletions
diff --git a/apps/web/src/core/index.ts b/apps/web/src/core/index.ts index 8ee76e28..e93b937d 100644 --- a/apps/web/src/core/index.ts +++ b/apps/web/src/core/index.ts @@ -8,6 +8,7 @@ import { CommandManager } from "./managers/commands"; import { SaveManager } from "./managers/save-manager"; import { AudioManager } from "./managers/audio-manager"; import { SelectionManager } from "./managers/selection-manager"; +import { ClipboardManager } from "./managers/clipboard-manager"; import { registerDefaultEffects } from "@/lib/effects"; import { registerDefaultMasks } from "@/lib/masks"; @@ -23,6 +24,7 @@ export class EditorCore { public readonly save: SaveManager; public readonly audio: AudioManager; public readonly selection: SelectionManager; + public readonly clipboard: ClipboardManager; private constructor() { registerDefaultEffects(); @@ -37,6 +39,8 @@ export class EditorCore { this.save = new SaveManager(this); this.audio = new AudioManager(this); this.selection = new SelectionManager(this); + this.clipboard = new ClipboardManager(this); + this.playback.bindTimelineScope(); this.command.registerReactor(() => { const activeScene = this.scenes.getActiveSceneOrNull(); if (!activeScene) { diff --git a/apps/web/src/core/managers/clipboard-manager.ts b/apps/web/src/core/managers/clipboard-manager.ts new file mode 100644 index 00000000..b19343df --- /dev/null +++ b/apps/web/src/core/managers/clipboard-manager.ts @@ -0,0 +1,81 @@ +import type { EditorCore } from "@/core"; +import { + buildPasteClipboardCommand, + copyClipboardEntry, + type ClipboardEntry, + type CopyContext, + type PasteContext, +} from "@/lib/clipboard"; + +export class ClipboardManager { + private entry: ClipboardEntry | null = null; + private listeners = new Set<() => void>(); + + constructor(private editor: EditorCore) {} + + getEntry(): ClipboardEntry | null { + return this.entry; + } + + hasEntry(): boolean { + return this.entry !== null; + } + + copy(): boolean { + const entry = copyClipboardEntry({ + context: this.getCopyContext(), + }); + if (!entry) { + return false; + } + + this.entry = entry; + this.notify(); + return true; + } + + paste({ time }: { time?: number } = {}): boolean { + if (!this.entry) { + return false; + } + + const command = buildPasteClipboardCommand({ + entry: this.entry, + context: this.getPasteContext({ time }), + }); + if (!command) { + return false; + } + + this.editor.command.execute({ command }); + return true; + } + + subscribe(listener: () => void): () => void { + this.listeners.add(listener); + return () => this.listeners.delete(listener); + } + + private getCopyContext(): CopyContext { + return { + editor: this.editor, + selectedElements: this.editor.selection.getSelectedElements(), + selectedKeyframes: this.editor.selection.getSelectedKeyframes(), + }; + } + + private getPasteContext({ time }: { time?: number }): PasteContext { + return { + editor: this.editor, + selectedElements: this.editor.selection.getSelectedElements(), + selectedKeyframes: this.editor.selection.getSelectedKeyframes(), + time: time ?? this.editor.playback.getCurrentTime(), + }; + } + + private notify(): void { + this.listeners.forEach((listener) => { + listener(); + }); + } +} |
