summaryrefslogtreecommitdiff
path: root/apps/web/src/core/managers/renderer-manager.ts
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-03-25 16:47:22 +0100
committerMaze Winther <mazewinther@gmail.com>2026-03-29 15:48:22 +0200
commit8db3bead13441a5882d8689dfc6a9f09c82375d9 (patch)
treee9d0cafa93904ffcbec3188c37961bcc16c6422d /apps/web/src/core/managers/renderer-manager.ts
parent39ea298a9ccba3104c549c0ca079c231981811f5 (diff)
downloadopencut-8db3bead13441a5882d8689dfc6a9f09c82375d9.tar.gz
feat: masks, properties refactor, shaders, storage migrations, and more
Diffstat (limited to 'apps/web/src/core/managers/renderer-manager.ts')
-rw-r--r--apps/web/src/core/managers/renderer-manager.ts60
1 files changed, 52 insertions, 8 deletions
diff --git a/apps/web/src/core/managers/renderer-manager.ts b/apps/web/src/core/managers/renderer-manager.ts
index 601a007e..75184c53 100644
--- a/apps/web/src/core/managers/renderer-manager.ts
+++ b/apps/web/src/core/managers/renderer-manager.ts
@@ -1,6 +1,6 @@
import type { EditorCore } from "@/core";
import type { RootNode } from "@/services/renderer/nodes/root-node";
-import type { ExportOptions, ExportResult } from "@/types/export";
+import type { ExportOptions, ExportResult } from "@/lib/export";
import { CanvasRenderer } from "@/services/renderer/canvas-renderer";
import { SceneExporter } from "@/services/renderer/scene-exporter";
import { buildScene } from "@/services/renderer/scene-builder";
@@ -8,6 +8,10 @@ import { createTimelineAudioBuffer } from "@/lib/media/audio";
import { formatTimeCode, getLastFrameTime } from "@/lib/time";
import { downloadBlob } from "@/utils/browser";
+type SnapshotResult =
+ | { success: true; blob: Blob; filename: string }
+ | { success: false; error: string };
+
export class RendererManager {
private renderTree: RootNode | null = null;
private listeners = new Set<() => void>();
@@ -24,6 +28,45 @@ export class RendererManager {
}
async saveSnapshot(): Promise<{ success: boolean; error?: string }> {
+ const snapshot = await this.createSnapshot();
+ if (!snapshot.success) {
+ return snapshot;
+ }
+
+ downloadBlob({ blob: snapshot.blob, filename: snapshot.filename });
+ return { success: true };
+ }
+
+ async copySnapshot(): Promise<{ success: boolean; error?: string }> {
+ if (typeof ClipboardItem === "undefined" || !navigator.clipboard?.write) {
+ return {
+ success: false,
+ error: "Clipboard image copy is not supported in this browser",
+ };
+ }
+
+ const snapshot = await this.createSnapshot();
+ if (!snapshot.success) {
+ return snapshot;
+ }
+
+ try {
+ await navigator.clipboard.write([
+ new ClipboardItem({
+ [snapshot.blob.type || "image/png"]: snapshot.blob,
+ }),
+ ]);
+ return { success: true };
+ } catch (error) {
+ console.error("Copy snapshot failed:", error);
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : "Unknown error",
+ };
+ }
+ }
+
+ private async createSnapshot(): Promise<SnapshotResult> {
try {
const renderTree = this.getRenderTree();
const activeProject = this.editor.project.getActive();
@@ -70,15 +113,14 @@ export class RendererManager {
timeInSeconds: renderTime,
fps,
}).replace(/:/g, "-");
- const safeName = activeProject.metadata.name
- .replace(/[<>:"/\\|?*]/g, "-")
- .trim() || "snapshot";
+ const safeName =
+ activeProject.metadata.name.replace(/[<>:"/\\|?*]/g, "-").trim() ||
+ "snapshot";
const filename = `${safeName}-${timecode}.png`;
- downloadBlob({ blob, filename });
- return { success: true };
+ return { success: true, blob, filename };
} catch (error) {
- console.error("Save snapshot failed:", error);
+ console.error("Snapshot capture failed:", error);
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
@@ -193,6 +235,8 @@ export class RendererManager {
}
private notify(): void {
- this.listeners.forEach((fn) => fn());
+ this.listeners.forEach((fn) => {
+ fn();
+ });
}
}