feat: masks, properties refactor, shaders, storage migrations, and more

This commit is contained in:
Maze Winther
2026-03-25 16:47:22 +01:00
parent 4aca4e7756
commit 08f5b92c3d
690 changed files with 35574 additions and 7308 deletions
+52 -8
View File
@@ -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();
});
}
}