@doenet/assignment-viewer 0.1.0-alpha-15 → 0.1.0-alpha-16
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 +67 -0
- package/dist/assignment-viewer.css +29 -0
- package/dist/index.d.ts +77 -2
- package/dist/index.js +397 -489
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -1,3 +1,70 @@
|
|
|
1
1
|
# Doenet assignment viewer
|
|
2
2
|
|
|
3
3
|
View assignments from questions written in DoenetML
|
|
4
|
+
|
|
5
|
+
## Mixed DoenetML versions
|
|
6
|
+
|
|
7
|
+
Each document in an assignment carries its own DoenetML `version`, and every
|
|
8
|
+
embedded viewer downloads and parses the multi-MB standalone bundle for its
|
|
9
|
+
document's version — so an assignment mixing versions multiplies that cost
|
|
10
|
+
by the number of distinct versions on the page.
|
|
11
|
+
|
|
12
|
+
The viewer does not normalize versions itself (that would change how the
|
|
13
|
+
older documents behave). Instead, when an assignment mixes versions, it
|
|
14
|
+
reports the condition to the containing page — a console warning alone would
|
|
15
|
+
be invisible to the site's visitors — via the `reportWarningsCallback` prop,
|
|
16
|
+
and the page decides how to display it:
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
<ActivityViewer
|
|
20
|
+
source={source}
|
|
21
|
+
reportWarningsCallback={(warnings) => {
|
|
22
|
+
for (const warning of warnings) {
|
|
23
|
+
if (warning.type === "mixedDoenetmlVersions") {
|
|
24
|
+
showBanner(
|
|
25
|
+
`This assignment mixes DoenetML versions (${warning.versions.join(", ")}), ` +
|
|
26
|
+
"which slows loading. Consider updating its documents to one version.",
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}}
|
|
31
|
+
/>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
To consolidate, normalize the documents' `version` fields in the assignment
|
|
35
|
+
source. Saved student state survives such a change: it is keyed on a source
|
|
36
|
+
hash that deliberately ignores `version`.
|
|
37
|
+
|
|
38
|
+
## Windowed mounting
|
|
39
|
+
|
|
40
|
+
Every item's viewer component is always mounted, but memory tracks what the
|
|
41
|
+
student can see — the pagination window or viewport — instead of the
|
|
42
|
+
assignment's length. The embedded viewers register with a page-wide
|
|
43
|
+
_windowed mounting_ policy (`mountPolicy` from `@doenet/doenetml-iframe`):
|
|
44
|
+
items only boot their iframe (a multi-MB bundle parse plus a core worker)
|
|
45
|
+
when near the viewport or within the pagination window, simultaneous boots
|
|
46
|
+
are capped, and at most `maxLiveViewers` stay live — the rest are **parked**:
|
|
47
|
+
their state is flushed, their iframe is replaced by a placeholder, and they
|
|
48
|
+
are restored (student's typed work intact) when the student returns to them.
|
|
49
|
+
In paginated mode the current page and its neighbors are kept live, so page
|
|
50
|
+
flips within the window stay instant.
|
|
51
|
+
|
|
52
|
+
This is the default. Tune it with the `mountPolicy` prop (overrides for
|
|
53
|
+
`maxLiveViewers`, `visibleMargin`, `parkDelayMs`, `flushTimeoutMs`,
|
|
54
|
+
`maxConcurrentBoots`):
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
<ActivityViewer
|
|
58
|
+
source={source}
|
|
59
|
+
flags={{ allowSaveState: true }}
|
|
60
|
+
mountPolicy={{ maxLiveViewers: 5 }}
|
|
61
|
+
/>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Parking requires a persistence path (`flags.allowSaveState` or
|
|
65
|
+
`flags.allowLocalState`) so no student work can be lost; without one,
|
|
66
|
+
viewers still mount lazily but stay live once booted.
|
|
67
|
+
|
|
68
|
+
To cut the per-document core cost further, `useSharedCoreWorker` serves all
|
|
69
|
+
documents' cores from a shared worker pool instead of one dedicated ~100 MB
|
|
70
|
+
worker per document (default off; see `@doenet/doenetml-iframe`).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
.assignment-viewer-root {
|
|
2
|
+
--canvas: white;
|
|
3
|
+
--canvasText: black;
|
|
4
|
+
--mainGray: #e3e3e3;
|
|
5
|
+
--mainBorder: 2px solid black;
|
|
6
|
+
--mainBorderRadius: 5px;
|
|
7
|
+
--buttonSurface: #e3e3e3;
|
|
8
|
+
--buttonSurfaceAlt: rgb(237, 242, 247);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.assignment-viewer-root[data-theme="dark"] {
|
|
12
|
+
--canvas: #121212;
|
|
13
|
+
--canvasText: white;
|
|
14
|
+
--mainGray: #a9a9a9;
|
|
15
|
+
--mainBorder: 2px solid white;
|
|
16
|
+
--buttonSurface: #3a3a3a;
|
|
17
|
+
--buttonSurfaceAlt: #2d3748;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.assignment-viewer-root {
|
|
21
|
+
background-color: var(--canvas);
|
|
22
|
+
color: var(--canvasText);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Give iframes the canvas background color so there's no white flash
|
|
26
|
+
while the standalone bundle loads its own dark-mode styles. */
|
|
27
|
+
.assignment-viewer-root iframe {
|
|
28
|
+
background-color: var(--canvas);
|
|
29
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { JSX } from 'react/jsx-runtime';
|
|
2
|
+
import { MountPolicy } from '@doenet/doenetml-iframe';
|
|
2
3
|
|
|
3
4
|
/** The source for creating an activity */
|
|
4
5
|
export declare type ActivitySource = SingleDocSource | SelectSource | SequenceSource;
|
|
5
6
|
|
|
6
|
-
export declare function ActivityViewer({ source, flags: specifiedFlags, activityId, userId, requestedVariantIndex, maxAttemptsAllowed, itemLevelAttempts, activityLevelAttempts, paginate, showFinishButton, forceDisable, forceShowCorrectness, forceShowSolution, forceUnsuppressCheckwork, addVirtualKeyboard, externalVirtualKeyboardProvided, doenetViewerUrl, fetchExternalDoenetML, darkMode, showAnswerResponseMenu, answerResponseCountsByItem, includeVariantSelector: _includeVariantSelector, showTitle, itemWord, }: {
|
|
7
|
+
export declare function ActivityViewer({ source, flags: specifiedFlags, activityId, userId, requestedVariantIndex, maxAttemptsAllowed, itemLevelAttempts, activityLevelAttempts, paginate, showFinishButton, forceDisable, forceShowCorrectness, forceShowSolution, forceUnsuppressCheckwork, addVirtualKeyboard, externalVirtualKeyboardProvided, doenetViewerUrl, doenetImagesUrl, standaloneUrl, cssUrl, doenetmlVersion, fetchExternalDoenetML, darkMode, showAnswerResponseMenu, answerResponseCountsByItem, includeVariantSelector: _includeVariantSelector, showTitle, itemWord, reportWarningsCallback, mountPolicy, useSharedCoreWorker, }: {
|
|
7
8
|
source: ActivitySource;
|
|
8
9
|
flags?: DoenetMLFlagsSubset;
|
|
9
10
|
activityId?: string;
|
|
@@ -20,16 +21,83 @@ export declare function ActivityViewer({ source, flags: specifiedFlags, activity
|
|
|
20
21
|
forceUnsuppressCheckwork?: boolean;
|
|
21
22
|
addVirtualKeyboard?: boolean;
|
|
22
23
|
externalVirtualKeyboardProvided?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* URL the `<ref>` renderer uses to build links to other Doenet
|
|
26
|
+
* activities. Forwarded to each document's viewer, which defaults it to
|
|
27
|
+
* `https://doenet.org/activityViewer`.
|
|
28
|
+
*/
|
|
23
29
|
doenetViewerUrl?: string;
|
|
30
|
+
/**
|
|
31
|
+
* URL used to resolve `<image source="doenet:…">` media references.
|
|
32
|
+
* Forwarded to each document's viewer, which defaults it to
|
|
33
|
+
* `https://doenet.org/api/media`.
|
|
34
|
+
*/
|
|
35
|
+
doenetImagesUrl?: string;
|
|
36
|
+
/**
|
|
37
|
+
* URL of a standalone DoenetML bundle to use for every document,
|
|
38
|
+
* instead of the CDN bundle for each document's `version`.
|
|
39
|
+
*/
|
|
40
|
+
standaloneUrl?: string;
|
|
41
|
+
/** URL of the CSS file that styles the standalone bundle. */
|
|
42
|
+
cssUrl?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Render every document with this DoenetML version, overriding each
|
|
45
|
+
* document's own `version`.
|
|
46
|
+
*/
|
|
47
|
+
doenetmlVersion?: string;
|
|
24
48
|
fetchExternalDoenetML?: (arg: string) => Promise<string>;
|
|
25
|
-
darkMode?:
|
|
49
|
+
darkMode?: ThemeSetting;
|
|
26
50
|
showAnswerResponseMenu?: boolean;
|
|
27
51
|
answerResponseCountsByItem?: Record<string, number>[];
|
|
28
52
|
includeVariantSelector?: boolean;
|
|
29
53
|
showTitle?: boolean;
|
|
30
54
|
itemWord?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Called with conditions in `source` worth surfacing to the user, e.g.
|
|
57
|
+
* an assignment mixing DoenetML versions. Invoked once per distinct set
|
|
58
|
+
* of warnings — not on every render, and not again when the consumer
|
|
59
|
+
* passes a fresh-but-equal `source` each render. The containing page
|
|
60
|
+
* decides how to display them; a `console.warn` is also emitted for
|
|
61
|
+
* developers.
|
|
62
|
+
*/
|
|
63
|
+
reportWarningsCallback?: (warnings: ActivityViewerWarning[]) => void;
|
|
64
|
+
/**
|
|
65
|
+
* Overrides for the windowed mounting policy every document's viewer
|
|
66
|
+
* registers with (see `MountPolicy` in `@doenet/doenetml-iframe`):
|
|
67
|
+
* at most `maxLiveViewers` viewers stay booted, the rest are parked
|
|
68
|
+
* losslessly as placeholders and restored near the viewport. Parking
|
|
69
|
+
* requires `flags.allowSaveState` or `flags.allowLocalState`; without
|
|
70
|
+
* them viewers still mount lazily but stay live once booted.
|
|
71
|
+
*/
|
|
72
|
+
mountPolicy?: Partial<Omit<MountPolicy, "mode">>;
|
|
73
|
+
/**
|
|
74
|
+
* Serve all documents' cores from a shared worker pool instead of one
|
|
75
|
+
* dedicated ~100 MB worker per document (see `useSharedCoreWorker` in
|
|
76
|
+
* `@doenet/doenetml-iframe`). Default off.
|
|
77
|
+
*/
|
|
78
|
+
useSharedCoreWorker?: boolean;
|
|
31
79
|
}): JSX.Element | null;
|
|
32
80
|
|
|
81
|
+
/**
|
|
82
|
+
* A condition in the provided activity worth surfacing to the user — passed
|
|
83
|
+
* to `reportWarningsCallback`, since a console warning is invisible to the
|
|
84
|
+
* site's visitors. How (and whether) to display it is the containing page's
|
|
85
|
+
* decision.
|
|
86
|
+
*/
|
|
87
|
+
export declare type ActivityViewerWarning = {
|
|
88
|
+
type: "mixedDoenetmlVersions";
|
|
89
|
+
/**
|
|
90
|
+
* The distinct DoenetML versions the assignment's documents request, in
|
|
91
|
+
* first-appearance order. Every embedded viewer downloads and parses
|
|
92
|
+
* the multi-MB standalone bundle for its document's version, so mixing
|
|
93
|
+
* versions multiplies that cost by the number of distinct versions.
|
|
94
|
+
* (Normalizing the `version` fields in the source avoids it; saved
|
|
95
|
+
* student state is keyed on a hash that ignores `version`, so it
|
|
96
|
+
* survives such a change.)
|
|
97
|
+
*/
|
|
98
|
+
versions: string[];
|
|
99
|
+
};
|
|
100
|
+
|
|
33
101
|
declare type DoenetMLFlags = {
|
|
34
102
|
showCorrectness: boolean;
|
|
35
103
|
readOnly: boolean;
|
|
@@ -100,4 +168,11 @@ declare type SingleDocSource = {
|
|
|
100
168
|
numVariants?: number;
|
|
101
169
|
};
|
|
102
170
|
|
|
171
|
+
/**
|
|
172
|
+
* `ThemeSetting` is the public, author-facing value of the `darkMode` prop:
|
|
173
|
+
* `"light"` and `"dark"` pin a specific theme, while `"system"` follows the
|
|
174
|
+
* OS/browser `prefers-color-scheme` preference and updates live.
|
|
175
|
+
*/
|
|
176
|
+
export declare type ThemeSetting = "dark" | "light" | "system";
|
|
177
|
+
|
|
103
178
|
export { }
|