summaryrefslogtreecommitdiff
path: root/apps/web/src
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-04-19 07:42:52 +0200
committerMaze Winther <mazewinther@gmail.com>2026-04-19 07:42:52 +0200
commit729d10592f4e802f91c555c309ff3ba4b119f843 (patch)
treea547a6d1ba90ca9cc03f406e0160df0af987644d /apps/web/src
parent9da365bc01fc54f6d3f0ca9c3212eee4c0cee7cd (diff)
downloadopencut-729d10592f4e802f91c555c309ff3ba4b119f843.tar.gz
fix: WebGL frame rendering and opencut-wasm 0.2.8
Diffstat (limited to 'apps/web/src')
-rw-r--r--apps/web/src/components/providers/editor-provider.tsx18
-rw-r--r--apps/web/src/core/managers/project-manager.ts10
-rw-r--r--apps/web/src/services/renderer/effect-preview.ts66
3 files changed, 58 insertions, 36 deletions
diff --git a/apps/web/src/components/providers/editor-provider.tsx b/apps/web/src/components/providers/editor-provider.tsx
index cac954ba..98e6d4c6 100644
--- a/apps/web/src/components/providers/editor-provider.tsx
+++ b/apps/web/src/components/providers/editor-provider.tsx
@@ -10,7 +10,10 @@ import { useKeybindingsStore } from "@/stores/keybindings-store";
import { useTimelineStore } from "@/stores/timeline-store";
import { useEditorActions } from "@/hooks/actions/use-editor-actions";
import { loadFontAtlas } from "@/lib/fonts/google-fonts";
-import { initializeGpuRenderer, isGpuAvailable } from "@/services/renderer/gpu-renderer";
+import {
+ initializeGpuRenderer,
+ isGpuAvailable,
+} from "@/services/renderer/gpu-renderer";
interface EditorProviderProps {
projectId: string;
@@ -62,9 +65,16 @@ export function EditorProvider({ projectId, children }: EditorProviderProps) {
setIsLoading(false);
}
} else {
- setError(
- err instanceof Error ? err.message : "Failed to load project",
- );
+ const wasmPanic = (window as Window & { __wasmPanic?: string })
+ .__wasmPanic;
+ if (wasmPanic) {
+ delete (window as Window & { __wasmPanic?: string }).__wasmPanic;
+ setError(wasmPanic);
+ } else {
+ setError(
+ err instanceof Error ? err.message : "Failed to load project",
+ );
+ }
setIsLoading(false);
}
}
diff --git a/apps/web/src/core/managers/project-manager.ts b/apps/web/src/core/managers/project-manager.ts
index 81b237da..8555786d 100644
--- a/apps/web/src/core/managers/project-manager.ts
+++ b/apps/web/src/core/managers/project-manager.ts
@@ -167,9 +167,13 @@ export class ProjectManager {
});
if (!project.metadata.thumbnail) {
- const didUpdateThumbnail = await this.updateThumbnailFromTimeline();
- if (didUpdateThumbnail) {
- await this.saveCurrentProject();
+ try {
+ const didUpdateThumbnail = await this.updateThumbnailFromTimeline();
+ if (didUpdateThumbnail) {
+ await this.saveCurrentProject();
+ }
+ } catch (error) {
+ console.error("Failed to generate project thumbnail:", error);
}
}
} catch (error) {
diff --git a/apps/web/src/services/renderer/effect-preview.ts b/apps/web/src/services/renderer/effect-preview.ts
index 8e081039..06e64d0e 100644
--- a/apps/web/src/services/renderer/effect-preview.ts
+++ b/apps/web/src/services/renderer/effect-preview.ts
@@ -18,11 +18,7 @@ class EffectPreviewService {
this.loadPreviewImage();
}
- onPreviewImageReady({
- callback,
- }: {
- callback: () => void;
- }): () => void {
+ onPreviewImageReady({ callback }: { callback: () => void }): () => void {
this.onReadyCallbacks.add(callback);
return () => this.onReadyCallbacks.delete(callback);
}
@@ -39,35 +35,47 @@ class EffectPreviewService {
uniformDimensions?: { width: number; height: number };
}): void {
const size = PREVIEW_SIZE;
+ const targetCtx = targetCanvas.getContext(
+ "2d",
+ ) as CanvasRenderingContext2D | null;
+ if (!targetCtx) {
+ return;
+ }
+
+ targetCanvas.width = size;
+ targetCanvas.height = size;
+
const source = this.getTestSource({ width: size, height: size });
- if (!source) return;
+ if (!source) {
+ targetCtx.clearRect(0, 0, size, size);
+ return;
+ }
- const definition = effectsRegistry.get(effectType);
- const resolvedParams =
- Object.keys(params).length > 0
- ? params
- : buildDefaultParamValues(definition.params);
+ try {
+ const definition = effectsRegistry.get(effectType);
+ const resolvedParams =
+ Object.keys(params).length > 0
+ ? params
+ : buildDefaultParamValues(definition.params);
- const passes = resolveEffectPasses({
- definition,
- effectParams: resolvedParams,
- width: uniformDimensions?.width ?? size,
- height: uniformDimensions?.height ?? size,
- });
- const result = this.applyGpuEffect({
- source,
- width: size,
- height: size,
- passes,
- });
+ const passes = resolveEffectPasses({
+ definition,
+ effectParams: resolvedParams,
+ width: uniformDimensions?.width ?? size,
+ height: uniformDimensions?.height ?? size,
+ });
+ const result = this.applyGpuEffect({
+ source,
+ width: size,
+ height: size,
+ passes,
+ });
- const targetCtx = targetCanvas.getContext(
- "2d",
- ) as CanvasRenderingContext2D | null;
- if (targetCtx) {
- targetCanvas.width = size;
- targetCanvas.height = size;
targetCtx.drawImage(result, 0, 0, size, size);
+ } catch (error) {
+ console.warn("Failed to render effect preview", { effectType, error });
+ targetCtx.clearRect(0, 0, size, size);
+ targetCtx.drawImage(source, 0, 0, size, size);
}
}