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 @@