diff --git a/frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts b/frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts index 12db75a0c0..b586152312 100644 --- a/frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts +++ b/frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts @@ -1129,7 +1129,10 @@ export const useSceneStore = create((set, get, store) => ({ const base64ToFile = (base64: string, filename: string): File => { const arr = base64.split(","); - const mime = arr[0].match(/:([^;]*);/)?.[1] || "image/png"; + // data:;base64: the text between the first ":" and the ";" after it. + const start = arr[0].indexOf(":") + 1; + const end = arr[0].indexOf(";", start); + const mime = (start > 0 && end !== -1 && arr[0].slice(start, end)) || "image/png"; const bstr = atob(arr[1]); let n = bstr.length; const u8arr = new Uint8Array(n); diff --git a/frontend/libs/components/pagescene/src/lib/contexts/EngineContext/EngineProvider.tsx b/frontend/libs/components/pagescene/src/lib/contexts/EngineContext/EngineProvider.tsx index c2a3cf718c..deec2df039 100644 --- a/frontend/libs/components/pagescene/src/lib/contexts/EngineContext/EngineProvider.tsx +++ b/frontend/libs/components/pagescene/src/lib/contexts/EngineContext/EngineProvider.tsx @@ -2,6 +2,7 @@ import { ReactNode, useEffect, useRef, useState } from "react"; import { EngineContext, setActiveEditor } from "./EngineContext"; import Editor from "../../engine/editor"; +import { ensureAmmoLoaded } from "../../engine/ammoLoader"; import type { PageSceneAdapter } from "../../adapter"; import { getSceneGenerationMetaData } from "../../sceneMetadata"; import { usePageSceneStore } from "../../PageSceneStore"; @@ -61,6 +62,9 @@ export const EngineProvider = ({ // them to null on unmount). if (!sceneContainer || !editorCanvas || !camViewCanvas) return undefined; + // Ammo.js WASM is a global side-effect script the physics path depends on; the singleton + // appends it to document.body once, however often the Editor is constructed. + void ensureAmmoLoaded(); const newEditor = new Editor(adapterRef.current); newEditor.initialize({ sceneToken: sceneToken || "", diff --git a/frontend/libs/components/pagescene/src/lib/engine/editor.ts b/frontend/libs/components/pagescene/src/lib/engine/editor.ts index 98d127cee6..a980c1d459 100644 --- a/frontend/libs/components/pagescene/src/lib/engine/editor.ts +++ b/frontend/libs/components/pagescene/src/lib/engine/editor.ts @@ -31,7 +31,6 @@ import { ModalTransformController } from "./editor/ModalTransformController"; import Stats from "three/examples/jsm/libs/stats.module.js"; import { SparkRenderer } from "@sparkjsdev/spark"; import { usePageSceneStore } from "../PageSceneStore"; -import { ensureAmmoLoaded } from "./ammoLoader"; import { EngineEventBus } from "./events/EngineEventBus"; import { EngineStoreBridge } from "./EngineStoreBridge"; import { @@ -187,11 +186,6 @@ class Editor { this.adapter = adapter; this.can_initialize = true; this.stats = new Stats(); - // Ammo.js WASM is a global side-effect script the physics path - // depends on. The singleton ensures it's appended to document.body - // exactly once, even across Editor re-construction. - void ensureAmmoLoaded(); - // Version and name. this.version = 2.0; // Clock, scene and camera essentials. diff --git a/frontend/libs/components/promptbox/src/lib/MentionTextarea.tsx b/frontend/libs/components/promptbox/src/lib/MentionTextarea.tsx index 23196ab9b0..174562c1fe 100644 --- a/frontend/libs/components/promptbox/src/lib/MentionTextarea.tsx +++ b/frontend/libs/components/promptbox/src/lib/MentionTextarea.tsx @@ -1587,6 +1587,7 @@ export const MentionTextarea = forwardRef( role="textbox" aria-multiline="true" aria-label={placeholder || "Prompt"} + tabIndex={disabled ? -1 : 0} ref={editorRef} contentEditable={!disabled} onInput={handleInput}