@doenet/doenetml-iframe 0.7.20-dev.324 → 0.7.20-dev.327

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import * as React__default from "react";
2
- import React__default__default, { createContext, useMemo, useCallback, useLayoutEffect, useEffect, useRef, useState, useContext, isValidElement } from "react";
1
+ import * as React from "react";
2
+ import React__default, { createContext, useMemo, useCallback, useLayoutEffect, useEffect, useRef, useState, useContext, isValidElement } from "react";
3
3
  import { MathJaxBaseContext, MathJax } from "better-react-mathjax";
4
4
  import { createRoot } from "react-dom/client";
5
5
  import { createPortal } from "react-dom";
@@ -337,7 +337,7 @@ var DefaultContext = {
337
337
  style: void 0,
338
338
  attr: void 0
339
339
  };
340
- var IconContext = React__default__default.createContext && /* @__PURE__ */ React__default__default.createContext(DefaultContext);
340
+ var IconContext = React__default.createContext && /* @__PURE__ */ React__default.createContext(DefaultContext);
341
341
  var _excluded = ["attr", "size", "title"];
342
342
  function _objectWithoutProperties(e2, t4) {
343
343
  if (null == e2) return {};
@@ -405,12 +405,12 @@ function _toPrimitive(t4, r2) {
405
405
  return ("string" === r2 ? String : Number)(t4);
406
406
  }
407
407
  function Tree2Element(tree) {
408
- return tree && tree.map((node, i2) => /* @__PURE__ */ React__default__default.createElement(node.tag, _objectSpread$5({
408
+ return tree && tree.map((node, i2) => /* @__PURE__ */ React__default.createElement(node.tag, _objectSpread$5({
409
409
  key: i2
410
410
  }, node.attr), Tree2Element(node.child)));
411
411
  }
412
412
  function GenIcon(data) {
413
- return (props) => /* @__PURE__ */ React__default__default.createElement(IconBase, _extends$2({
413
+ return (props) => /* @__PURE__ */ React__default.createElement(IconBase, _extends$2({
414
414
  attr: _objectSpread$5({}, data.attr)
415
415
  }, props), Tree2Element(data.child));
416
416
  }
@@ -421,7 +421,7 @@ function IconBase(props) {
421
421
  var className;
422
422
  if (conf.className) className = conf.className;
423
423
  if (props.className) className = (className ? className + " " : "") + props.className;
424
- return /* @__PURE__ */ React__default__default.createElement("svg", _extends$2({
424
+ return /* @__PURE__ */ React__default.createElement("svg", _extends$2({
425
425
  stroke: "currentColor",
426
426
  fill: "currentColor",
427
427
  strokeWidth: "0"
@@ -433,9 +433,9 @@ function IconBase(props) {
433
433
  height: computedSize,
434
434
  width: computedSize,
435
435
  xmlns: "http://www.w3.org/2000/svg"
436
- }), title && /* @__PURE__ */ React__default__default.createElement("title", null, title), props.children);
436
+ }), title && /* @__PURE__ */ React__default.createElement("title", null, title), props.children);
437
437
  };
438
- return IconContext !== void 0 ? /* @__PURE__ */ React__default__default.createElement(IconContext.Consumer, null, (conf) => elem(conf)) : elem(DefaultContext);
438
+ return IconContext !== void 0 ? /* @__PURE__ */ React__default.createElement(IconContext.Consumer, null, (conf) => elem(conf)) : elem(DefaultContext);
439
439
  }
440
440
  function MdError(props) {
441
441
  return GenIcon({ "attr": { "viewBox": "0 0 24 24" }, "child": [{ "tag": "path", "attr": { "fill": "none", "d": "M0 0h24v24H0z" }, "child": [] }, { "tag": "path", "attr": { "d": "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m1 15h-2v-2h2zm0-4h-2V7h2z" }, "child": [] }] })(props);
@@ -20232,10 +20232,10 @@ var astToFiniteField$1$1 = class astToFiniteField$12 {
20232
20232
  convert(t4, n2, r2 = PRIME$1) {
20233
20233
  return finite_field_evaluate_ast$1(t4, n2, r2);
20234
20234
  }
20235
- }, evaluation_exports$1 = __exportAll$1({ evaluate: () => evaluate$1, evaluate_to_constant: () => evaluate_to_constant$1, f: () => f$2, finite_field_evaluate: () => finite_field_evaluate$1 }), astToMathjs$1$1 = new astToMathjs$2$1({ mathjs: mathjs_default$1 }), astToFiniteField$2 = new astToFiniteField$1$1(), f$2 = function(t4) {
20235
+ }, evaluation_exports$1 = __exportAll$1({ evaluate: () => evaluate$2, evaluate_to_constant: () => evaluate_to_constant$1, f: () => f$2, finite_field_evaluate: () => finite_field_evaluate$1 }), astToMathjs$1$1 = new astToMathjs$2$1({ mathjs: mathjs_default$1 }), astToFiniteField$2 = new astToFiniteField$1$1(), f$2 = function(t4) {
20236
20236
  var n2 = get_tree$1(t4), r2 = factorial_to_gamma_function$1(astToMathjs$1$1.convert(log_subscript_to_two_arg_log$1(normalize_function_names$1(normalize_applied_functions$1(n2)))));
20237
20237
  return r2.evaluate.bind(r2);
20238
- }, evaluate$1 = function(t4, n2) {
20238
+ }, evaluate$2 = function(t4, n2) {
20239
20239
  return f$2(t4)(n2);
20240
20240
  }, finite_field_evaluate$1 = function(t4, n2, r2) {
20241
20241
  return astToFiniteField$2.convert(t4.tree, n2, r2);
@@ -25618,7 +25618,7 @@ function requireCssesc$1() {
25618
25618
  return cssesc_1$1;
25619
25619
  }
25620
25620
  requireCssesc$1();
25621
- const viewerIframeJsSource = '(function() {\n "use strict";\n async function waitForStandaloneBundle(timeoutMs) {\n if (typeof window.renderDoenetViewerToContainer === "function") {\n return true;\n }\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n if (typeof window.renderDoenetViewerToContainer === "function") {\n return true;\n }\n }\n return false;\n }\n ComlinkViewer.expose(\n { renderViewerWithFunctionProps },\n ComlinkViewer.windowEndpoint(globalThis.parent)\n );\n function renderViewerWithFunctionProps(...args) {\n const augmentedDoenetViewerProps = { ...doenetViewerProps };\n augmentedDoenetViewerProps.externalVirtualKeyboardProvided = true;\n for (const propName of doenetViewerPropsSpecified) {\n if (!(propName in doenetViewerProps)) {\n const idx = args.indexOf(propName);\n if (idx !== -1) {\n augmentedDoenetViewerProps[propName] = args[idx + 1];\n }\n }\n }\n if (!doenetViewerPropsSpecified.includes("requestScrollTo")) {\n augmentedDoenetViewerProps.requestScrollTo = (offset) => {\n messageParentFromViewer({ type: "scrollTo", offset });\n };\n }\n window.renderDoenetViewerToContainer(\n document.getElementById("root"),\n void 0,\n augmentedDoenetViewerProps\n );\n }\n function installSharedCorePortProvider() {\n let coreCounter = 0;\n window.doenetGlobalConfig.createExternalCoreWorkerPort = () => {\n const coreId = `${viewerId}-core-${++coreCounter}`;\n const channel = new MessageChannel();\n window.parent.postMessage(\n { origin: viewerId, data: { type: "createSharedCore", coreId } },\n window.parent.origin,\n [channel.port2]\n );\n return {\n port: channel.port1,\n destroy: (suspectWedge) => {\n messageParentFromViewer({\n type: "destroySharedCore",\n coreId,\n suspectWedge: Boolean(suspectWedge)\n });\n }\n };\n };\n }\n (async () => {\n if (await waitForStandaloneBundle(6e4)) {\n if (typeof doenetSharedCoreWorker !== "undefined" && doenetSharedCoreWorker) {\n installSharedCorePortProvider();\n }\n messageParentFromViewer({ iframeReady: true });\n } else {\n messageParentFromViewer({\n error: "Invalid DoenetML version or DoenetML package not found"\n });\n }\n })().catch((err) => {\n console.error(\n "iframe DoenetViewer: unexpected failure while signalling iframeReady",\n err\n );\n try {\n messageParentFromViewer({\n error: "iframe viewer failed to initialize"\n });\n } catch {\n }\n });\n function messageParentFromViewer(data) {\n window.parent.postMessage(\n {\n origin: viewerId,\n data\n },\n window.parent.origin\n );\n }\n})();\n';
25621
+ const viewerIframeJsSource = '(function() {\n "use strict";\n let lastAugmentedProps = null;\n let currentFunctionProps = {};\n const functionPropDispatchers = {};\n function getFunctionPropDispatcher(key) {\n let dispatcher = functionPropDispatchers[key];\n if (!dispatcher) {\n dispatcher = (...callArgs) => currentFunctionProps[key]?.(...callArgs);\n functionPropDispatchers[key] = dispatcher;\n }\n return dispatcher;\n }\n function functionPropArgsToMap(args) {\n const map = {};\n for (let i = 0; i < args.length; i += 2) {\n const key = args[i];\n const fn = args[i + 1];\n if (typeof key === "string" && typeof fn === "function") {\n map[key] = fn;\n }\n }\n return map;\n }\n function releaseFunctionProxy(fn) {\n try {\n fn?.[ComlinkViewer.releaseProxy]?.();\n } catch (e) {\n console.warn(\n "iframe DoenetViewer: failed to release stale Comlink proxy",\n e\n );\n }\n }\n function setCurrentFunctionProps(incoming) {\n for (const [key, fn] of Object.entries(currentFunctionProps)) {\n if (incoming[key] !== fn) {\n releaseFunctionProxy(fn);\n }\n }\n currentFunctionProps = incoming;\n }\n function defaultRequestScrollTo(offset) {\n messageParentFromViewer({ type: "scrollTo", offset });\n }\n function syncAugmentedFunctionProps() {\n if (!lastAugmentedProps) {\n return;\n }\n const next = {};\n for (const [key, val] of Object.entries(lastAugmentedProps)) {\n if (typeof val !== "function") {\n next[key] = val;\n }\n }\n for (const key of Object.keys(currentFunctionProps)) {\n next[key] = getFunctionPropDispatcher(key);\n }\n if (!("requestScrollTo" in next)) {\n next.requestScrollTo = defaultRequestScrollTo;\n }\n lastAugmentedProps = next;\n }\n let currentDoenetMLSource = null;\n function resolveDoenetMLSource(root) {\n if (currentDoenetMLSource !== null) {\n return currentDoenetMLSource;\n }\n const scriptTag = root.querySelector(\'script[type="text/doenetml"]\');\n currentDoenetMLSource = scriptTag?.innerHTML ?? "";\n return currentDoenetMLSource;\n }\n async function waitForStandaloneBundle(timeoutMs) {\n if (typeof window.renderDoenetViewerToContainer === "function") {\n return true;\n }\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n if (typeof window.renderDoenetViewerToContainer === "function") {\n return true;\n }\n }\n return false;\n }\n ComlinkViewer.expose(\n {\n renderViewerWithFunctionProps,\n updateViewerProps(updatedSerializableProps) {\n if (!lastAugmentedProps) {\n console.warn(\n "iframe DoenetViewer: updateViewerProps arrived before renderViewerWithFunctionProps completed — likely a bug in the iframe wrapper\'s queue/replay sequencing."\n );\n return;\n }\n const { doenetML, ...rest } = updatedSerializableProps;\n if (typeof doenetML === "string") {\n currentDoenetMLSource = doenetML;\n }\n lastAugmentedProps = {\n ...lastAugmentedProps,\n ...rest\n };\n renderWithLastAugmentedProps();\n },\n updateViewerFunctionProps(...args) {\n if (!lastAugmentedProps) {\n console.warn(\n "iframe DoenetViewer: updateViewerFunctionProps arrived before renderViewerWithFunctionProps completed — likely a bug in the iframe wrapper\'s queue/replay sequencing."\n );\n return;\n }\n const incoming = functionPropArgsToMap(args);\n const prevKeys = Object.keys(currentFunctionProps).sort();\n const nextKeys = Object.keys(incoming).sort();\n const keysChanged = prevKeys.length !== nextKeys.length || prevKeys.some((key, i) => key !== nextKeys[i]);\n setCurrentFunctionProps(incoming);\n if (keysChanged) {\n syncAugmentedFunctionProps();\n renderWithLastAugmentedProps();\n }\n }\n },\n ComlinkViewer.windowEndpoint(globalThis.parent)\n );\n function renderViewerWithFunctionProps(...args) {\n setCurrentFunctionProps(functionPropArgsToMap(args));\n const augmentedDoenetViewerProps = { ...doenetViewerProps };\n augmentedDoenetViewerProps.externalVirtualKeyboardProvided = true;\n for (const propName of doenetViewerPropsSpecified) {\n if (!(propName in doenetViewerProps) && propName in currentFunctionProps) {\n augmentedDoenetViewerProps[propName] = getFunctionPropDispatcher(propName);\n }\n }\n if (!doenetViewerPropsSpecified.includes("requestScrollTo")) {\n augmentedDoenetViewerProps.requestScrollTo = defaultRequestScrollTo;\n }\n lastAugmentedProps = augmentedDoenetViewerProps;\n renderWithLastAugmentedProps();\n }\n function renderWithLastAugmentedProps() {\n const root = document.getElementById("root");\n window.renderDoenetViewerToContainer(\n root,\n resolveDoenetMLSource(root),\n lastAugmentedProps\n );\n }\n function installSharedCorePortProvider() {\n let coreCounter = 0;\n window.doenetGlobalConfig.createExternalCoreWorkerPort = () => {\n const coreId = `${viewerId}-core-${++coreCounter}`;\n const channel = new MessageChannel();\n window.parent.postMessage(\n { origin: viewerId, data: { type: "createSharedCore", coreId } },\n window.parent.origin,\n [channel.port2]\n );\n return {\n port: channel.port1,\n destroy: (suspectWedge) => {\n messageParentFromViewer({\n type: "destroySharedCore",\n coreId,\n suspectWedge: Boolean(suspectWedge)\n });\n }\n };\n };\n }\n (async () => {\n if (await waitForStandaloneBundle(6e4)) {\n if (typeof doenetSharedCoreWorker !== "undefined" && doenetSharedCoreWorker) {\n installSharedCorePortProvider();\n }\n messageParentFromViewer({ iframeReady: true });\n } else {\n messageParentFromViewer({\n error: "Invalid DoenetML version or DoenetML package not found"\n });\n }\n })().catch((err) => {\n console.error(\n "iframe DoenetViewer: unexpected failure while signalling iframeReady",\n err\n );\n try {\n messageParentFromViewer({\n error: "iframe viewer failed to initialize"\n });\n } catch {\n }\n });\n function messageParentFromViewer(data) {\n window.parent.postMessage(\n {\n origin: viewerId,\n data\n },\n window.parent.origin\n );\n }\n})();\n';
25622
25622
  const editorIframeJsSource = `(function() {
25623
25623
  "use strict";
25624
25624
  let editorControlHandle = null;
@@ -25858,6 +25858,9 @@ function createIframeBodyOpenTag(darkMode) {
25858
25858
  }
25859
25859
  function createHtmlForDoenetViewer(id2, doenetML, doenetViewerProps, standaloneUrl, cssUrl, useSharedCoreWorker = false) {
25860
25860
  const doenetViewerPropsSpecified = Object.keys(doenetViewerProps);
25861
+ if (!doenetViewerPropsSpecified.includes("initializedCallback")) {
25862
+ doenetViewerPropsSpecified.push("initializedCallback");
25863
+ }
25861
25864
  return `
25862
25865
  <html style="overflow:hidden">
25863
25866
  ${createIframeHead(standaloneUrl, cssUrl)}
@@ -26026,6 +26029,193 @@ function destroySharedCoresForViewer(viewerId) {
26026
26029
  }
26027
26030
  }
26028
26031
  }
26032
+ const DEFAULT_MAX_LIVE_VIEWERS = 3;
26033
+ const DEFAULT_VISIBLE_MARGIN = "1000px";
26034
+ const DEFAULT_FLUSH_TIMEOUT_MS = 5e3;
26035
+ const DEFAULT_PARK_DELAY_MS = 2e3;
26036
+ const DEFAULT_MAX_CONCURRENT_BOOTS = 2;
26037
+ const BOOT_SLOT_WATCHDOG_MS = 9e4;
26038
+ const records = /* @__PURE__ */ new Map();
26039
+ let visibleOrderCounter = 0;
26040
+ let effectiveMaxLive = null;
26041
+ let warnedMixedBudgets = false;
26042
+ let evaluateTimerId = null;
26043
+ let evaluateTimerAt = Infinity;
26044
+ let effectiveMaxBoots = null;
26045
+ const bootSlotHolders = /* @__PURE__ */ new Set();
26046
+ let bootRequestCounter = 0;
26047
+ const bootQueue = [];
26048
+ function registerWindowedViewer({
26049
+ id: id2,
26050
+ maxLiveViewers,
26051
+ maxConcurrentBoots,
26052
+ parkDelayMs,
26053
+ canPark,
26054
+ requestPark
26055
+ }) {
26056
+ effectiveMaxBoots = effectiveMaxBoots === null ? maxConcurrentBoots : Math.min(effectiveMaxBoots, maxConcurrentBoots);
26057
+ if (effectiveMaxLive !== null && effectiveMaxLive !== maxLiveViewers && !warnedMixedBudgets) {
26058
+ warnedMixedBudgets = true;
26059
+ console.warn(
26060
+ `DoenetViewer mountPolicy: viewers on this page specify different maxLiveViewers (${effectiveMaxLive} and ${maxLiveViewers}); using the smaller value.`
26061
+ );
26062
+ }
26063
+ effectiveMaxLive = effectiveMaxLive === null ? maxLiveViewers : Math.min(effectiveMaxLive, maxLiveViewers);
26064
+ records.set(id2, {
26065
+ id: id2,
26066
+ state: "live",
26067
+ visible: false,
26068
+ lastVisibleOrder: 0,
26069
+ invisibleSince: Date.now(),
26070
+ parkDelayMs,
26071
+ canPark,
26072
+ requestPark
26073
+ });
26074
+ scheduleEvaluate(parkDelayMs);
26075
+ return () => {
26076
+ records.delete(id2);
26077
+ cancelBootRequest(id2);
26078
+ releaseBootSlot(id2);
26079
+ scheduleEvaluate(0);
26080
+ };
26081
+ }
26082
+ function requestBootSlot(id2, grant) {
26083
+ if (bootSlotHolders.has(id2) || bootQueue.some((r2) => r2.id === id2)) {
26084
+ return;
26085
+ }
26086
+ bootQueue.push({ id: id2, requestOrder: ++bootRequestCounter, grant });
26087
+ pumpBootQueue();
26088
+ }
26089
+ function cancelBootRequest(id2) {
26090
+ const idx = bootQueue.findIndex((r2) => r2.id === id2);
26091
+ if (idx !== -1) {
26092
+ bootQueue.splice(idx, 1);
26093
+ }
26094
+ }
26095
+ function releaseBootSlot(id2) {
26096
+ if (bootSlotHolders.delete(id2)) {
26097
+ pumpBootQueue();
26098
+ }
26099
+ }
26100
+ function pumpBootQueue() {
26101
+ const max2 = effectiveMaxBoots ?? Infinity;
26102
+ while (bootQueue.length > 0 && bootSlotHolders.size < max2) {
26103
+ let best = 0;
26104
+ for (let i2 = 1; i2 < bootQueue.length; i2++) {
26105
+ const bestVisible = Boolean(
26106
+ records.get(bootQueue[best].id)?.visible
26107
+ );
26108
+ const thisVisible = Boolean(records.get(bootQueue[i2].id)?.visible);
26109
+ if (thisVisible && !bestVisible || thisVisible === bestVisible && bootQueue[i2].requestOrder < bootQueue[best].requestOrder) {
26110
+ best = i2;
26111
+ }
26112
+ }
26113
+ const [request] = bootQueue.splice(best, 1);
26114
+ bootSlotHolders.add(request.id);
26115
+ request.grant();
26116
+ }
26117
+ }
26118
+ function setViewerVisibility(id2, visible) {
26119
+ const record = records.get(id2);
26120
+ if (!record || record.visible === visible) {
26121
+ return;
26122
+ }
26123
+ record.visible = visible;
26124
+ if (visible) {
26125
+ record.lastVisibleOrder = ++visibleOrderCounter;
26126
+ scheduleEvaluate(0);
26127
+ } else {
26128
+ record.invisibleSince = Date.now();
26129
+ scheduleEvaluate(record.parkDelayMs);
26130
+ }
26131
+ }
26132
+ function notifyViewerState(id2, state) {
26133
+ const record = records.get(id2);
26134
+ if (!record || record.state === state) {
26135
+ return;
26136
+ }
26137
+ record.state = state;
26138
+ scheduleEvaluate(0);
26139
+ }
26140
+ function getViewerLifecycleStats() {
26141
+ let live = 0, parking = 0, parked = 0;
26142
+ for (const record of records.values()) {
26143
+ if (record.state === "live") {
26144
+ live++;
26145
+ } else if (record.state === "parking") {
26146
+ parking++;
26147
+ } else {
26148
+ parked++;
26149
+ }
26150
+ }
26151
+ return {
26152
+ registered: records.size,
26153
+ live,
26154
+ parking,
26155
+ parked,
26156
+ booting: bootSlotHolders.size,
26157
+ bootQueue: bootQueue.length
26158
+ };
26159
+ }
26160
+ function scheduleEvaluate(delayMs) {
26161
+ const at = Date.now() + delayMs;
26162
+ if (evaluateTimerId !== null) {
26163
+ if (at >= evaluateTimerAt) {
26164
+ return;
26165
+ }
26166
+ clearTimeout(evaluateTimerId);
26167
+ }
26168
+ evaluateTimerAt = at;
26169
+ evaluateTimerId = setTimeout(() => {
26170
+ evaluateTimerId = null;
26171
+ evaluateTimerAt = Infinity;
26172
+ evaluate$1();
26173
+ }, delayMs);
26174
+ }
26175
+ function evaluate$1() {
26176
+ if (effectiveMaxLive === null) {
26177
+ return;
26178
+ }
26179
+ const now = Date.now();
26180
+ let live = 0;
26181
+ for (const record of records.values()) {
26182
+ if (record.state === "live") {
26183
+ live++;
26184
+ }
26185
+ }
26186
+ let excess = live - effectiveMaxLive;
26187
+ if (excess <= 0) {
26188
+ return;
26189
+ }
26190
+ const candidates = [];
26191
+ let nextDebounceExpiry = Infinity;
26192
+ for (const record of records.values()) {
26193
+ if (record.state !== "live" || record.visible) {
26194
+ continue;
26195
+ }
26196
+ const eligibleAt = record.invisibleSince + record.parkDelayMs;
26197
+ if (eligibleAt > now) {
26198
+ nextDebounceExpiry = Math.min(nextDebounceExpiry, eligibleAt);
26199
+ continue;
26200
+ }
26201
+ if (!record.canPark()) {
26202
+ continue;
26203
+ }
26204
+ candidates.push(record);
26205
+ }
26206
+ candidates.sort((a2, b2) => a2.lastVisibleOrder - b2.lastVisibleOrder);
26207
+ for (const record of candidates) {
26208
+ if (excess <= 0) {
26209
+ break;
26210
+ }
26211
+ record.state = "parking";
26212
+ excess--;
26213
+ record.requestPark();
26214
+ }
26215
+ if (excess > 0 && nextDebounceExpiry < Infinity) {
26216
+ scheduleEvaluate(Math.max(0, nextDebounceExpiry - now));
26217
+ }
26218
+ }
26029
26219
  function findNodesWithPositionInfo(nodes) {
26030
26220
  if (typeof nodes !== "object" || nodes === null) {
26031
26221
  return [];
@@ -34639,7 +34829,7 @@ for (const key of [
34639
34829
  ]) {
34640
34830
  key.isMath = true;
34641
34831
  }
34642
- const BackspaceIcon = /* @__PURE__ */ React__default__default.createElement(
34832
+ const BackspaceIcon = /* @__PURE__ */ React__default.createElement(
34643
34833
  "svg",
34644
34834
  {
34645
34835
  "aria-hidden": "true",
@@ -34651,7 +34841,7 @@ const BackspaceIcon = /* @__PURE__ */ React__default__default.createElement(
34651
34841
  xmlns: "http://www.w3.org/2000/svg",
34652
34842
  viewBox: "0 0 576 512"
34653
34843
  },
34654
- /* @__PURE__ */ React__default__default.createElement(
34844
+ /* @__PURE__ */ React__default.createElement(
34655
34845
  "path",
34656
34846
  {
34657
34847
  fill: "currentColor",
@@ -34665,19 +34855,19 @@ function Keyboard({
34665
34855
  }) {
34666
34856
  switch (style) {
34667
34857
  case "alpha_lower":
34668
- return /* @__PURE__ */ React__default__default.createElement("div", { className: "virtual-keyboard alpha" }, /* @__PURE__ */ React__default__default.createElement(Qwerty, { type: "lower", onClick }));
34858
+ return /* @__PURE__ */ React__default.createElement("div", { className: "virtual-keyboard alpha" }, /* @__PURE__ */ React__default.createElement(Qwerty, { type: "lower", onClick }));
34669
34859
  case "alpha_upper":
34670
- return /* @__PURE__ */ React__default__default.createElement("div", { className: "virtual-keyboard alpha" }, /* @__PURE__ */ React__default__default.createElement(Qwerty, { type: "upper", onClick }));
34860
+ return /* @__PURE__ */ React__default.createElement("div", { className: "virtual-keyboard alpha" }, /* @__PURE__ */ React__default.createElement(Qwerty, { type: "upper", onClick }));
34671
34861
  case "numeric":
34672
- return /* @__PURE__ */ React__default__default.createElement("div", { className: "virtual-keyboard numeric" }, /* @__PURE__ */ React__default__default.createElement(Numeric, { onClick }));
34862
+ return /* @__PURE__ */ React__default.createElement("div", { className: "virtual-keyboard numeric" }, /* @__PURE__ */ React__default.createElement(Numeric, { onClick }));
34673
34863
  case "greek_lower":
34674
- return /* @__PURE__ */ React__default__default.createElement("div", { className: "virtual-keyboard greek" }, /* @__PURE__ */ React__default__default.createElement(Greek, { type: "lower", onClick }));
34864
+ return /* @__PURE__ */ React__default.createElement("div", { className: "virtual-keyboard greek" }, /* @__PURE__ */ React__default.createElement(Greek, { type: "lower", onClick }));
34675
34865
  case "greek_upper":
34676
- return /* @__PURE__ */ React__default__default.createElement("div", { className: "virtual-keyboard greek" }, /* @__PURE__ */ React__default__default.createElement(Greek, { type: "upper", onClick }));
34866
+ return /* @__PURE__ */ React__default.createElement("div", { className: "virtual-keyboard greek" }, /* @__PURE__ */ React__default.createElement(Greek, { type: "upper", onClick }));
34677
34867
  case "symbol":
34678
- return /* @__PURE__ */ React__default__default.createElement("div", { className: "virtual-keyboard symbol" }, /* @__PURE__ */ React__default__default.createElement(Symbol$1, { onClick }));
34868
+ return /* @__PURE__ */ React__default.createElement("div", { className: "virtual-keyboard symbol" }, /* @__PURE__ */ React__default.createElement(Symbol$1, { onClick }));
34679
34869
  case "function":
34680
- return /* @__PURE__ */ React__default__default.createElement("div", { className: "virtual-keyboard composite" }, /* @__PURE__ */ React__default__default.createElement(Composite, { onClick }));
34870
+ return /* @__PURE__ */ React__default.createElement("div", { className: "virtual-keyboard composite" }, /* @__PURE__ */ React__default.createElement(Composite, { onClick }));
34681
34871
  }
34682
34872
  }
34683
34873
  function Key({
@@ -34686,12 +34876,12 @@ function Key({
34686
34876
  }) {
34687
34877
  let content = keyInfo.displayName;
34688
34878
  if (keyInfo.isMath) {
34689
- content = /* @__PURE__ */ React__default__default.createElement("div", { style: { pointerEvents: "none" } }, /* @__PURE__ */ React__default__default.createElement(MathJax, null, "\\(" + content + "\\)"));
34879
+ content = /* @__PURE__ */ React__default.createElement("div", { style: { pointerEvents: "none" } }, /* @__PURE__ */ React__default.createElement(MathJax, null, "\\(" + content + "\\)"));
34690
34880
  }
34691
34881
  if (keyInfo.name === "backspace") {
34692
34882
  content = BackspaceIcon;
34693
34883
  }
34694
- return /* @__PURE__ */ React__default__default.createElement(
34884
+ return /* @__PURE__ */ React__default.createElement(
34695
34885
  "button",
34696
34886
  {
34697
34887
  className: `key key-${keyInfo.name} ${keyInfo.isMath ? "math" : ""}`,
@@ -34723,7 +34913,7 @@ function Qwerty({
34723
34913
  SPECIAL_KEYS.right,
34724
34914
  SPECIAL_KEYS.enter
34725
34915
  ];
34726
- return /* @__PURE__ */ React__default__default.createElement(React__default__default.Fragment, null, [row1, row2, row3, row4].map((row, i2) => /* @__PURE__ */ React__default__default.createElement("div", { key: i2, className: "row" }, row.map((key) => /* @__PURE__ */ React__default__default.createElement(Key, { keyInfo: key, onClick, key: key.name })))));
34916
+ return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, [row1, row2, row3, row4].map((row, i2) => /* @__PURE__ */ React__default.createElement("div", { key: i2, className: "row" }, row.map((key) => /* @__PURE__ */ React__default.createElement(Key, { keyInfo: key, onClick, key: key.name })))));
34727
34917
  }
34728
34918
  function Numeric({ onClick }) {
34729
34919
  const row1 = [
@@ -34747,7 +34937,7 @@ function Numeric({ onClick }) {
34747
34937
  SPECIAL_KEYS.right,
34748
34938
  SPECIAL_KEYS.enter
34749
34939
  ];
34750
- return /* @__PURE__ */ React__default__default.createElement(React__default__default.Fragment, null, /* @__PURE__ */ React__default__default.createElement("div", { className: "keyboard-region" }, Object.values(VAR_KEYS).map((key) => /* @__PURE__ */ React__default__default.createElement(Key, { keyInfo: key, onClick, key: key.name }))), /* @__PURE__ */ React__default__default.createElement("div", { className: "sub-keyboard" }, [row1, row2, row3, row4].map((row, i2) => /* @__PURE__ */ React__default__default.createElement("div", { key: i2, className: "row" }, row.map((key) => /* @__PURE__ */ React__default__default.createElement(
34940
+ return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement("div", { className: "keyboard-region" }, Object.values(VAR_KEYS).map((key) => /* @__PURE__ */ React__default.createElement(Key, { keyInfo: key, onClick, key: key.name }))), /* @__PURE__ */ React__default.createElement("div", { className: "sub-keyboard" }, [row1, row2, row3, row4].map((row, i2) => /* @__PURE__ */ React__default.createElement("div", { key: i2, className: "row" }, row.map((key) => /* @__PURE__ */ React__default.createElement(
34751
34941
  Key,
34752
34942
  {
34753
34943
  keyInfo: key,
@@ -34757,12 +34947,12 @@ function Numeric({ onClick }) {
34757
34947
  ))))));
34758
34948
  }
34759
34949
  function Symbol$1({ onClick }) {
34760
- return /* @__PURE__ */ React__default__default.createElement(React__default__default.Fragment, null, /* @__PURE__ */ React__default__default.createElement("div", { className: "keyboard-region" }, Object.values(SYMBOL_KEYS).map((key) => /* @__PURE__ */ React__default__default.createElement(Key, { keyInfo: key, onClick, key: key.name }))), /* @__PURE__ */ React__default__default.createElement("div", { className: "keyboard-region" }, Object.values(OTHER_SYMBOLS).map((key) => /* @__PURE__ */ React__default__default.createElement(Key, { keyInfo: key, onClick, key: key.name })), [
34950
+ return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement("div", { className: "keyboard-region" }, Object.values(SYMBOL_KEYS).map((key) => /* @__PURE__ */ React__default.createElement(Key, { keyInfo: key, onClick, key: key.name }))), /* @__PURE__ */ React__default.createElement("div", { className: "keyboard-region" }, Object.values(OTHER_SYMBOLS).map((key) => /* @__PURE__ */ React__default.createElement(Key, { keyInfo: key, onClick, key: key.name })), [
34761
34951
  SPECIAL_KEYS.left,
34762
34952
  SPECIAL_KEYS.right,
34763
34953
  SPECIAL_KEYS.backspace,
34764
34954
  SPECIAL_KEYS.enter
34765
- ].map((key) => /* @__PURE__ */ React__default__default.createElement(Key, { keyInfo: key, onClick, key: key.name }))));
34955
+ ].map((key) => /* @__PURE__ */ React__default.createElement(Key, { keyInfo: key, onClick, key: key.name }))));
34766
34956
  }
34767
34957
  function Greek({
34768
34958
  type,
@@ -34783,15 +34973,15 @@ function Greek({
34783
34973
  SPECIAL_KEYS.right,
34784
34974
  SPECIAL_KEYS.enter
34785
34975
  ];
34786
- return /* @__PURE__ */ React__default__default.createElement(React__default__default.Fragment, null, [row1, row2, row3, row4].map((row, i2) => /* @__PURE__ */ React__default__default.createElement("div", { key: i2, className: "row" }, row.map((key) => /* @__PURE__ */ React__default__default.createElement(Key, { keyInfo: key, onClick, key: key.name })))));
34976
+ return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, [row1, row2, row3, row4].map((row, i2) => /* @__PURE__ */ React__default.createElement("div", { key: i2, className: "row" }, row.map((key) => /* @__PURE__ */ React__default.createElement(Key, { keyInfo: key, onClick, key: key.name })))));
34787
34977
  }
34788
34978
  function Composite({ onClick }) {
34789
- return /* @__PURE__ */ React__default__default.createElement(React__default__default.Fragment, null, /* @__PURE__ */ React__default__default.createElement("div", { className: "keyboard-region" }, Object.values(TRIG_KEYS).map((key) => /* @__PURE__ */ React__default__default.createElement(Key, { keyInfo: key, onClick, key: key.name }))), /* @__PURE__ */ React__default__default.createElement("div", { className: "keyboard-region" }, Object.values(FUNCTION_KEYS).map((key) => /* @__PURE__ */ React__default__default.createElement(Key, { keyInfo: key, onClick, key: key.name })), [
34979
+ return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement("div", { className: "keyboard-region" }, Object.values(TRIG_KEYS).map((key) => /* @__PURE__ */ React__default.createElement(Key, { keyInfo: key, onClick, key: key.name }))), /* @__PURE__ */ React__default.createElement("div", { className: "keyboard-region" }, Object.values(FUNCTION_KEYS).map((key) => /* @__PURE__ */ React__default.createElement(Key, { keyInfo: key, onClick, key: key.name })), [
34790
34980
  SPECIAL_KEYS.backspace,
34791
34981
  SPECIAL_KEYS.left,
34792
34982
  SPECIAL_KEYS.right,
34793
34983
  SPECIAL_KEYS.enter
34794
- ].map((key) => /* @__PURE__ */ React__default__default.createElement(Key, { keyInfo: key, onClick, key: key.name }))));
34984
+ ].map((key) => /* @__PURE__ */ React__default.createElement(Key, { keyInfo: key, onClick, key: key.name }))));
34795
34985
  }
34796
34986
  const FocusableContext = createContext(true);
34797
34987
  function flatten2DArray(array) {
@@ -35235,7 +35425,7 @@ function mergeProps(base, overrides2) {
35235
35425
  }
35236
35426
  return props;
35237
35427
  }
35238
- const _React = { ...React__default };
35428
+ const _React = { ...React };
35239
35429
  const useReactId = _React.useId;
35240
35430
  const useReactInsertionEffect = _React.useInsertionEffect;
35241
35431
  const useSafeLayoutEffect = canUseDOM ? useLayoutEffect : useEffect;
@@ -35352,7 +35542,7 @@ function useMetadataProps(props, key, value) {
35352
35542
  return [parent?.[key], { onLoadedMetadataCapture }];
35353
35543
  }
35354
35544
  function forwardRef(render) {
35355
- const Role = React__default.forwardRef((props, ref) => render({
35545
+ const Role = React.forwardRef((props, ref) => render({
35356
35546
  ...props,
35357
35547
  ref
35358
35548
  }));
@@ -35360,18 +35550,18 @@ function forwardRef(render) {
35360
35550
  return Role;
35361
35551
  }
35362
35552
  function memo(Component, propsAreEqual) {
35363
- return React__default.memo(Component, propsAreEqual);
35553
+ return React.memo(Component, propsAreEqual);
35364
35554
  }
35365
35555
  function createElement(Type, props) {
35366
35556
  const { wrapElement, render, ...rest2 } = props;
35367
35557
  const mergedRef = useMergeRefs(props.ref, getRefProperty(render));
35368
35558
  let element;
35369
- if (React__default.isValidElement(render)) {
35559
+ if (React.isValidElement(render)) {
35370
35560
  const renderProps = {
35371
35561
  ...render.props,
35372
35562
  ref: mergedRef
35373
35563
  };
35374
- element = React__default.cloneElement(render, mergeProps(rest2, renderProps));
35564
+ element = React.cloneElement(render, mergeProps(rest2, renderProps));
35375
35565
  } else if (render) element = render(rest2);
35376
35566
  else element = /* @__PURE__ */ jsxRuntimeExports.jsx(Type, { ...rest2 });
35377
35567
  if (wrapElement) return wrapElement(element);
@@ -35385,17 +35575,17 @@ function createHook(useProps) {
35385
35575
  return useRole;
35386
35576
  }
35387
35577
  function createStoreContext(providers = [], scopedProviders = []) {
35388
- const context = React__default.createContext(void 0);
35389
- const scopedContext = React__default.createContext(void 0);
35390
- const useContext2 = () => React__default.useContext(context);
35578
+ const context = React.createContext(void 0);
35579
+ const scopedContext = React.createContext(void 0);
35580
+ const useContext2 = () => React.useContext(context);
35391
35581
  const useScopedContext = (onlyScoped = false) => {
35392
- const scoped = React__default.useContext(scopedContext);
35582
+ const scoped = React.useContext(scopedContext);
35393
35583
  const store = useContext2();
35394
35584
  if (onlyScoped) return scoped;
35395
35585
  return scoped || store;
35396
35586
  };
35397
35587
  const useProviderContext = () => {
35398
- const scoped = React__default.useContext(scopedContext);
35588
+ const scoped = React.useContext(scopedContext);
35399
35589
  const store = useContext2();
35400
35590
  if (scoped && scoped === store) return;
35401
35591
  return store;
@@ -36649,7 +36839,7 @@ var hasRequiredUseSyncExternalStoreShim_production;
36649
36839
  function requireUseSyncExternalStoreShim_production() {
36650
36840
  if (hasRequiredUseSyncExternalStoreShim_production) return useSyncExternalStoreShim_production;
36651
36841
  hasRequiredUseSyncExternalStoreShim_production = 1;
36652
- var React2 = React__default__default;
36842
+ var React2 = React__default;
36653
36843
  function is(x2, y2) {
36654
36844
  return x2 === y2 && (0 !== x2 || 1 / x2 === 1 / y2) || x2 !== x2 && y2 !== y2;
36655
36845
  }
@@ -36706,7 +36896,7 @@ var shimExports = requireShim();
36706
36896
  const noopSubscribe = () => () => {
36707
36897
  };
36708
36898
  function useStoreState(store, keyOrSelector = identity$1) {
36709
- const storeSubscribe = React__default.useCallback((callback) => {
36899
+ const storeSubscribe = React.useCallback((callback) => {
36710
36900
  if (!store) return noopSubscribe();
36711
36901
  return subscribe(store, null, callback);
36712
36902
  }, [store]);
@@ -36723,8 +36913,8 @@ function useStoreState(store, keyOrSelector = identity$1) {
36723
36913
  return shimExports.useSyncExternalStore(storeSubscribe, getSnapshot, getSnapshot);
36724
36914
  }
36725
36915
  function useStoreStateObject(store, object2) {
36726
- const objRef = React__default.useRef({});
36727
- const storeSubscribe = React__default.useCallback((callback) => {
36916
+ const objRef = React.useRef({});
36917
+ const storeSubscribe = React.useCallback((callback) => {
36728
36918
  if (!store) return noopSubscribe();
36729
36919
  return subscribe(store, null, callback);
36730
36920
  }, [store]);
@@ -36781,10 +36971,10 @@ function useStoreProps(store, props, key, setKey) {
36781
36971
  });
36782
36972
  }
36783
36973
  function useStore(createStore2, props) {
36784
- const [store, setStore] = React__default.useState(() => createStore2(props));
36974
+ const [store, setStore] = React.useState(() => createStore2(props));
36785
36975
  useSafeLayoutEffect(() => init(store), [store]);
36786
- const useState2 = React__default.useCallback((keyOrSelector) => useStoreState(store, keyOrSelector), [store]);
36787
- return [React__default.useMemo(() => ({
36976
+ const useState2 = React.useCallback((keyOrSelector) => useStoreState(store, keyOrSelector), [store]);
36977
+ return [React.useMemo(() => ({
36788
36978
  ...store,
36789
36979
  useState: useState2
36790
36980
  }), [store, useState2]), useEvent(() => {
@@ -37626,10 +37816,10 @@ function TabProvider(props = {}) {
37626
37816
  });
37627
37817
  }
37628
37818
  function ManagedKeyboard({ onClick }) {
37629
- const [style, setStyle] = React__default__default.useState("numeric");
37819
+ const [style, setStyle] = React__default.useState("numeric");
37630
37820
  const store = useTabStore();
37631
37821
  const storeState = store.useState();
37632
- React__default__default.useEffect(() => {
37822
+ React__default.useEffect(() => {
37633
37823
  if (!storeState.activeId) {
37634
37824
  return;
37635
37825
  }
@@ -37643,7 +37833,7 @@ function ManagedKeyboard({ onClick }) {
37643
37833
  setStyle(storeState.activeId);
37644
37834
  }
37645
37835
  }, [storeState, style]);
37646
- const wrappedOnClick = React__default__default.useCallback(
37836
+ const wrappedOnClick = React__default.useCallback(
37647
37837
  (e2) => {
37648
37838
  const firstPress = e2[0];
37649
37839
  if (!firstPress) {
@@ -37670,7 +37860,7 @@ function ManagedKeyboard({ onClick }) {
37670
37860
  },
37671
37861
  [style]
37672
37862
  );
37673
- return /* @__PURE__ */ React__default__default.createElement(TabProvider, null, /* @__PURE__ */ React__default__default.createElement(TabList, { className: "virtual-keyboard-tab-list" }, /* @__PURE__ */ React__default__default.createElement(Tab, { store, id: "numeric" }, "123"), /* @__PURE__ */ React__default__default.createElement(Tab, { store, id: "function" }, "f(x)"), /* @__PURE__ */ React__default__default.createElement(Tab, { store, id: "alpha_lower" }, "ABC"), /* @__PURE__ */ React__default__default.createElement(Tab, { store, id: "greek_lower" }, "αβγ"), /* @__PURE__ */ React__default__default.createElement(Tab, { store, id: "symbol" }, "$%∞")), /* @__PURE__ */ React__default__default.createElement(Keyboard, { style, onClick: wrappedOnClick }));
37863
+ return /* @__PURE__ */ React__default.createElement(TabProvider, null, /* @__PURE__ */ React__default.createElement(TabList, { className: "virtual-keyboard-tab-list" }, /* @__PURE__ */ React__default.createElement(Tab, { store, id: "numeric" }, "123"), /* @__PURE__ */ React__default.createElement(Tab, { store, id: "function" }, "f(x)"), /* @__PURE__ */ React__default.createElement(Tab, { store, id: "alpha_lower" }, "ABC"), /* @__PURE__ */ React__default.createElement(Tab, { store, id: "greek_lower" }, "αβγ"), /* @__PURE__ */ React__default.createElement(Tab, { store, id: "symbol" }, "$%∞")), /* @__PURE__ */ React__default.createElement(Keyboard, { style, onClick: wrappedOnClick }));
37674
37864
  }
37675
37865
  var classnames = { exports: {} };
37676
37866
  var hasRequiredClassnames;
@@ -37732,7 +37922,7 @@ function requireClassnames() {
37732
37922
  }
37733
37923
  var classnamesExports = requireClassnames();
37734
37924
  const classNames = /* @__PURE__ */ getDefaultExportFromCjs$1(classnamesExports);
37735
- const KeyboardIcon = () => /* @__PURE__ */ React__default__default.createElement(
37925
+ const KeyboardIcon = () => /* @__PURE__ */ React__default.createElement(
37736
37926
  "svg",
37737
37927
  {
37738
37928
  stroke: "currentColor",
@@ -37745,15 +37935,15 @@ const KeyboardIcon = () => /* @__PURE__ */ React__default__default.createElement
37745
37935
  width: "1em",
37746
37936
  xmlns: "http://www.w3.org/2000/svg"
37747
37937
  },
37748
- /* @__PURE__ */ React__default__default.createElement("path", { d: "M528 448H48c-26.51 0-48-21.49-48-48V112c0-26.51 21.49-48 48-48h480c26.51 0 48 21.49 48 48v288c0 26.51-21.49 48-48 48zM128 180v-40c0-6.627-5.373-12-12-12H76c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm-336 96v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm-336 96v-40c0-6.627-5.373-12-12-12H76c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm288 0v-40c0-6.627-5.373-12-12-12H172c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h232c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12z" })
37938
+ /* @__PURE__ */ React__default.createElement("path", { d: "M528 448H48c-26.51 0-48-21.49-48-48V112c0-26.51 21.49-48 48-48h480c26.51 0 48 21.49 48 48v288c0 26.51-21.49 48-48 48zM128 180v-40c0-6.627-5.373-12-12-12H76c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm-336 96v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm-336 96v-40c0-6.627-5.373-12-12-12H76c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm288 0v-40c0-6.627-5.373-12-12-12H172c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h232c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12z" })
37749
37939
  );
37750
37940
  function KeyboardTray({
37751
37941
  onClick,
37752
37942
  theme
37753
37943
  }) {
37754
- const [open, setOpen] = React__default__default.useState(false);
37944
+ const [open, setOpen] = React__default.useState(false);
37755
37945
  return createPortal(
37756
- /* @__PURE__ */ React__default__default.createElement(
37946
+ /* @__PURE__ */ React__default.createElement(
37757
37947
  "div",
37758
37948
  {
37759
37949
  id: "virtual-keyboard-tray",
@@ -37770,7 +37960,7 @@ function KeyboardTray({
37770
37960
  ]);
37771
37961
  }
37772
37962
  },
37773
- /* @__PURE__ */ React__default__default.createElement(
37963
+ /* @__PURE__ */ React__default.createElement(
37774
37964
  "button",
37775
37965
  {
37776
37966
  className: "open-keyboard-button",
@@ -37778,9 +37968,9 @@ function KeyboardTray({
37778
37968
  title: open ? "Close Keyboard" : "Open Keyboard",
37779
37969
  "aria-label": open ? "Close Keyboard" : "Open Keyboard"
37780
37970
  },
37781
- /* @__PURE__ */ React__default__default.createElement(KeyboardIcon, null)
37971
+ /* @__PURE__ */ React__default.createElement(KeyboardIcon, null)
37782
37972
  ),
37783
- /* @__PURE__ */ React__default__default.createElement("div", { className: "keyboard-container" }, /* @__PURE__ */ React__default__default.createElement(
37973
+ /* @__PURE__ */ React__default.createElement("div", { className: "keyboard-container" }, /* @__PURE__ */ React__default.createElement(
37784
37974
  "button",
37785
37975
  {
37786
37976
  className: "close-keyboard-button",
@@ -37789,7 +37979,7 @@ function KeyboardTray({
37789
37979
  "aria-label": "Close Keyboard"
37790
37980
  },
37791
37981
  "×"
37792
- ), /* @__PURE__ */ React__default__default.createElement(ManagedKeyboard, { onClick }))
37982
+ ), /* @__PURE__ */ React__default.createElement(ManagedKeyboard, { onClick }))
37793
37983
  ),
37794
37984
  document.body
37795
37985
  );
@@ -37905,7 +38095,7 @@ function MathJaxContext({
37905
38095
  });
37906
38096
  return { version: version2, promise };
37907
38097
  }, [config2, src, useExistingMathJax, timeoutMs, version2]);
37908
- return /* @__PURE__ */ React__default__default.createElement(MathJaxBaseContext.Provider, { value }, children);
38098
+ return /* @__PURE__ */ React__default.createElement(MathJaxBaseContext.Provider, { value }, children);
37909
38099
  }
37910
38100
  var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
37911
38101
  function getDefaultExportFromCjs(x2) {
@@ -63053,7 +63243,7 @@ function rerenderTray() {
63053
63243
  virtualKeyboardState.keyboardReactRoot?.render(renderTray(theme));
63054
63244
  }
63055
63245
  function renderTray(theme) {
63056
- return /* @__PURE__ */ React__default__default.createElement(MathJaxContext, { config: mathjaxConfig, version: 4 }, /* @__PURE__ */ React__default__default.createElement(
63246
+ return /* @__PURE__ */ React__default.createElement(MathJaxContext, { config: mathjaxConfig, version: 4 }, /* @__PURE__ */ React__default.createElement(
63057
63247
  KeyboardTray,
63058
63248
  {
63059
63249
  theme,
@@ -63068,12 +63258,12 @@ function UniqueKeyboardTray({
63068
63258
  theme,
63069
63259
  ownerRef
63070
63260
  }) {
63071
- const [id2] = React__default__default.useState(() => {
63261
+ const [id2] = React__default.useState(() => {
63072
63262
  const next = virtualKeyboardState.nextRegistrationId;
63073
63263
  virtualKeyboardState.nextRegistrationId += 1;
63074
63264
  return next;
63075
63265
  });
63076
- React__default__default.useEffect(() => {
63266
+ React__default.useEffect(() => {
63077
63267
  if (virtualKeyboardState.count === 0) {
63078
63268
  const keyboardDomNode = document.createElement("footer");
63079
63269
  keyboardDomNode.id = "virtual-keyboard-dummy";
@@ -63132,7 +63322,7 @@ function UniqueKeyboardTray({
63132
63322
  }
63133
63323
  };
63134
63324
  }, []);
63135
- React__default__default.useEffect(() => {
63325
+ React__default.useEffect(() => {
63136
63326
  const registration = getRegistrationById(id2);
63137
63327
  if (registration) {
63138
63328
  registration.onClick = onClick;
@@ -63147,7 +63337,7 @@ function ExternalVirtualKeyboard({
63147
63337
  theme,
63148
63338
  ownerRef
63149
63339
  }) {
63150
- return /* @__PURE__ */ React__default__default.createElement(
63340
+ return /* @__PURE__ */ React__default.createElement(
63151
63341
  UniqueKeyboardTray,
63152
63342
  {
63153
63343
  ownerRef,
@@ -63172,7 +63362,7 @@ function ExternalVirtualKeyboard({
63172
63362
  }
63173
63363
  );
63174
63364
  }
63175
- const version = "0.7.20-dev.324";
63365
+ const version = "0.7.20-dev.327";
63176
63366
  const latestDoenetmlVersion = version;
63177
63367
  function subscribeToPinnedTheme() {
63178
63368
  return () => {
@@ -63180,13 +63370,32 @@ function subscribeToPinnedTheme() {
63180
63370
  }
63181
63371
  function useResolvedTheme(setting) {
63182
63372
  const subscribe2 = setting === "system" ? subscribeToSystemTheme : subscribeToPinnedTheme;
63183
- const systemTheme = React__default__default.useSyncExternalStore(
63373
+ const systemTheme = React__default.useSyncExternalStore(
63184
63374
  subscribe2,
63185
63375
  getSystemTheme,
63186
63376
  () => "light"
63187
63377
  );
63188
63378
  return setting === "system" ? systemTheme : setting;
63189
63379
  }
63380
+ const LIVE_UPDATE_MIN_VERSION = [0, 7, 18];
63381
+ function versionSupportsLiveUpdates(version2) {
63382
+ const match2 = version2.replace(/^v/, "").match(/^(\d+)(?:\.(\d+))?(?:\.(\d+))?/);
63383
+ if (!match2) {
63384
+ return true;
63385
+ }
63386
+ const parts = [match2[1], match2[2], match2[3]].map(
63387
+ (p2) => p2 === void 0 ? Infinity : parseInt(p2, 10)
63388
+ );
63389
+ for (let i2 = 0; i2 < 3; i2++) {
63390
+ if (parts[i2] > LIVE_UPDATE_MIN_VERSION[i2]) {
63391
+ return true;
63392
+ }
63393
+ if (parts[i2] < LIVE_UPDATE_MIN_VERSION[i2]) {
63394
+ return false;
63395
+ }
63396
+ }
63397
+ return true;
63398
+ }
63190
63399
  function DoenetViewer({
63191
63400
  doenetML,
63192
63401
  standaloneUrl: specifiedStandaloneUrl,
@@ -63194,16 +63403,87 @@ function DoenetViewer({
63194
63403
  doenetmlVersion: specifiedDoenetmlVersion,
63195
63404
  autodetectVersion = true,
63196
63405
  useSharedCoreWorker = false,
63406
+ mountPolicy,
63197
63407
  ...doenetViewerProps
63198
63408
  }) {
63199
- const [id2, _2] = React__default__default.useState(() => Math.random().toString(36).slice(2));
63200
- const ref = React__default__default.useRef(null);
63201
- const [height, setHeight] = React__default__default.useState("500px");
63202
- const [inErrorState, setInErrorState] = React__default__default.useState(null);
63203
- const [ignoreDetectedVersion, setIgnoreDetectedVersion] = React__default__default.useState(false);
63409
+ const [id2, _2] = React__default.useState(() => Math.random().toString(36).slice(2));
63410
+ const ref = React__default.useRef(null);
63411
+ const viewerIframeRef = React__default.useRef(null);
63412
+ const pendingActions = React__default.useRef([]);
63413
+ const lastSentPropsSnapshotRef = React__default.useRef(null);
63414
+ const lastSentFunctionPropsRef = React__default.useRef(null);
63415
+ const doenetViewerPropsRef = React__default.useRef(doenetViewerProps);
63416
+ doenetViewerPropsRef.current = doenetViewerProps;
63417
+ const doenetMLRef = React__default.useRef(doenetML);
63418
+ doenetMLRef.current = doenetML;
63419
+ const windowed = mountPolicy?.mode === "windowed";
63420
+ const [parked, setParked] = React__default.useState(windowed);
63421
+ const [parkGeneration, setParkGeneration] = React__default.useState(0);
63422
+ const parkedRef = React__default.useRef(windowed);
63423
+ const parkingRef = React__default.useRef(false);
63424
+ const visibleRef = React__default.useRef(false);
63425
+ const containerRef = React__default.useRef(null);
63426
+ const parkFlushCounterRef = React__default.useRef(0);
63427
+ const parkFlushIdRef = React__default.useRef(null);
63428
+ const parkFlushCleanupRef = React__default.useRef(null);
63429
+ const lastCapturedReportRef = React__default.useRef(null);
63430
+ const parkedSnapshotRef = React__default.useRef(
63431
+ void 0
63432
+ );
63433
+ const everHadStateRef = React__default.useRef(false);
63434
+ const pinnedVariantIndexRef = React__default.useRef(null);
63435
+ if (windowed && pinnedVariantIndexRef.current === null) {
63436
+ pinnedVariantIndexRef.current = doenetViewerProps.requestedVariantIndex ?? Math.floor(Math.random() * 1e6) + 1;
63437
+ }
63438
+ const bootWatchdogRef = React__default.useRef(
63439
+ null
63440
+ );
63441
+ function clearBootWatchdog() {
63442
+ if (bootWatchdogRef.current !== null) {
63443
+ clearTimeout(bootWatchdogRef.current);
63444
+ bootWatchdogRef.current = null;
63445
+ }
63446
+ }
63447
+ function relinquishBootSlot() {
63448
+ clearBootWatchdog();
63449
+ releaseBootSlot(id2);
63450
+ }
63451
+ const composedInitializedCallbackRef = React__default.useRef(null);
63452
+ if (composedInitializedCallbackRef.current === null) {
63453
+ composedInitializedCallbackRef.current = (arg) => {
63454
+ relinquishBootSlot();
63455
+ doenetViewerPropsRef.current.initializedCallback?.(arg);
63456
+ };
63457
+ }
63458
+ function functionPropToProxy(key, prop) {
63459
+ if (windowed && key === "initializedCallback") {
63460
+ return composedInitializedCallbackRef.current;
63461
+ }
63462
+ return prop;
63463
+ }
63464
+ function appendComposedInitializedCallback(proxiedFunctions, hostFunctions) {
63465
+ if (windowed && !("initializedCallback" in hostFunctions)) {
63466
+ proxiedFunctions.push("initializedCallback");
63467
+ proxiedFunctions.push(
63468
+ proxy(composedInitializedCallbackRef.current)
63469
+ );
63470
+ }
63471
+ }
63472
+ const [height, setHeight] = React__default.useState("500px");
63473
+ const [inErrorState, setInErrorState] = React__default.useState(null);
63474
+ const [ignoreDetectedVersion, setIgnoreDetectedVersion] = React__default.useState(false);
63204
63475
  const resolvedTheme = useResolvedTheme(
63205
63476
  doenetViewerProps.darkMode ?? "system"
63206
63477
  );
63478
+ function syncIframeBodyBackground() {
63479
+ const body = ref.current?.contentDocument?.body;
63480
+ if (body) {
63481
+ setIframeBodyBackground(
63482
+ body,
63483
+ doenetViewerPropsRef.current.darkMode
63484
+ );
63485
+ }
63486
+ }
63207
63487
  let standaloneUrl, cssUrl;
63208
63488
  let foundAutoVersion = false;
63209
63489
  let detectedVersion;
@@ -63226,21 +63506,79 @@ function DoenetViewer({
63226
63506
  } else {
63227
63507
  cssUrl = `https://cdn.jsdelivr.net/npm/@doenet/standalone@${selectedDoenetmlVersion}/style.css`;
63228
63508
  }
63229
- const standaloneUrlRef = React__default__default.useRef(standaloneUrl);
63509
+ const usingCustomStandaloneUrl = Boolean(
63510
+ specifiedStandaloneUrl && !foundAutoVersion
63511
+ );
63512
+ const liveUpdatesSupported = usingCustomStandaloneUrl || versionSupportsLiveUpdates(selectedDoenetmlVersion);
63513
+ const standaloneUrlRef = React__default.useRef(standaloneUrl);
63230
63514
  standaloneUrlRef.current = standaloneUrl;
63231
- React__default__default.useEffect(() => {
63232
- if (!useSharedCoreWorker) {
63233
- return;
63515
+ const propsSnapshotStr = serializePropsSnapshot(
63516
+ { doenetML },
63517
+ doenetViewerProps
63518
+ );
63519
+ const legacyReloadKey = liveUpdatesSupported ? "" : propsSnapshotStr;
63520
+ const srcDoc = React__default.useMemo(() => {
63521
+ let bakedProps = doenetViewerPropsRef.current;
63522
+ if (windowed) {
63523
+ bakedProps = { ...bakedProps };
63524
+ if (bakedProps.requestedVariantIndex === void 0) {
63525
+ bakedProps.requestedVariantIndex = pinnedVariantIndexRef.current;
63526
+ }
63527
+ if (parkedSnapshotRef.current !== void 0) {
63528
+ bakedProps.initialState = parkedSnapshotRef.current;
63529
+ bakedProps.flags = {
63530
+ ...bakedProps.flags ?? {},
63531
+ allowLoadState: true
63532
+ };
63533
+ }
63234
63534
  }
63535
+ return createHtmlForDoenetViewer(
63536
+ id2,
63537
+ doenetMLRef.current,
63538
+ bakedProps,
63539
+ standaloneUrl,
63540
+ cssUrl,
63541
+ useSharedCoreWorker
63542
+ );
63543
+ }, [
63544
+ id2,
63545
+ standaloneUrl,
63546
+ cssUrl,
63547
+ useSharedCoreWorker,
63548
+ legacyReloadKey,
63549
+ parkGeneration
63550
+ ]);
63551
+ React__default.useEffect(() => {
63235
63552
  return () => {
63236
63553
  destroySharedCoresForViewer(id2);
63237
63554
  };
63238
63555
  }, []);
63239
- React__default__default.useEffect(() => {
63556
+ React__default.useLayoutEffect(() => {
63557
+ viewerIframeRef.current = null;
63558
+ destroySharedCoresForViewer(id2);
63559
+ lastSentPropsSnapshotRef.current = serializePropsSnapshot(
63560
+ { doenetML: doenetMLRef.current },
63561
+ doenetViewerPropsRef.current
63562
+ );
63563
+ lastSentFunctionPropsRef.current = null;
63564
+ }, [srcDoc]);
63565
+ React__default.useEffect(() => {
63240
63566
  const listener = (event) => {
63241
63567
  if (event.origin !== window.location.origin) {
63242
63568
  return;
63243
63569
  }
63570
+ if (windowed && parkedRef.current && event.data.subject === "SPLICE.flushState") {
63571
+ const currentProps = doenetViewerPropsRef.current;
63572
+ window.postMessage({
63573
+ subject: "SPLICE.flushState.response",
63574
+ activity_id: currentProps.activityId ?? "a",
63575
+ doc_id: currentProps.docId ?? "1",
63576
+ message_id: event.data.message_id,
63577
+ success: true,
63578
+ hadState: everHadStateRef.current
63579
+ });
63580
+ return;
63581
+ }
63244
63582
  if (event.data.subject === "SPLICE.getState.response" || event.data.subject === "SPLICE.requestSolutionView.response" || event.data.subject == "SPLICE.submitAllAnswers" || event.data.subject === "SPLICE.flushState") {
63245
63583
  ref.current?.contentWindow?.postMessage(event.data);
63246
63584
  return;
@@ -63253,6 +63591,16 @@ function DoenetViewer({
63253
63591
  setHeight(event.data.height + "px");
63254
63592
  }
63255
63593
  }
63594
+ if (windowed) {
63595
+ if (event.data.subject === "SPLICE.reportScoreAndState") {
63596
+ lastCapturedReportRef.current = event.data;
63597
+ return;
63598
+ }
63599
+ if (event.data.subject === "SPLICE.flushState.response" && event.data.message_id === parkFlushIdRef.current) {
63600
+ completeParkFlush(event.data);
63601
+ return;
63602
+ }
63603
+ }
63256
63604
  if (event.data?.origin !== id2) {
63257
63605
  return;
63258
63606
  }
@@ -63282,41 +63630,274 @@ function DoenetViewer({
63282
63630
  });
63283
63631
  }
63284
63632
  if (data.error) {
63633
+ if (windowed) {
63634
+ relinquishBootSlot();
63635
+ }
63285
63636
  return setInErrorState(data.error);
63286
63637
  } else if (data.iframeReady) {
63287
63638
  if (ref.current) {
63288
63639
  const viewerIframe = wrap$3(
63289
63640
  windowEndpoint(ref.current.contentWindow)
63290
63641
  );
63642
+ const latestProps = doenetViewerPropsRef.current;
63291
63643
  const proxiedFunctions = [];
63292
- for (const [key, prop] of Object.entries(
63293
- doenetViewerProps
63294
- )) {
63644
+ const registeredFunctions = {};
63645
+ for (const [key, prop] of Object.entries(latestProps)) {
63295
63646
  if (typeof prop === "function") {
63647
+ registeredFunctions[key] = prop;
63296
63648
  proxiedFunctions.push(key);
63297
- proxiedFunctions.push(proxy(prop));
63649
+ proxiedFunctions.push(
63650
+ proxy(functionPropToProxy(key, prop))
63651
+ );
63298
63652
  }
63299
63653
  }
63300
- viewerIframe?.renderViewerWithFunctionProps(
63301
- ...proxiedFunctions
63654
+ appendComposedInitializedCallback(
63655
+ proxiedFunctions,
63656
+ registeredFunctions
63657
+ );
63658
+ viewerIframe.renderViewerWithFunctionProps(...proxiedFunctions).catch(
63659
+ logComlinkError(
63660
+ "DoenetViewer",
63661
+ "renderViewerWithFunctionProps"
63662
+ )
63302
63663
  );
63664
+ lastSentFunctionPropsRef.current = registeredFunctions;
63665
+ viewerIframeRef.current = viewerIframe;
63666
+ const queued = pendingActions.current.splice(0);
63667
+ for (const action of queued) {
63668
+ action(viewerIframe);
63669
+ }
63670
+ syncIframeBodyBackground();
63303
63671
  }
63304
63672
  }
63305
63673
  };
63306
- if (ref.current) {
63307
- window.addEventListener("message", listener);
63308
- }
63674
+ window.addEventListener("message", listener);
63309
63675
  return () => {
63310
63676
  window.removeEventListener("message", listener);
63311
63677
  };
63312
63678
  }, []);
63679
+ React__default.useEffect(() => {
63680
+ if (!liveUpdatesSupported) {
63681
+ return;
63682
+ }
63683
+ if (lastSentPropsSnapshotRef.current === propsSnapshotStr) {
63684
+ return;
63685
+ }
63686
+ lastSentPropsSnapshotRef.current = propsSnapshotStr;
63687
+ const snapshot = JSON.parse(propsSnapshotStr);
63688
+ const action = (remote) => {
63689
+ remote.updateViewerProps(snapshot).catch(logComlinkError("DoenetViewer", "updateViewerProps"));
63690
+ };
63691
+ if (viewerIframeRef.current) {
63692
+ action(viewerIframeRef.current);
63693
+ } else {
63694
+ pendingActions.current.push(action);
63695
+ }
63696
+ }, [propsSnapshotStr, srcDoc, liveUpdatesSupported]);
63697
+ React__default.useEffect(() => {
63698
+ syncIframeBodyBackground();
63699
+ }, [doenetViewerProps.darkMode]);
63700
+ React__default.useEffect(() => {
63701
+ if (!liveUpdatesSupported) {
63702
+ return;
63703
+ }
63704
+ const prev = lastSentFunctionPropsRef.current;
63705
+ if (prev === null) {
63706
+ return;
63707
+ }
63708
+ const current = {};
63709
+ for (const [key, val] of Object.entries(doenetViewerPropsRef.current)) {
63710
+ if (typeof val === "function") {
63711
+ current[key] = val;
63712
+ }
63713
+ }
63714
+ const prevKeys = Object.keys(prev);
63715
+ const currKeys = Object.keys(current);
63716
+ let changed = prevKeys.length !== currKeys.length;
63717
+ if (!changed) {
63718
+ for (const k2 of currKeys) {
63719
+ if (prev[k2] !== current[k2]) {
63720
+ changed = true;
63721
+ break;
63722
+ }
63723
+ }
63724
+ }
63725
+ if (!changed) {
63726
+ return;
63727
+ }
63728
+ lastSentFunctionPropsRef.current = current;
63729
+ const proxiedFunctions = [];
63730
+ for (const [key, val] of Object.entries(current)) {
63731
+ proxiedFunctions.push(key);
63732
+ proxiedFunctions.push(proxy(functionPropToProxy(key, val)));
63733
+ }
63734
+ appendComposedInitializedCallback(proxiedFunctions, current);
63735
+ const action = (remote) => {
63736
+ remote.updateViewerFunctionProps(...proxiedFunctions).catch(
63737
+ logComlinkError(
63738
+ "DoenetViewer",
63739
+ "updateViewerFunctionProps"
63740
+ )
63741
+ );
63742
+ };
63743
+ if (viewerIframeRef.current) {
63744
+ action(viewerIframeRef.current);
63745
+ } else {
63746
+ pendingActions.current.push(action);
63747
+ }
63748
+ });
63749
+ function hasPersistencePath() {
63750
+ const flags = doenetViewerPropsRef.current.flags;
63751
+ return Boolean(flags?.allowSaveState || flags?.allowLocalState);
63752
+ }
63753
+ function canPark() {
63754
+ return hasPersistencePath() && Boolean(ref.current?.contentWindow);
63755
+ }
63756
+ function beginPark() {
63757
+ if (parkedRef.current || parkingRef.current) {
63758
+ return;
63759
+ }
63760
+ if (!ref.current?.contentWindow) {
63761
+ notifyViewerState(id2, "live");
63762
+ return;
63763
+ }
63764
+ parkingRef.current = true;
63765
+ const flushId = `__doenetParkFlush-${id2}-${++parkFlushCounterRef.current}`;
63766
+ parkFlushIdRef.current = flushId;
63767
+ const post = () => {
63768
+ ref.current?.contentWindow?.postMessage({
63769
+ subject: "SPLICE.flushState",
63770
+ message_id: flushId
63771
+ });
63772
+ };
63773
+ const retryTimer = setInterval(post, 500);
63774
+ const timeoutTimer = setTimeout(() => {
63775
+ onParkFlushTimeout();
63776
+ }, mountPolicy?.flushTimeoutMs ?? DEFAULT_FLUSH_TIMEOUT_MS);
63777
+ parkFlushCleanupRef.current = () => {
63778
+ clearInterval(retryTimer);
63779
+ clearTimeout(timeoutTimer);
63780
+ parkFlushCleanupRef.current = null;
63781
+ };
63782
+ post();
63783
+ }
63784
+ function endParkFlush() {
63785
+ if (!parkingRef.current) {
63786
+ return false;
63787
+ }
63788
+ parkFlushCleanupRef.current?.();
63789
+ parkFlushIdRef.current = null;
63790
+ parkingRef.current = false;
63791
+ if (visibleRef.current) {
63792
+ notifyViewerState(id2, "live");
63793
+ return false;
63794
+ }
63795
+ return true;
63796
+ }
63797
+ function completeParkFlush(ack) {
63798
+ if (!endParkFlush()) {
63799
+ return;
63800
+ }
63801
+ if (ack.hadState) {
63802
+ everHadStateRef.current = true;
63803
+ if (lastCapturedReportRef.current?.state) {
63804
+ parkedSnapshotRef.current = lastCapturedReportRef.current.state;
63805
+ }
63806
+ }
63807
+ finishPark();
63808
+ }
63809
+ function onParkFlushTimeout() {
63810
+ if (!endParkFlush()) {
63811
+ return;
63812
+ }
63813
+ finishPark();
63814
+ }
63815
+ function finishPark() {
63816
+ viewerIframeRef.current = null;
63817
+ destroySharedCoresForViewer(id2);
63818
+ relinquishBootSlot();
63819
+ parkedRef.current = true;
63820
+ setParked(true);
63821
+ notifyViewerState(id2, "parked");
63822
+ }
63823
+ function unpark() {
63824
+ if (!parkedRef.current) {
63825
+ return;
63826
+ }
63827
+ requestBootSlot(id2, () => {
63828
+ if (!parkedRef.current || !visibleRef.current) {
63829
+ releaseBootSlot(id2);
63830
+ return;
63831
+ }
63832
+ parkedRef.current = false;
63833
+ setParked(false);
63834
+ setParkGeneration((generation) => generation + 1);
63835
+ notifyViewerState(id2, "live");
63836
+ clearBootWatchdog();
63837
+ bootWatchdogRef.current = setTimeout(() => {
63838
+ bootWatchdogRef.current = null;
63839
+ releaseBootSlot(id2);
63840
+ }, BOOT_SLOT_WATCHDOG_MS);
63841
+ });
63842
+ }
63843
+ React__default.useEffect(() => {
63844
+ if (!windowed || !containerRef.current) {
63845
+ return;
63846
+ }
63847
+ const observer = new IntersectionObserver(
63848
+ (entries) => {
63849
+ for (const entry of entries) {
63850
+ visibleRef.current = entry.isIntersecting;
63851
+ setViewerVisibility(id2, entry.isIntersecting);
63852
+ if (entry.isIntersecting && parkedRef.current) {
63853
+ unpark();
63854
+ } else if (!entry.isIntersecting) {
63855
+ cancelBootRequest(id2);
63856
+ }
63857
+ }
63858
+ },
63859
+ {
63860
+ rootMargin: mountPolicy?.visibleMargin ?? DEFAULT_VISIBLE_MARGIN
63861
+ }
63862
+ );
63863
+ observer.observe(containerRef.current);
63864
+ return () => {
63865
+ observer.disconnect();
63866
+ };
63867
+ }, [windowed]);
63868
+ React__default.useEffect(() => {
63869
+ if (!windowed) {
63870
+ return;
63871
+ }
63872
+ if (!hasPersistencePath()) {
63873
+ console.warn(
63874
+ "DoenetViewer mountPolicy: windowed mounting is enabled but neither flags.allowSaveState nor flags.allowLocalState is set, so parking would lose student work. This viewer will always stay live."
63875
+ );
63876
+ }
63877
+ const unregister = registerWindowedViewer({
63878
+ id: id2,
63879
+ maxLiveViewers: mountPolicy?.maxLiveViewers ?? DEFAULT_MAX_LIVE_VIEWERS,
63880
+ maxConcurrentBoots: mountPolicy?.maxConcurrentBoots ?? DEFAULT_MAX_CONCURRENT_BOOTS,
63881
+ parkDelayMs: mountPolicy?.parkDelayMs ?? DEFAULT_PARK_DELAY_MS,
63882
+ canPark,
63883
+ requestPark: beginPark
63884
+ });
63885
+ if (parkedRef.current) {
63886
+ notifyViewerState(id2, "parked");
63887
+ }
63888
+ return () => {
63889
+ parkFlushCleanupRef.current?.();
63890
+ clearBootWatchdog();
63891
+ unregister();
63892
+ };
63893
+ }, [windowed]);
63313
63894
  if (inErrorState) {
63314
63895
  if (foundAutoVersion) {
63315
63896
  setIgnoreDetectedVersion(true);
63316
63897
  setInErrorState("");
63317
63898
  return null;
63318
63899
  }
63319
- let errorIcon = /* @__PURE__ */ React__default__default.createElement(
63900
+ let errorIcon = /* @__PURE__ */ React__default.createElement(
63320
63901
  MdError,
63321
63902
  {
63322
63903
  fontSize: "24pt",
@@ -63324,7 +63905,7 @@ function DoenetViewer({
63324
63905
  style: { verticalAlign: "middle" }
63325
63906
  }
63326
63907
  );
63327
- return /* @__PURE__ */ React__default__default.createElement(
63908
+ return /* @__PURE__ */ React__default.createElement(
63328
63909
  "div",
63329
63910
  {
63330
63911
  style: {
@@ -63344,19 +63925,28 @@ function DoenetViewer({
63344
63925
  inErrorState
63345
63926
  );
63346
63927
  }
63347
- return /* @__PURE__ */ React__default__default.createElement(React__default__default.Fragment, null, addVirtualKeyboard ? /* @__PURE__ */ React__default__default.createElement(ExternalVirtualKeyboard, { ownerRef: ref, theme: resolvedTheme }) : null, /* @__PURE__ */ React__default__default.createElement(
63928
+ const viewerContent = parked ? (
63929
+ // Fixed-height placeholder holding a parked viewer's place in the
63930
+ // layout (the last height the iframe reported survives in state, so
63931
+ // parking does not shift the page).
63932
+ /* @__PURE__ */ React__default.createElement(
63933
+ "div",
63934
+ {
63935
+ "data-doenet-parked-viewer": "true",
63936
+ style: {
63937
+ width: "100%",
63938
+ boxSizing: "border-box",
63939
+ height,
63940
+ minHeight: 50
63941
+ }
63942
+ }
63943
+ )
63944
+ ) : /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, addVirtualKeyboard ? /* @__PURE__ */ React__default.createElement(ExternalVirtualKeyboard, { ownerRef: ref, theme: resolvedTheme }) : null, /* @__PURE__ */ React__default.createElement(
63348
63945
  "iframe",
63349
63946
  {
63350
63947
  title: "Doenet document",
63351
63948
  ref,
63352
- srcDoc: createHtmlForDoenetViewer(
63353
- id2,
63354
- doenetML,
63355
- doenetViewerProps,
63356
- standaloneUrl,
63357
- cssUrl,
63358
- useSharedCoreWorker
63359
- ),
63949
+ srcDoc,
63360
63950
  style: {
63361
63951
  width: "100%",
63362
63952
  boxSizing: "border-box",
@@ -63367,13 +63957,26 @@ function DoenetViewer({
63367
63957
  height
63368
63958
  }
63369
63959
  ));
63960
+ if (!windowed) {
63961
+ return viewerContent;
63962
+ }
63963
+ return /* @__PURE__ */ React__default.createElement("div", { ref: containerRef, style: { width: "100%" } }, viewerContent);
63964
+ }
63965
+ function serializePropsSnapshot(seed, props) {
63966
+ const snapshot = { ...seed };
63967
+ for (const [key, val] of Object.entries(props)) {
63968
+ if (typeof val !== "function") {
63969
+ snapshot[key] = val;
63970
+ }
63971
+ }
63972
+ return JSON.stringify(snapshot);
63370
63973
  }
63371
- function logComlinkError(method) {
63974
+ function logComlinkError(component, method) {
63372
63975
  return (err) => {
63373
- console.warn(`iframe DoenetEditor: ${method} failed`, err);
63976
+ console.warn(`iframe ${component}: ${method} failed`, err);
63374
63977
  };
63375
63978
  }
63376
- const DoenetEditor = React__default__default.forwardRef(function DoenetEditor2({
63979
+ const DoenetEditor = React__default.forwardRef(function DoenetEditor2({
63377
63980
  doenetML,
63378
63981
  standaloneUrl: specifiedStandaloneUrl,
63379
63982
  cssUrl: specifiedCssUrl,
@@ -63383,17 +63986,17 @@ const DoenetEditor = React__default__default.forwardRef(function DoenetEditor2({
63383
63986
  autodetectVersion = true,
63384
63987
  ...doenetEditorProps
63385
63988
  }, forwardedRef) {
63386
- const [id2, _2] = React__default__default.useState(() => Math.random().toString(36).slice(2));
63387
- const ref = React__default__default.useRef(null);
63388
- const editorIframeRef = React__default__default.useRef(null);
63389
- const pendingActions = React__default__default.useRef([]);
63390
- const lastSentPropsSnapshotRef = React__default__default.useRef(null);
63391
- const lastSentFunctionPropsRef = React__default__default.useRef(null);
63392
- const doenetEditorPropsRef = React__default__default.useRef(doenetEditorProps);
63989
+ const [id2, _2] = React__default.useState(() => Math.random().toString(36).slice(2));
63990
+ const ref = React__default.useRef(null);
63991
+ const editorIframeRef = React__default.useRef(null);
63992
+ const pendingActions = React__default.useRef([]);
63993
+ const lastSentPropsSnapshotRef = React__default.useRef(null);
63994
+ const lastSentFunctionPropsRef = React__default.useRef(null);
63995
+ const doenetEditorPropsRef = React__default.useRef(doenetEditorProps);
63393
63996
  doenetEditorPropsRef.current = doenetEditorProps;
63394
- const [inErrorState, setInErrorState] = React__default__default.useState(null);
63395
- const [ignoreDetectedVersion, setIgnoreDetectedVersion] = React__default__default.useState(false);
63396
- const [initialDiagnostics, setInitialDiagnostics] = React__default__default.useState([]);
63997
+ const [inErrorState, setInErrorState] = React__default.useState(null);
63998
+ const [ignoreDetectedVersion, setIgnoreDetectedVersion] = React__default.useState(false);
63999
+ const [initialDiagnostics, setInitialDiagnostics] = React__default.useState([]);
63397
64000
  const resolvedTheme = useResolvedTheme(
63398
64001
  doenetEditorProps.darkMode ?? "system"
63399
64002
  );
@@ -63406,12 +64009,17 @@ const DoenetEditor = React__default__default.forwardRef(function DoenetEditor2({
63406
64009
  );
63407
64010
  }
63408
64011
  }
63409
- React__default__default.useImperativeHandle(
64012
+ React__default.useImperativeHandle(
63410
64013
  forwardedRef,
63411
64014
  () => ({
63412
64015
  openDiagnosticsTab(tabId) {
63413
64016
  const action = (remote) => {
63414
- remote.openDiagnosticsTab(tabId).catch(logComlinkError("openDiagnosticsTab"));
64017
+ remote.openDiagnosticsTab(tabId).catch(
64018
+ logComlinkError(
64019
+ "DoenetEditor",
64020
+ "openDiagnosticsTab"
64021
+ )
64022
+ );
63415
64023
  };
63416
64024
  if (editorIframeRef.current) {
63417
64025
  action(editorIframeRef.current);
@@ -63421,7 +64029,12 @@ const DoenetEditor = React__default__default.forwardRef(function DoenetEditor2({
63421
64029
  },
63422
64030
  closeDiagnosticsPanel() {
63423
64031
  const action = (remote) => {
63424
- remote.closeDiagnosticsPanel().catch(logComlinkError("closeDiagnosticsPanel"));
64032
+ remote.closeDiagnosticsPanel().catch(
64033
+ logComlinkError(
64034
+ "DoenetEditor",
64035
+ "closeDiagnosticsPanel"
64036
+ )
64037
+ );
63425
64038
  };
63426
64039
  if (editorIframeRef.current) {
63427
64040
  action(editorIframeRef.current);
@@ -63431,7 +64044,12 @@ const DoenetEditor = React__default__default.forwardRef(function DoenetEditor2({
63431
64044
  },
63432
64045
  updateRenderedView() {
63433
64046
  const action = (remote) => {
63434
- remote.updateRenderedView().catch(logComlinkError("updateRenderedView"));
64047
+ remote.updateRenderedView().catch(
64048
+ logComlinkError(
64049
+ "DoenetEditor",
64050
+ "updateRenderedView"
64051
+ )
64052
+ );
63435
64053
  };
63436
64054
  if (editorIframeRef.current) {
63437
64055
  action(editorIframeRef.current);
@@ -63442,7 +64060,7 @@ const DoenetEditor = React__default__default.forwardRef(function DoenetEditor2({
63442
64060
  }),
63443
64061
  []
63444
64062
  );
63445
- const initialIframePropsRef = React__default__default.useRef(null);
64063
+ const initialIframePropsRef = React__default.useRef(null);
63446
64064
  if (initialIframePropsRef.current === null) {
63447
64065
  initialIframePropsRef.current = {
63448
64066
  doenetML,
@@ -63473,7 +64091,7 @@ const DoenetEditor = React__default__default.forwardRef(function DoenetEditor2({
63473
64091
  } else {
63474
64092
  cssUrl = `https://cdn.jsdelivr.net/npm/@doenet/standalone@${selectedDoenetmlVersion}/style.css`;
63475
64093
  }
63476
- React__default__default.useEffect(() => {
64094
+ React__default.useEffect(() => {
63477
64095
  const listener = (event) => {
63478
64096
  if (event.origin !== window.location.origin || event.data?.origin !== id2) {
63479
64097
  return;
@@ -63497,7 +64115,10 @@ const DoenetEditor = React__default__default.forwardRef(function DoenetEditor2({
63497
64115
  }
63498
64116
  }
63499
64117
  editorIframe?.renderEditorWithFunctionProps(...proxiedFunctions).catch(
63500
- logComlinkError("renderEditorWithFunctionProps")
64118
+ logComlinkError(
64119
+ "DoenetEditor",
64120
+ "renderEditorWithFunctionProps"
64121
+ )
63501
64122
  );
63502
64123
  lastSentFunctionPropsRef.current = registeredFunctions;
63503
64124
  editorIframeRef.current = editorIframe;
@@ -63516,7 +64137,7 @@ const DoenetEditor = React__default__default.forwardRef(function DoenetEditor2({
63516
64137
  window.removeEventListener("message", listener);
63517
64138
  };
63518
64139
  }, []);
63519
- const srcDoc = React__default__default.useMemo(() => {
64140
+ const srcDoc = React__default.useMemo(() => {
63520
64141
  const baseProps = {
63521
64142
  ...initialIframePropsRef.current.doenetEditorProps
63522
64143
  };
@@ -63537,36 +64158,26 @@ const DoenetEditor = React__default__default.forwardRef(function DoenetEditor2({
63537
64158
  cssUrl
63538
64159
  );
63539
64160
  }, [id2, standaloneUrl, cssUrl, initialDiagnostics]);
63540
- const propsForUpdate = { width };
63541
- for (const [key, val] of Object.entries(doenetEditorProps)) {
63542
- if (typeof val !== "function") {
63543
- propsForUpdate[key] = val;
63544
- }
63545
- }
63546
- const propsSnapshotStr = JSON.stringify(propsForUpdate);
63547
- React__default__default.useLayoutEffect(() => {
64161
+ const propsSnapshotStr = serializePropsSnapshot(
64162
+ { width },
64163
+ doenetEditorProps
64164
+ );
64165
+ React__default.useLayoutEffect(() => {
63548
64166
  editorIframeRef.current = null;
63549
- const baselineProps = {
63550
- width: initialIframePropsRef.current.width
63551
- };
63552
- for (const [key, val] of Object.entries(
64167
+ lastSentPropsSnapshotRef.current = serializePropsSnapshot(
64168
+ { width: initialIframePropsRef.current.width },
63553
64169
  initialIframePropsRef.current.doenetEditorProps
63554
- )) {
63555
- if (typeof val !== "function") {
63556
- baselineProps[key] = val;
63557
- }
63558
- }
63559
- lastSentPropsSnapshotRef.current = JSON.stringify(baselineProps);
64170
+ );
63560
64171
  lastSentFunctionPropsRef.current = null;
63561
64172
  }, [srcDoc]);
63562
- React__default__default.useEffect(() => {
64173
+ React__default.useEffect(() => {
63563
64174
  if (lastSentPropsSnapshotRef.current === propsSnapshotStr) {
63564
64175
  return;
63565
64176
  }
63566
64177
  lastSentPropsSnapshotRef.current = propsSnapshotStr;
63567
64178
  const snapshot = JSON.parse(propsSnapshotStr);
63568
64179
  const action = (remote) => {
63569
- remote.updateEditorProps(snapshot).catch(logComlinkError("updateEditorProps"));
64180
+ remote.updateEditorProps(snapshot).catch(logComlinkError("DoenetEditor", "updateEditorProps"));
63570
64181
  };
63571
64182
  if (editorIframeRef.current) {
63572
64183
  action(editorIframeRef.current);
@@ -63574,10 +64185,10 @@ const DoenetEditor = React__default__default.forwardRef(function DoenetEditor2({
63574
64185
  pendingActions.current.push(action);
63575
64186
  }
63576
64187
  }, [propsSnapshotStr, srcDoc]);
63577
- React__default__default.useEffect(() => {
64188
+ React__default.useEffect(() => {
63578
64189
  syncIframeBodyBackground();
63579
64190
  }, [doenetEditorProps.darkMode]);
63580
- React__default__default.useEffect(() => {
64191
+ React__default.useEffect(() => {
63581
64192
  const prev = lastSentFunctionPropsRef.current;
63582
64193
  if (prev === null) {
63583
64194
  return;
@@ -63609,7 +64220,12 @@ const DoenetEditor = React__default__default.forwardRef(function DoenetEditor2({
63609
64220
  proxiedFunctions.push(proxy(val));
63610
64221
  }
63611
64222
  const action = (remote) => {
63612
- remote.updateEditorFunctionProps(...proxiedFunctions).catch(logComlinkError("updateEditorFunctionProps"));
64223
+ remote.updateEditorFunctionProps(...proxiedFunctions).catch(
64224
+ logComlinkError(
64225
+ "DoenetEditor",
64226
+ "updateEditorFunctionProps"
64227
+ )
64228
+ );
63613
64229
  };
63614
64230
  if (editorIframeRef.current) {
63615
64231
  action(editorIframeRef.current);
@@ -63635,7 +64251,7 @@ const DoenetEditor = React__default__default.forwardRef(function DoenetEditor2({
63635
64251
  ]);
63636
64252
  return null;
63637
64253
  }
63638
- let errorIcon = /* @__PURE__ */ React__default__default.createElement(
64254
+ let errorIcon = /* @__PURE__ */ React__default.createElement(
63639
64255
  MdError,
63640
64256
  {
63641
64257
  fontSize: "24pt",
@@ -63643,7 +64259,7 @@ const DoenetEditor = React__default__default.forwardRef(function DoenetEditor2({
63643
64259
  style: { verticalAlign: "middle", marginRight: "5px" }
63644
64260
  }
63645
64261
  );
63646
- return /* @__PURE__ */ React__default__default.createElement(
64262
+ return /* @__PURE__ */ React__default.createElement(
63647
64263
  "div",
63648
64264
  {
63649
64265
  style: {
@@ -63663,7 +64279,7 @@ const DoenetEditor = React__default__default.forwardRef(function DoenetEditor2({
63663
64279
  inErrorState
63664
64280
  );
63665
64281
  }
63666
- return /* @__PURE__ */ React__default__default.createElement(React__default__default.Fragment, null, addVirtualKeyboard ? /* @__PURE__ */ React__default__default.createElement(ExternalVirtualKeyboard, { ownerRef: ref, theme: resolvedTheme }) : null, /* @__PURE__ */ React__default__default.createElement(
64282
+ return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, addVirtualKeyboard ? /* @__PURE__ */ React__default.createElement(ExternalVirtualKeyboard, { ownerRef: ref, theme: resolvedTheme }) : null, /* @__PURE__ */ React__default.createElement(
63667
64283
  "iframe",
63668
64284
  {
63669
64285
  title: "Doenet Editor",
@@ -63686,6 +64302,7 @@ export {
63686
64302
  defaultCreativeCommonsVersion$1 as defaultCreativeCommonsVersion,
63687
64303
  getMediaLicenseDisplay,
63688
64304
  getMediaLicenseInfo,
64305
+ getViewerLifecycleStats,
63689
64306
  mathjaxConfig$1 as mathjaxConfig,
63690
64307
  mediaLicenses$1 as mediaLicenses,
63691
64308
  version