From d2e45e525b48461560a0f6cc1298fe48c90f0c98 Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Tue, 31 Mar 2026 01:38:24 +0200 Subject: [PATCH] Update app.css and ViewportCanvas.tsx for WebGL fallback handling --- src/app/app.css | 21 ++++++++++++ src/viewport-three/ViewportCanvas.tsx | 46 +++++++++++++++++++++------ 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/src/app/app.css b/src/app/app.css index 7425867e..e6fea002 100644 --- a/src/app/app.css +++ b/src/app/app.css @@ -290,6 +290,27 @@ button:disabled { height: 100%; } +.viewport-canvas__fallback { + position: absolute; + inset: 18px; + display: flex; + flex-direction: column; + justify-content: flex-end; + gap: 8px; + padding: 18px; + color: #f3e8da; + background: linear-gradient(180deg, rgba(8, 10, 14, 0.12) 0%, rgba(8, 10, 14, 0.58) 100%); + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: 18px; +} + +.viewport-canvas__fallback-title { + font-size: 0.78rem; + font-weight: 700; + letter-spacing: 0.14em; + text-transform: uppercase; +} + .status-bar { display: flex; align-items: center; diff --git a/src/viewport-three/ViewportCanvas.tsx b/src/viewport-three/ViewportCanvas.tsx index c4efe056..4268149b 100644 --- a/src/viewport-three/ViewportCanvas.tsx +++ b/src/viewport-three/ViewportCanvas.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import type { WorldSettings } from "../document/scene-document"; @@ -11,6 +11,7 @@ interface ViewportCanvasProps { export function ViewportCanvas({ world }: ViewportCanvasProps) { const containerRef = useRef(null); const hostRef = useRef(null); + const [viewportMessage, setViewportMessage] = useState(null); useEffect(() => { const container = containerRef.current; @@ -19,19 +20,46 @@ export function ViewportCanvas({ world }: ViewportCanvasProps) { return; } - const viewportHost = new ViewportHost(); - hostRef.current = viewportHost; - viewportHost.mount(container); + const testCanvas = document.createElement("canvas"); + const hasWebGl = + testCanvas.getContext("webgl2") !== null || + testCanvas.getContext("webgl") !== null || + testCanvas.getContext("experimental-webgl") !== null; - return () => { - viewportHost.dispose(); - hostRef.current = null; - }; + if (!hasWebGl) { + setViewportMessage("WebGL is unavailable in this browser environment. The viewport shell is visible, but rendering is disabled."); + return; + } + + try { + const viewportHost = new ViewportHost(); + hostRef.current = viewportHost; + viewportHost.mount(container); + setViewportMessage(null); + + return () => { + viewportHost.dispose(); + hostRef.current = null; + }; + } catch (error) { + const message = error instanceof Error ? error.message : "Viewport initialization failed."; + setViewportMessage(`Viewport initialization failed: ${message}`); + return; + } }, []); useEffect(() => { hostRef.current?.updateWorld(world); }, [world]); - return
; + return ( +
+ {viewportMessage === null ? null : ( +
+
Viewport Unavailable
+
{viewportMessage}
+
+ )} +
+ ); }