summaryrefslogtreecommitdiff
path: root/apps/web/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/core')
-rw-r--r--apps/web/src/core/index.ts4
-rw-r--r--apps/web/src/core/managers/clipboard-manager.ts81
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();
+ });
+ }
+}