diff --git a/packages/doenetml-iframe/src/test-main.tsx b/packages/doenetml-iframe/src/test-main.tsx index 24c09d84bb..59f1767fb6 100644 --- a/packages/doenetml-iframe/src/test-main.tsx +++ b/packages/doenetml-iframe/src/test-main.tsx @@ -23,12 +23,72 @@ const STANDALONE_CSS_BLOB_URL = URL.createObjectURL( new Blob([STANDALONE_CSS], { type: "text/css" }), ); +const SOURCE_STORAGE_KEY = "doenetml-iframe-dev-source"; +const SAVE_DEBOUNCE_MS = 500; + +const DEFAULT_EDITOR_SOURCE = `

Use this to test DoenetML

+ + + + + `; + +let saveTimer: number | null = null; + +function getInitialSource(): string { + try { + return ( + localStorage.getItem(SOURCE_STORAGE_KEY) ?? DEFAULT_EDITOR_SOURCE + ); + } catch { + return DEFAULT_EDITOR_SOURCE; + } +} + +// Wired to the editor's immediate (per-keystroke) change callback, so debounce +// the writes: calling localStorage.setItem synchronously on every keystroke can +// noticeably block the UI for larger documents. +let pendingSource: string | null = null; + +window.addEventListener("pagehide", () => { + if (pendingSource === null) { + return; + } + if (saveTimer !== null) { + window.clearTimeout(saveTimer); + saveTimer = null; + } + try { + localStorage.setItem(SOURCE_STORAGE_KEY, pendingSource); + } catch { + // Ignore localStorage failures in constrained environments. + } + pendingSource = null; +}); + +function saveSource(source: string) { + pendingSource = source; + if (saveTimer !== null) { + window.clearTimeout(saveTimer); + } + saveTimer = window.setTimeout(() => { + saveTimer = null; + try { + localStorage.setItem(SOURCE_STORAGE_KEY, source); + } catch { + // Ignore localStorage failures in constrained environments. + } + pendingSource = null; + }, SAVE_DEBOUNCE_MS); +} + const root = ReactDOM.createRoot(document.getElementById("root")!); root.render(); function App() { const DOENET_LEGACY_VERSION = "0.6.5"; const editorRef = useRef(null); + const [initialEditorSource] = React.useState(getInitialSource); return ( @@ -95,18 +155,14 @@ function App() {

Use this to test DoenetML

- - - - - `} + doenetML={initialEditorSource} standaloneUrl={STANDALONE_BLOB_URL} cssUrl={STANDALONE_CSS_BLOB_URL} activityId={"a"} showDiagnostics={true} showResponses={false} fetchExternalDoenetML={fetchExternalDoenetML} + immediateDoenetmlChangeCallback={saveSource} />
); diff --git a/packages/doenetml-iframe/vite.config.ts b/packages/doenetml-iframe/vite.config.ts index c5e514f13c..22e651c4df 100644 --- a/packages/doenetml-iframe/vite.config.ts +++ b/packages/doenetml-iframe/vite.config.ts @@ -43,6 +43,9 @@ export default defineConfig({ }), suppressLogPlugin(), ], + server: { + host: "0.0.0.0", + }, build: { minify: false, outDir: "dist/component", diff --git a/packages/standalone/index.html b/packages/standalone/index.html index bee77c21ea..fd7f067d56 100644 --- a/packages/standalone/index.html +++ b/packages/standalone/index.html @@ -12,41 +12,41 @@
diff --git a/packages/standalone/src/test-main.tsx b/packages/standalone/src/test-main.tsx index 81cff13ef2..ecff36c5af 100644 --- a/packages/standalone/src/test-main.tsx +++ b/packages/standalone/src/test-main.tsx @@ -7,13 +7,55 @@ import { renderDoenetEditorToContainer, } from "./index"; +const SOURCE_STORAGE_KEY_PREFIX = "doenetml-standalone-dev-source-"; +const SAVE_DEBOUNCE_MS = 500; + +const saveTimers = new Map>(); + +// Wired to the editor's immediate (per-keystroke) change callback, so debounce +// the writes: calling localStorage.setItem synchronously on every keystroke can +// noticeably block the UI for larger documents. +function saveSource(key: string, source: string) { + const pending = saveTimers.get(key); + if (pending !== undefined) { + window.clearTimeout(pending); + } + saveTimers.set( + key, + window.setTimeout(() => { + saveTimers.delete(key); + try { + localStorage.setItem(key, source); + } catch { + // Ignore localStorage failures in constrained environments. + } + }, SAVE_DEBOUNCE_MS), + ); +} + document.addEventListener("DOMContentLoaded", () => { document.querySelectorAll(".doenetml-viewer").forEach((container) => { console.log("Rendering doenet viewer to container", container); renderDoenetViewerToContainer(container); }); - document.querySelectorAll(".doenetml-editor").forEach((container) => { - console.log("Rendering doenet editor to container", container); - renderDoenetEditorToContainer(container); - }); + document + .querySelectorAll(".doenetml-editor") + .forEach((container, index) => { + console.log("Rendering doenet editor to container", container); + const storageKey = `${SOURCE_STORAGE_KEY_PREFIX}${index}`; + let storedSource: string | null = null; + try { + storedSource = localStorage.getItem(storageKey); + } catch { + // Ignore localStorage failures in constrained environments. + } + renderDoenetEditorToContainer( + container, + storedSource ?? undefined, + { + immediateDoenetmlChangeCallback: (source: string) => + saveSource(storageKey, source), + }, + ); + }); }); diff --git a/packages/standalone/vite.config.ts b/packages/standalone/vite.config.ts index 44154318ad..3c7bbe3202 100644 --- a/packages/standalone/vite.config.ts +++ b/packages/standalone/vite.config.ts @@ -21,6 +21,9 @@ export default defineConfig({ }), suppressLogPlugin(), ], + server: { + host: "0.0.0.0", + }, build: { minify: true, sourcemap: true,