diff --git a/frontend/src/components/FightViewer.vue b/frontend/src/components/FightViewer.vue index da62e76..37ccc3d 100644 --- a/frontend/src/components/FightViewer.vue +++ b/frontend/src/components/FightViewer.vue @@ -26,6 +26,7 @@ let scene: FightSceneController | null = null const sceneReady = ref(false) let cleanupTimerHandle: ReturnType | null = null let destroyed = false +const contextLost = ref(false) const isReplaying = ref(false) const ttsProgress = ref(-1) @@ -195,6 +196,18 @@ async function initScene() { container.prepend(newCanvas) } canvasRef.value = newCanvas + contextLost.value = false + + // Handle WebGL/Canvas context loss (GPU pressure, tab backgrounding, etc.) + newCanvas.addEventListener('webglcontextlost', (e) => { + e.preventDefault() // Allow context restoration + contextLost.value = true + sceneReady.value = false + }) + newCanvas.addEventListener('webglcontextrestored', () => { + contextLost.value = false + initScene() // Re-create the scene with fresh context + }) // Wrap scene creation in try/catch + timeout so a mobile sprite loading failure // or hang never blocks the overlay/voice/round flow @@ -720,6 +733,13 @@ async function _doReplay() {
+ +
+
+

CANVAS CONTEXT LOST

+

Recovering...

+
+