@doenet/doenetml-iframe 0.7.21-dev.352 → 0.7.21-dev.354
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/README.md +86 -1
- package/index.d.ts +28 -1
- package/index.js +76 -41
- package/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -80,7 +80,7 @@ the inner viewer applies them with the same semantics as the in-process
|
|
|
80
80
|
|
|
81
81
|
| Prop change | Effect |
|
|
82
82
|
| -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
83
|
-
| `render`, `darkMode`, `flags`, `answerResponseCounts`, callbacks, …
|
|
83
|
+
| `render`, `darkMode`, `styleOverrides`, `flags`, `answerResponseCounts`, callbacks, … | applied live, no reload (flipping `render` false→true starts the document in the already-loaded realm) |
|
|
84
84
|
| `doenetML`, `activityId`, `docId`, `attemptNumber`, `requestedVariantIndex` | the document's core re-initializes **inside the same iframe realm** — the multi-MB bundle is not re-parsed |
|
|
85
85
|
| `initialState`, `forceDisable` and the other `force*` props, `userId` | read at (re-)initialization only, exactly like the in-process viewer — change `docId`/`attemptNumber` or remount via `key=` to apply |
|
|
86
86
|
| `standaloneUrl`, `cssUrl`, `doenetmlVersion` (or a version change detected in `doenetML`), `useSharedCoreWorker` | a different bundle/realm is required, so the iframe reloads |
|
|
@@ -88,6 +88,91 @@ the inner viewer applies them with the same semantics as the in-process
|
|
|
88
88
|
To force a full remount (fresh realm and worker), change the component's
|
|
89
89
|
React `key`.
|
|
90
90
|
|
|
91
|
+
### Reader style overrides (`styleOverrides`)
|
|
92
|
+
|
|
93
|
+
Hosts can let a **reader** remap what each style number looks like — for
|
|
94
|
+
example a color-blind user picking colors they can better tell apart — by
|
|
95
|
+
passing a `styleOverrides` prop (type `ReaderStyleOverrides`, re-exported
|
|
96
|
+
from this package). Overrides win over everything authored in the document
|
|
97
|
+
(`<styleDefinition>` and `<stylePalette>` alike) and update live when the
|
|
98
|
+
prop changes:
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
<DoenetViewer
|
|
102
|
+
doenetML={source}
|
|
103
|
+
styleOverrides={{
|
|
104
|
+
styles: {
|
|
105
|
+
1: { lineColor: "#0072b2", markerColor: "#0072b2" },
|
|
106
|
+
2: { lineColor: "#d55e00", lineWidth: 6 },
|
|
107
|
+
},
|
|
108
|
+
}}
|
|
109
|
+
/>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
A reader can also switch the whole document to one of the built-in style
|
|
113
|
+
palettes by name (case-insensitive) — for example `grayscale`, four grays
|
|
114
|
+
laddered for maximum luminance separation, for readers who distinguish
|
|
115
|
+
styles by lightness alone, or `okabeIto` for common color vision
|
|
116
|
+
deficiencies:
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
<DoenetViewer
|
|
120
|
+
doenetML={source}
|
|
121
|
+
styleOverrides={{ palette: "grayscale" }}
|
|
122
|
+
/>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
A reader-selected palette replaces the document's base styles everywhere:
|
|
126
|
+
authored `<stylePalette>` selections and `<styleDefinition>` customizations
|
|
127
|
+
are discarded (they were tuned against different colors), style numbers
|
|
128
|
+
beyond the reader palette's size wrap around onto it, and any `styles`
|
|
129
|
+
overrides apply on top of the reader's palette. Unregistered palette names
|
|
130
|
+
are ignored, so offer readers the palette list as fixed choices.
|
|
131
|
+
|
|
132
|
+
Every style-definition key is overridable except the `*Word` descriptors:
|
|
133
|
+
all colors (including the `*DarkMode` variants), opacities, `lineWidth`,
|
|
134
|
+
`lineStyle`, `markerStyle`, `markerSize`, `markerFilled`, and the fill
|
|
135
|
+
settings. Human-readable color/style words are always re-derived from the
|
|
136
|
+
overridden values, so text style descriptions (e.g. "the blue line") stay
|
|
137
|
+
truthful, and a missing dark-mode color is derived from the reader's
|
|
138
|
+
light-mode color with the same accessibility-aware derivation authored
|
|
139
|
+
styles get. Pass `null` (or omit the prop) to clear all overrides.
|
|
140
|
+
|
|
141
|
+
The same prop exists on `<DoenetEditor>`, where it applies to the editor's
|
|
142
|
+
rendered preview (the code pane and context help keep showing the authored
|
|
143
|
+
values).
|
|
144
|
+
|
|
145
|
+
#### Listing the available palettes (`onStylePalettes`)
|
|
146
|
+
|
|
147
|
+
To build a palette picker, a host needs the palettes that the DoenetML
|
|
148
|
+
version **inside the iframe** supports — this wrapper and the standalone
|
|
149
|
+
bundle ship separately, and a host may pin an older `doenetmlVersion`. The
|
|
150
|
+
`onStylePalettes` callback fires once the bundle has booted, with that
|
|
151
|
+
bundle's palettes:
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
const [palettes, setPalettes] = React.useState(null);
|
|
155
|
+
|
|
156
|
+
<DoenetViewer
|
|
157
|
+
doenetML={source}
|
|
158
|
+
onStylePalettes={setPalettes}
|
|
159
|
+
styleOverrides={chosenPalette ? { palette: chosenPalette } : undefined}
|
|
160
|
+
/>;
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Each entry is `{ name, description, styles }`, where `styles` is keyed by
|
|
164
|
+
style number (contiguous from 1, always at least four) and each style is a
|
|
165
|
+
fully-resolved style definition — `lineColor`, `markerColor`, `fillColor`,
|
|
166
|
+
`textColor` and their `*DarkMode` variants for swatches in either theme,
|
|
167
|
+
plus `lineWidth`, `lineStyle`, `markerStyle`, and `markerSize` for the
|
|
168
|
+
non-color distinctions palettes also carry, and the `*Word` fields (e.g.
|
|
169
|
+
`"blue"`) for labelling swatches accessibly. Feed a chosen `name` back in as
|
|
170
|
+
the `palette` field of `styleOverrides`.
|
|
171
|
+
|
|
172
|
+
The callback receives `null` when the booted bundle predates palette
|
|
173
|
+
discovery, which is the host's cue to hide the picker. The same prop exists
|
|
174
|
+
on `<DoenetEditor>`.
|
|
175
|
+
|
|
91
176
|
Function props (callbacks) are forwarded across the iframe boundary via
|
|
92
177
|
Comlink proxies and always follow the latest identity passed — parents may
|
|
93
178
|
pass inline arrow functions freely; identity churn does not re-render the
|
package/index.d.ts
CHANGED
|
@@ -15,6 +15,9 @@ import { MediaLicenseDisplay } from '../packages/utils/dist';
|
|
|
15
15
|
import { MediaLicenseInfo } from '../packages/utils/dist';
|
|
16
16
|
import { MediaLicenseKind } from '../packages/utils/dist';
|
|
17
17
|
import { mediaLicenses } from '../packages/utils/dist';
|
|
18
|
+
import { ReaderStyleOverrides } from '../packages/utils/dist';
|
|
19
|
+
import { ReaderStyleValueOverrides } from '../packages/utils/dist';
|
|
20
|
+
import { StylePaletteInfo } from '../packages/utils/dist';
|
|
18
21
|
import { WarningRecord } from '../packages/utils/dist';
|
|
19
22
|
|
|
20
23
|
export { CreativeCommonsVersion }
|
|
@@ -32,6 +35,15 @@ export declare const DoenetEditor: default_2.ForwardRefExoticComponent<Omit<Doen
|
|
|
32
35
|
export { DoenetEditorHandle }
|
|
33
36
|
|
|
34
37
|
export declare type DoenetEditorIframeProps = DoenetEditorProps & {
|
|
38
|
+
/**
|
|
39
|
+
* Called once the standalone bundle inside the iframe has booted, with
|
|
40
|
+
* the style palettes THAT bundle supports — the source of truth for a
|
|
41
|
+
* host rendering a palette picker, since the iframe may run a pinned or
|
|
42
|
+
* older DoenetML version than this wrapper. Receives `null` when the
|
|
43
|
+
* booted bundle predates palette discovery. Feed a chosen palette name
|
|
44
|
+
* back in as the `palette` field of `styleOverrides`.
|
|
45
|
+
*/
|
|
46
|
+
onStylePalettes?: (palettes: StylePaletteInfo[] | null) => void;
|
|
35
47
|
doenetML: string;
|
|
36
48
|
/**
|
|
37
49
|
* The URL of a standalone DoenetML bundle. This may be from the CDN.
|
|
@@ -86,10 +98,19 @@ declare type DoenetEditorProps = Omit<React.ComponentProps<typeof DoenetEditor_2
|
|
|
86
98
|
* v0.7.18 cannot re-render in place; for those the wrapper falls back to
|
|
87
99
|
* reloading the iframe on any prop change, its historical behavior.)
|
|
88
100
|
*/
|
|
89
|
-
export declare function DoenetViewer({ doenetML, standaloneUrl: specifiedStandaloneUrl, cssUrl: specifiedCssUrl, doenetmlVersion: specifiedDoenetmlVersion, autodetectVersion, useSharedCoreWorker, mountPolicy, keepLive, ...doenetViewerProps }: DoenetViewerIframeProps): default_2.JSX.Element | null;
|
|
101
|
+
export declare function DoenetViewer({ doenetML, standaloneUrl: specifiedStandaloneUrl, cssUrl: specifiedCssUrl, doenetmlVersion: specifiedDoenetmlVersion, autodetectVersion, useSharedCoreWorker, mountPolicy, keepLive, onStylePalettes, ...doenetViewerProps }: DoenetViewerIframeProps): default_2.JSX.Element | null;
|
|
90
102
|
|
|
91
103
|
export declare type DoenetViewerIframeProps = DoenetViewerProps & {
|
|
92
104
|
doenetML: string;
|
|
105
|
+
/**
|
|
106
|
+
* Called once the standalone bundle inside the iframe has booted, with
|
|
107
|
+
* the style palettes THAT bundle supports — the source of truth for a
|
|
108
|
+
* host rendering a palette picker, since the iframe may run a pinned or
|
|
109
|
+
* older DoenetML version than this wrapper. Receives `null` when the
|
|
110
|
+
* booted bundle predates palette discovery. Feed a chosen palette name
|
|
111
|
+
* back in as the `palette` field of `styleOverrides`.
|
|
112
|
+
*/
|
|
113
|
+
onStylePalettes?: (palettes: StylePaletteInfo[] | null) => void;
|
|
93
114
|
/**
|
|
94
115
|
* The URL of a standalone DoenetML bundle. This may be from the CDN.
|
|
95
116
|
* If autodetectVersion is `true` and a version is detected, this URL is ignored.
|
|
@@ -250,6 +271,12 @@ export declare type MountPolicy = {
|
|
|
250
271
|
maxConcurrentBoots?: number;
|
|
251
272
|
};
|
|
252
273
|
|
|
274
|
+
export { ReaderStyleOverrides }
|
|
275
|
+
|
|
276
|
+
export { ReaderStyleValueOverrides }
|
|
277
|
+
|
|
278
|
+
export { StylePaletteInfo }
|
|
279
|
+
|
|
253
280
|
export declare const version: string;
|
|
254
281
|
|
|
255
282
|
export { WarningRecord }
|
package/index.js
CHANGED
|
@@ -1004,7 +1004,7 @@ const defaultPalette$1 = {
|
|
|
1004
1004
|
}
|
|
1005
1005
|
};
|
|
1006
1006
|
const okabeItoPalette$1 = {
|
|
1007
|
-
name: "
|
|
1007
|
+
name: "okabeIto",
|
|
1008
1008
|
description: "Colorblind-friendly blues, oranges, greens, and purples adapted from the Okabe-Ito palette, with varied marker shapes and line styles.",
|
|
1009
1009
|
styles: {
|
|
1010
1010
|
1: {
|
|
@@ -1175,7 +1175,7 @@ const okabeItoPalette$1 = {
|
|
|
1175
1175
|
}
|
|
1176
1176
|
};
|
|
1177
1177
|
const tolBrightPalette$1 = {
|
|
1178
|
-
name: "
|
|
1178
|
+
name: "tolBright",
|
|
1179
1179
|
description: "Colorblind-friendly bright blues, reds, greens, and purples from Paul Tol's bright palette, with varied marker shapes and line styles.",
|
|
1180
1180
|
styles: {
|
|
1181
1181
|
1: {
|
|
@@ -1309,7 +1309,7 @@ const tolBrightPalette$1 = {
|
|
|
1309
1309
|
}
|
|
1310
1310
|
};
|
|
1311
1311
|
const tolMutedPalette$1 = {
|
|
1312
|
-
name: "
|
|
1312
|
+
name: "tolMuted",
|
|
1313
1313
|
description: "Nine colorblind-friendly muted styles from Paul Tol's muted palette, with varied marker shapes and line styles.",
|
|
1314
1314
|
styles: {
|
|
1315
1315
|
1: {
|
|
@@ -1508,7 +1508,7 @@ const tolMutedPalette$1 = {
|
|
|
1508
1508
|
}
|
|
1509
1509
|
};
|
|
1510
1510
|
const tolHighContrastPalette$1 = {
|
|
1511
|
-
name: "
|
|
1511
|
+
name: "tolHighContrast",
|
|
1512
1512
|
description: "Four maximum-distinction styles from Paul Tol's high-contrast palette, distinguishable even in grayscale.",
|
|
1513
1513
|
styles: {
|
|
1514
1514
|
1: {
|
|
@@ -2018,7 +2018,7 @@ const categoricalPalette$1 = {
|
|
|
2018
2018
|
}
|
|
2019
2019
|
};
|
|
2020
2020
|
const grumpyNarwhalPalette$1 = {
|
|
2021
|
-
name: "
|
|
2021
|
+
name: "grumpyNarwhal",
|
|
2022
2022
|
description: "Six saturated hues — burnt orange, forest green, deep pink, arctic teal, gold, and purple — going neon in dark mode.",
|
|
2023
2023
|
styles: {
|
|
2024
2024
|
1: {
|
|
@@ -2174,24 +2174,27 @@ function deepFreezePalette$1(palette) {
|
|
|
2174
2174
|
Object.freeze(palette.styles);
|
|
2175
2175
|
return Object.freeze(palette);
|
|
2176
2176
|
}
|
|
2177
|
+
function registerByKey$1(palette) {
|
|
2178
|
+
return [palette.name.toLowerCase(), deepFreezePalette$1(palette)];
|
|
2179
|
+
}
|
|
2177
2180
|
const STYLE_PALETTES$1 = Object.assign(
|
|
2178
2181
|
/* @__PURE__ */ Object.create(null),
|
|
2179
|
-
|
|
2180
|
-
[
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
tolHighContrastPalette$1
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2182
|
+
Object.fromEntries(
|
|
2183
|
+
[
|
|
2184
|
+
defaultPalette$1,
|
|
2185
|
+
okabeItoPalette$1,
|
|
2186
|
+
tolBrightPalette$1,
|
|
2187
|
+
tolMutedPalette$1,
|
|
2188
|
+
tolHighContrastPalette$1,
|
|
2189
|
+
ibmPalette$1,
|
|
2190
|
+
grayscalePalette$1,
|
|
2191
|
+
categoricalPalette$1,
|
|
2192
|
+
grumpyNarwhalPalette$1
|
|
2193
|
+
].map(registerByKey$1)
|
|
2194
|
+
)
|
|
2192
2195
|
);
|
|
2193
2196
|
Object.freeze(STYLE_PALETTES$1);
|
|
2194
|
-
defaultPalette$1.name;
|
|
2197
|
+
defaultPalette$1.name.toLowerCase();
|
|
2195
2198
|
Object.freeze(
|
|
2196
2199
|
Object.keys(STYLE_PALETTES$1)
|
|
2197
2200
|
);
|
|
@@ -26957,7 +26960,7 @@ function requireCssesc$1() {
|
|
|
26957
26960
|
return cssesc_1$1;
|
|
26958
26961
|
}
|
|
26959
26962
|
requireCssesc$1();
|
|
26960
|
-
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 if (key === "reportScoreAndStateCallback" && typeof doenetWindowedViewer !== "undefined" && doenetWindowedViewer) {\n dispatcher = (report) => messageParentFromViewer({\n type: "reportScoreAndState",\n report\n });\n } else {\n dispatcher = (...callArgs) => currentFunctionProps[key]?.(...callArgs);\n }\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({
|
|
26963
|
+
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 if (key === "reportScoreAndStateCallback" && typeof doenetWindowedViewer !== "undefined" && doenetWindowedViewer) {\n dispatcher = (report) => messageParentFromViewer({\n type: "reportScoreAndState",\n report\n });\n } else {\n dispatcher = (...callArgs) => currentFunctionProps[key]?.(...callArgs);\n }\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({\n iframeReady: true,\n stylePalettes: readStylePalettesFromBundle()\n });\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 readStylePalettesFromBundle() {\n try {\n return window.getDoenetStylePalettes?.() ?? null;\n } catch (err) {\n console.error(\n "iframe DoenetML: failed to read style palettes from the standalone bundle",\n err\n );\n return null;\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';
|
|
26961
26964
|
const editorIframeJsSource = `(function() {
|
|
26962
26965
|
"use strict";
|
|
26963
26966
|
let editorControlHandle = null;
|
|
@@ -27126,7 +27129,10 @@ const editorIframeJsSource = `(function() {
|
|
|
27126
27129
|
}
|
|
27127
27130
|
(async () => {
|
|
27128
27131
|
if (await waitForStandaloneBundle(6e4)) {
|
|
27129
|
-
messageParentFromEditor({
|
|
27132
|
+
messageParentFromEditor({
|
|
27133
|
+
iframeReady: true,
|
|
27134
|
+
stylePalettes: readStylePalettesFromBundle()
|
|
27135
|
+
});
|
|
27130
27136
|
} else {
|
|
27131
27137
|
messageParentFromEditor({
|
|
27132
27138
|
error: "Invalid DoenetML version or DoenetML package not found"
|
|
@@ -27144,6 +27150,17 @@ const editorIframeJsSource = `(function() {
|
|
|
27144
27150
|
} catch {
|
|
27145
27151
|
}
|
|
27146
27152
|
});
|
|
27153
|
+
function readStylePalettesFromBundle() {
|
|
27154
|
+
try {
|
|
27155
|
+
return window.getDoenetStylePalettes?.() ?? null;
|
|
27156
|
+
} catch (err) {
|
|
27157
|
+
console.error(
|
|
27158
|
+
"iframe DoenetML: failed to read style palettes from the standalone bundle",
|
|
27159
|
+
err
|
|
27160
|
+
);
|
|
27161
|
+
return null;
|
|
27162
|
+
}
|
|
27163
|
+
}
|
|
27147
27164
|
function messageParentFromEditor(data) {
|
|
27148
27165
|
window.parent.postMessage(
|
|
27149
27166
|
{
|
|
@@ -40049,7 +40066,7 @@ const defaultPalette = {
|
|
|
40049
40066
|
}
|
|
40050
40067
|
};
|
|
40051
40068
|
const okabeItoPalette = {
|
|
40052
|
-
name: "
|
|
40069
|
+
name: "okabeIto",
|
|
40053
40070
|
description: "Colorblind-friendly blues, oranges, greens, and purples adapted from the Okabe-Ito palette, with varied marker shapes and line styles.",
|
|
40054
40071
|
styles: {
|
|
40055
40072
|
1: {
|
|
@@ -40220,7 +40237,7 @@ const okabeItoPalette = {
|
|
|
40220
40237
|
}
|
|
40221
40238
|
};
|
|
40222
40239
|
const tolBrightPalette = {
|
|
40223
|
-
name: "
|
|
40240
|
+
name: "tolBright",
|
|
40224
40241
|
description: "Colorblind-friendly bright blues, reds, greens, and purples from Paul Tol's bright palette, with varied marker shapes and line styles.",
|
|
40225
40242
|
styles: {
|
|
40226
40243
|
1: {
|
|
@@ -40354,7 +40371,7 @@ const tolBrightPalette = {
|
|
|
40354
40371
|
}
|
|
40355
40372
|
};
|
|
40356
40373
|
const tolMutedPalette = {
|
|
40357
|
-
name: "
|
|
40374
|
+
name: "tolMuted",
|
|
40358
40375
|
description: "Nine colorblind-friendly muted styles from Paul Tol's muted palette, with varied marker shapes and line styles.",
|
|
40359
40376
|
styles: {
|
|
40360
40377
|
1: {
|
|
@@ -40553,7 +40570,7 @@ const tolMutedPalette = {
|
|
|
40553
40570
|
}
|
|
40554
40571
|
};
|
|
40555
40572
|
const tolHighContrastPalette = {
|
|
40556
|
-
name: "
|
|
40573
|
+
name: "tolHighContrast",
|
|
40557
40574
|
description: "Four maximum-distinction styles from Paul Tol's high-contrast palette, distinguishable even in grayscale.",
|
|
40558
40575
|
styles: {
|
|
40559
40576
|
1: {
|
|
@@ -41063,7 +41080,7 @@ const categoricalPalette = {
|
|
|
41063
41080
|
}
|
|
41064
41081
|
};
|
|
41065
41082
|
const grumpyNarwhalPalette = {
|
|
41066
|
-
name: "
|
|
41083
|
+
name: "grumpyNarwhal",
|
|
41067
41084
|
description: "Six saturated hues — burnt orange, forest green, deep pink, arctic teal, gold, and purple — going neon in dark mode.",
|
|
41068
41085
|
styles: {
|
|
41069
41086
|
1: {
|
|
@@ -41219,24 +41236,27 @@ function deepFreezePalette(palette) {
|
|
|
41219
41236
|
Object.freeze(palette.styles);
|
|
41220
41237
|
return Object.freeze(palette);
|
|
41221
41238
|
}
|
|
41239
|
+
function registerByKey(palette) {
|
|
41240
|
+
return [palette.name.toLowerCase(), deepFreezePalette(palette)];
|
|
41241
|
+
}
|
|
41222
41242
|
const STYLE_PALETTES = Object.assign(
|
|
41223
41243
|
/* @__PURE__ */ Object.create(null),
|
|
41224
|
-
|
|
41225
|
-
[
|
|
41226
|
-
|
|
41227
|
-
|
|
41228
|
-
|
|
41229
|
-
|
|
41230
|
-
tolHighContrastPalette
|
|
41231
|
-
|
|
41232
|
-
|
|
41233
|
-
|
|
41234
|
-
|
|
41235
|
-
|
|
41236
|
-
|
|
41244
|
+
Object.fromEntries(
|
|
41245
|
+
[
|
|
41246
|
+
defaultPalette,
|
|
41247
|
+
okabeItoPalette,
|
|
41248
|
+
tolBrightPalette,
|
|
41249
|
+
tolMutedPalette,
|
|
41250
|
+
tolHighContrastPalette,
|
|
41251
|
+
ibmPalette,
|
|
41252
|
+
grayscalePalette,
|
|
41253
|
+
categoricalPalette,
|
|
41254
|
+
grumpyNarwhalPalette
|
|
41255
|
+
].map(registerByKey)
|
|
41256
|
+
)
|
|
41237
41257
|
);
|
|
41238
41258
|
Object.freeze(STYLE_PALETTES);
|
|
41239
|
-
defaultPalette.name;
|
|
41259
|
+
defaultPalette.name.toLowerCase();
|
|
41240
41260
|
Object.freeze(
|
|
41241
41261
|
Object.keys(STYLE_PALETTES)
|
|
41242
41262
|
);
|
|
@@ -66089,7 +66109,7 @@ function ExternalVirtualKeyboard({
|
|
|
66089
66109
|
}
|
|
66090
66110
|
);
|
|
66091
66111
|
}
|
|
66092
|
-
const version = "0.7.21-dev.
|
|
66112
|
+
const version = "0.7.21-dev.354";
|
|
66093
66113
|
const latestDoenetmlVersion = version;
|
|
66094
66114
|
function subscribeToPinnedTheme() {
|
|
66095
66115
|
return () => {
|
|
@@ -66113,6 +66133,7 @@ function DoenetViewer({
|
|
|
66113
66133
|
useSharedCoreWorker = false,
|
|
66114
66134
|
mountPolicy,
|
|
66115
66135
|
keepLive = false,
|
|
66136
|
+
onStylePalettes,
|
|
66116
66137
|
...doenetViewerProps
|
|
66117
66138
|
}) {
|
|
66118
66139
|
const [id2, _2] = React__default.useState(() => Math.random().toString(36).slice(2));
|
|
@@ -66123,6 +66144,8 @@ function DoenetViewer({
|
|
|
66123
66144
|
const lastSentFunctionPropsRef = React__default.useRef(null);
|
|
66124
66145
|
const doenetViewerPropsRef = React__default.useRef(doenetViewerProps);
|
|
66125
66146
|
doenetViewerPropsRef.current = doenetViewerProps;
|
|
66147
|
+
const onStylePalettesRef = React__default.useRef(onStylePalettes);
|
|
66148
|
+
onStylePalettesRef.current = onStylePalettes;
|
|
66126
66149
|
const doenetMLRef = React__default.useRef(doenetML);
|
|
66127
66150
|
doenetMLRef.current = doenetML;
|
|
66128
66151
|
const windowed = mountPolicy?.mode === "windowed";
|
|
@@ -66369,6 +66392,9 @@ function DoenetViewer({
|
|
|
66369
66392
|
}
|
|
66370
66393
|
return setInErrorState(data.error);
|
|
66371
66394
|
} else if (data.iframeReady) {
|
|
66395
|
+
onStylePalettesRef.current?.(
|
|
66396
|
+
data.stylePalettes ?? null
|
|
66397
|
+
);
|
|
66372
66398
|
if (ref.current) {
|
|
66373
66399
|
const viewerIframe = wrap$3(
|
|
66374
66400
|
windowEndpoint(ref.current.contentWindow)
|
|
@@ -66721,6 +66747,9 @@ function serializePropsSnapshot(seed, props) {
|
|
|
66721
66747
|
snapshot[key] = val;
|
|
66722
66748
|
}
|
|
66723
66749
|
}
|
|
66750
|
+
if (snapshot.styleOverrides === void 0) {
|
|
66751
|
+
snapshot.styleOverrides = null;
|
|
66752
|
+
}
|
|
66724
66753
|
return JSON.stringify(snapshot);
|
|
66725
66754
|
}
|
|
66726
66755
|
function logComlinkError(component, method) {
|
|
@@ -66736,6 +66765,7 @@ const DoenetEditor = React__default.forwardRef(function DoenetEditor2({
|
|
|
66736
66765
|
width = "100%",
|
|
66737
66766
|
height = "500px",
|
|
66738
66767
|
autodetectVersion = true,
|
|
66768
|
+
onStylePalettes,
|
|
66739
66769
|
...doenetEditorProps
|
|
66740
66770
|
}, forwardedRef) {
|
|
66741
66771
|
const [id2, _2] = React__default.useState(() => Math.random().toString(36).slice(2));
|
|
@@ -66746,6 +66776,8 @@ const DoenetEditor = React__default.forwardRef(function DoenetEditor2({
|
|
|
66746
66776
|
const lastSentFunctionPropsRef = React__default.useRef(null);
|
|
66747
66777
|
const doenetEditorPropsRef = React__default.useRef(doenetEditorProps);
|
|
66748
66778
|
doenetEditorPropsRef.current = doenetEditorProps;
|
|
66779
|
+
const onStylePalettesRef = React__default.useRef(onStylePalettes);
|
|
66780
|
+
onStylePalettesRef.current = onStylePalettes;
|
|
66749
66781
|
const [inErrorState, setInErrorState] = React__default.useState(null);
|
|
66750
66782
|
const [ignoreDetectedVersion, setIgnoreDetectedVersion] = React__default.useState(false);
|
|
66751
66783
|
const [initialDiagnostics, setInitialDiagnostics] = React__default.useState([]);
|
|
@@ -66852,6 +66884,9 @@ const DoenetEditor = React__default.forwardRef(function DoenetEditor2({
|
|
|
66852
66884
|
if (data.error) {
|
|
66853
66885
|
return setInErrorState(data.error);
|
|
66854
66886
|
} else if (data.iframeReady) {
|
|
66887
|
+
onStylePalettesRef.current?.(
|
|
66888
|
+
data.stylePalettes ?? null
|
|
66889
|
+
);
|
|
66855
66890
|
if (ref.current) {
|
|
66856
66891
|
const editorIframe = wrap$3(
|
|
66857
66892
|
windowEndpoint(ref.current.contentWindow)
|