@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/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useState, useRef, useMemo, useEffect, useReducer, useCallback, Component } from "react";
1
+ import React, { memo, useState, useMemo, useRef, useReducer, useCallback, useEffect, useSyncExternalStore, Component } from "react";
2
2
  import { DoenetViewer } from "@doenet/doenetml-iframe";
3
3
  function getDefaultExportFromCjs(x) {
4
4
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
@@ -1061,7 +1061,7 @@ function generateNewSingleDocAttempt({
1061
1061
  const numVariantsToExclude = numPrevVariants % numVariants;
1062
1062
  const numVariantOptions = numVariants - numVariantsToExclude;
1063
1063
  const variantsToExclude = previousVariants.slice(numPrevVariants - numVariantsToExclude, numPrevVariants).sort((a, b) => a - b);
1064
- const rngSeed = state.initialVariant.toString() + "|" + state.id.toString() + "|" + state.attemptNumber.toString() + "|" + parentAttempt.toString();
1064
+ const rngSeed = state.initialVariant.toString() + "|" + state.id + "|" + state.attemptNumber.toString() + "|" + parentAttempt.toString();
1065
1065
  const rng = rngClass$3(rngSeed);
1066
1066
  let selectedVariant = Math.floor(rng() * numVariantOptions) + 1;
1067
1067
  for (const excludedVariant of variantsToExclude) {
@@ -1130,7 +1130,7 @@ function initializeSelectState({
1130
1130
  numActivityVariants,
1131
1131
  restrictToVariantSlice
1132
1132
  }) {
1133
- const rngSeed = variant.toString() + "|" + source.id.toString();
1133
+ const rngSeed = variant.toString() + "|" + source.id;
1134
1134
  const rng = rngClass$2(rngSeed);
1135
1135
  const childStates = [];
1136
1136
  const extendedId = source.id + (restrictToVariantSlice === void 0 ? "" : "|" + restrictToVariantSlice.idx.toString());
@@ -1251,7 +1251,7 @@ function generateNewSelectAttempt({
1251
1251
  if (childOptionsLeft.length !== numLeftInGroup) {
1252
1252
  throw Error("we did something wrong");
1253
1253
  }
1254
- const rngSeed = state.initialVariant.toString() + "|" + state.id.toString() + "|" + state.attemptNumber.toString() + "|" + parentAttempt.toString();
1254
+ const rngSeed = state.initialVariant.toString() + "|" + state.id + "|" + state.attemptNumber.toString() + "|" + parentAttempt.toString();
1255
1255
  const rng = rngClass$2(rngSeed);
1256
1256
  const childrenChosen = [];
1257
1257
  for (let i = 0; i < Math.min(numToSelect, numLeftInGroup); i++) {
@@ -1341,7 +1341,7 @@ function generateNewSingleDocAttemptForMultiSelect({
1341
1341
  );
1342
1342
  const idxOfAllExcluded = [...otherSelectedIds, ...additionalExcludes].map((id) => childIdToIdx[id]).sort((a, b) => a - b);
1343
1343
  const slotNum = state.selectedChildren.map((a) => a.id).indexOf(childId);
1344
- const rngSeed = state.initialVariant.toString() + "|" + state.id.toString() + "|" + state.attemptNumber.toString() + "|" + parentAttempt.toString();
1344
+ const rngSeed = state.initialVariant.toString() + "|" + state.id + "|" + state.attemptNumber.toString() + "|" + parentAttempt.toString();
1345
1345
  const rng = rngClass$2(rngSeed);
1346
1346
  let selectedIdx = Math.floor(
1347
1347
  rng() * (numChildren - idxOfAllExcluded.length)
@@ -1487,7 +1487,7 @@ function initializeSequenceState({
1487
1487
  numActivityVariants,
1488
1488
  restrictToVariantSlice
1489
1489
  }) {
1490
- const rngSeed = variant.toString() + "|" + source.id.toString();
1490
+ const rngSeed = variant.toString() + "|" + source.id;
1491
1491
  const rng = rngClass$1(rngSeed);
1492
1492
  const extendedId = source.id + (restrictToVariantSlice === void 0 ? "" : "|" + restrictToVariantSlice.idx.toString());
1493
1493
  const childStates = source.items.map((activitySource) => {
@@ -1531,7 +1531,7 @@ function generateNewSequenceAttempt({
1531
1531
  ];
1532
1532
  }
1533
1533
  };
1534
- const rngSeed = state.initialVariant.toString() + "|" + state.id.toString() + "|" + state.attemptNumber.toString() + "|" + parentAttempt.toString();
1534
+ const rngSeed = state.initialVariant.toString() + "|" + state.id + "|" + state.attemptNumber.toString() + "|" + parentAttempt.toString();
1535
1535
  const rng = rngClass$1(rngSeed);
1536
1536
  let startInd = 0;
1537
1537
  while (startInd < state.allChildren.length) {
@@ -2465,6 +2465,20 @@ function isExportedActivityState(obj) {
2465
2465
  ) && typeof typedObj.sourceHash === "string"
2466
2466
  );
2467
2467
  }
2468
+ function collectDoenetmlVersions(source) {
2469
+ if (source.type === "singleDoc") {
2470
+ return [source.version];
2471
+ }
2472
+ const versions = [];
2473
+ for (const item of source.items) {
2474
+ for (const version of collectDoenetmlVersions(item)) {
2475
+ if (!versions.includes(version)) {
2476
+ versions.push(version);
2477
+ }
2478
+ }
2479
+ }
2480
+ return versions;
2481
+ }
2468
2482
  function initializeActivityState({
2469
2483
  source,
2470
2484
  variant,
@@ -2518,7 +2532,13 @@ function initializeActivityAndDoenetState({
2518
2532
  });
2519
2533
  const numItems = getNumItems(source);
2520
2534
  const itemAttemptNumbers = Array(numItems).fill(1);
2521
- return { activityState, doenetStates: [], itemAttemptNumbers };
2535
+ return {
2536
+ activityState,
2537
+ doenetStates: [],
2538
+ itemAttemptNumbers,
2539
+ stateVersion: 1,
2540
+ errMsg: null
2541
+ };
2522
2542
  }
2523
2543
  function generateNewActivityAttempt({
2524
2544
  state,
@@ -3015,143 +3035,31 @@ function IconBase(props) {
3015
3035
  function MdError(props) {
3016
3036
  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 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" }, "child": [] }] })(props);
3017
3037
  }
3018
- function SelectActivity({
3019
- flags,
3020
- baseId,
3021
- maxAttemptsAllowed,
3022
- forceDisable = false,
3023
- forceShowCorrectness = false,
3024
- forceShowSolution = false,
3025
- forceUnsuppressCheckwork = false,
3026
- doenetViewerUrl,
3027
- fetchExternalDoenetML,
3028
- darkMode = "light",
3029
- showAnswerResponseMenu = false,
3030
- answerResponseCountsByItem = [],
3038
+ const SelectActivity = memo(function SelectActivity2({
3031
3039
  state,
3032
- doenetStates,
3033
- loadedStateNum,
3034
- reportScoreAndStateCallback,
3035
- checkRender,
3036
- checkHidden,
3037
- allowItemAttemptButtons = false,
3038
- generateNewItemAttempt,
3039
- hasRenderedCallback,
3040
- reportVisibility = false,
3041
- reportVisibilityCallback,
3042
- itemAttemptNumbers,
3043
- itemSequence,
3044
- itemWord
3040
+ ...props
3045
3041
  }) {
3046
3042
  const selectedActivities = [];
3047
- const selectedIds = [];
3048
3043
  for (const activity of state.selectedChildren) {
3049
3044
  selectedActivities.push(
3050
- /* @__PURE__ */ jsxRuntimeExports.jsx(
3051
- Activity,
3052
- {
3053
- state: activity,
3054
- doenetStates,
3055
- loadedStateNum,
3056
- flags,
3057
- baseId,
3058
- maxAttemptsAllowed,
3059
- forceDisable,
3060
- forceShowCorrectness,
3061
- forceShowSolution,
3062
- forceUnsuppressCheckwork,
3063
- doenetViewerUrl,
3064
- fetchExternalDoenetML,
3065
- darkMode,
3066
- showAnswerResponseMenu,
3067
- answerResponseCountsByItem,
3068
- reportScoreAndStateCallback,
3069
- checkRender,
3070
- checkHidden,
3071
- allowItemAttemptButtons,
3072
- generateNewItemAttempt,
3073
- hasRenderedCallback,
3074
- reportVisibility,
3075
- reportVisibilityCallback,
3076
- itemAttemptNumbers,
3077
- itemSequence,
3078
- itemWord
3079
- },
3080
- activity.id
3081
- )
3045
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { ...props, state: activity }, activity.id)
3082
3046
  );
3083
- selectedIds.push(activity.id);
3084
3047
  }
3085
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { hidden: !checkRender(state), children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: selectedActivities }) }, state.attemptNumber);
3086
- }
3087
- function SequenceActivity({
3088
- flags,
3089
- baseId,
3090
- maxAttemptsAllowed,
3091
- forceDisable = false,
3092
- forceShowCorrectness = false,
3093
- forceShowSolution = false,
3094
- forceUnsuppressCheckwork = false,
3095
- doenetViewerUrl,
3096
- fetchExternalDoenetML,
3097
- darkMode = "light",
3098
- showAnswerResponseMenu = false,
3099
- answerResponseCountsByItem = [],
3048
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: selectedActivities }) }, state.attemptNumber);
3049
+ });
3050
+ const SequenceActivity = memo(function SequenceActivity2({
3100
3051
  state,
3101
- doenetStates,
3102
- loadedStateNum,
3103
- reportScoreAndStateCallback,
3104
- checkRender,
3105
- checkHidden,
3106
- allowItemAttemptButtons = false,
3107
- generateNewItemAttempt,
3108
- hasRenderedCallback,
3109
- reportVisibility = false,
3110
- reportVisibilityCallback,
3111
- itemAttemptNumbers,
3112
- itemSequence,
3113
- itemWord
3052
+ ...props
3114
3053
  }) {
3115
3054
  const activityList = [];
3116
3055
  for (const activity of state.orderedChildren) {
3117
3056
  activityList.push(
3118
- /* @__PURE__ */ jsxRuntimeExports.jsx(
3119
- Activity,
3120
- {
3121
- state: activity,
3122
- doenetStates,
3123
- loadedStateNum,
3124
- flags,
3125
- baseId,
3126
- maxAttemptsAllowed,
3127
- forceDisable,
3128
- forceShowCorrectness,
3129
- forceShowSolution,
3130
- forceUnsuppressCheckwork,
3131
- doenetViewerUrl,
3132
- fetchExternalDoenetML,
3133
- darkMode,
3134
- showAnswerResponseMenu,
3135
- answerResponseCountsByItem,
3136
- reportScoreAndStateCallback,
3137
- checkRender,
3138
- checkHidden,
3139
- allowItemAttemptButtons,
3140
- generateNewItemAttempt,
3141
- hasRenderedCallback,
3142
- reportVisibility,
3143
- reportVisibilityCallback,
3144
- itemAttemptNumbers,
3145
- itemSequence,
3146
- itemWord
3147
- },
3148
- activity.id
3149
- )
3057
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { ...props, state: activity }, activity.id)
3150
3058
  );
3151
3059
  }
3152
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { hidden: !checkRender(state), children: activityList }, state.attemptNumber);
3153
- }
3154
- function SingleDocActivity({
3060
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: activityList }, state.attemptNumber);
3061
+ });
3062
+ const SingleDocActivity = memo(function SingleDocActivity2({
3155
3063
  flags,
3156
3064
  baseId,
3157
3065
  maxAttemptsAllowed,
@@ -3160,80 +3068,66 @@ function SingleDocActivity({
3160
3068
  forceShowSolution = false,
3161
3069
  forceUnsuppressCheckwork = false,
3162
3070
  doenetViewerUrl,
3071
+ doenetImagesUrl,
3072
+ standaloneUrl,
3073
+ cssUrl,
3074
+ doenetmlVersion,
3163
3075
  fetchExternalDoenetML,
3164
3076
  darkMode = "light",
3165
3077
  showAnswerResponseMenu = false,
3166
- answerResponseCountsByItem = [],
3078
+ answerResponseCounts,
3167
3079
  state,
3168
- doenetStates,
3169
- loadedStateNum,
3080
+ doenetState,
3081
+ stateVersion,
3082
+ mountPolicy,
3083
+ useSharedCoreWorker = false,
3170
3084
  reportScoreAndStateCallback,
3171
- checkRender,
3172
3085
  checkHidden,
3086
+ checkKeepLive,
3173
3087
  allowItemAttemptButtons = false,
3174
3088
  generateNewItemAttempt,
3175
3089
  hasRenderedCallback,
3176
- reportVisibility = false,
3177
- reportVisibilityCallback,
3178
- itemAttemptNumbers,
3179
- itemSequence,
3090
+ itemAttemptNumber,
3180
3091
  itemWord
3181
3092
  }) {
3182
3093
  const [rendered, setRendered] = useState(false);
3183
3094
  const [attemptNumber, setAttemptNumber] = useState(state.attemptNumber);
3184
- const [loadedStateNumUsed, setLoadedStateNumUsed] = useState(0);
3095
+ const [stateVersionUsed, setStateVersionUsed] = useState(stateVersion);
3185
3096
  const [initialDoenetState, setInitialDoenetState] = useState(
3186
- state.doenetStateIdx === null ? null : doenetStates[state.doenetStateIdx] ?? null
3097
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3098
+ doenetState ?? null
3187
3099
  );
3188
3100
  const [requestedVariantIndex, setRequestedVariantIndex] = useState(
3189
3101
  state.currentVariant
3190
3102
  );
3191
- const ref = useRef(null);
3192
- const itemIdx = useMemo(
3193
- () => itemSequence.indexOf(state.id),
3194
- [itemSequence, state.id]
3195
- );
3196
- const itemAttemptNumber = itemAttemptNumbers[itemIdx];
3197
- useEffect(() => {
3198
- if (reportVisibility && ref.current) {
3199
- const observer = new IntersectionObserver(
3200
- ([entry]) => {
3201
- reportVisibilityCallback(state.id, entry.isIntersecting);
3202
- },
3203
- { rootMargin: "1000px 1000px 1000px 1000px" }
3204
- );
3205
- observer.observe(ref.current);
3206
- return () => {
3207
- observer.disconnect();
3208
- };
3209
- }
3210
- }, [reportVisibility, ref, reportVisibilityCallback, state.id]);
3211
- if (state.attemptNumber !== attemptNumber || loadedStateNumUsed !== loadedStateNum) {
3103
+ if (state.attemptNumber !== attemptNumber || stateVersionUsed !== stateVersion) {
3212
3104
  setAttemptNumber(state.attemptNumber);
3213
- setLoadedStateNumUsed(loadedStateNum);
3105
+ setStateVersionUsed(stateVersion);
3214
3106
  setInitialDoenetState(
3215
- state.doenetStateIdx === null ? null : doenetStates[state.doenetStateIdx] ?? null
3107
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3108
+ doenetState ?? null
3216
3109
  );
3217
3110
  setRequestedVariantIndex(state.currentVariant);
3218
3111
  }
3219
- const initialCounters = {
3220
- question: state.initialQuestionCounter,
3221
- problem: state.initialQuestionCounter,
3222
- exercise: state.initialQuestionCounter
3223
- };
3112
+ const initialCounters = useMemo(
3113
+ () => ({
3114
+ question: state.initialQuestionCounter,
3115
+ problem: state.initialQuestionCounter,
3116
+ exercise: state.initialQuestionCounter
3117
+ }),
3118
+ [state.initialQuestionCounter]
3119
+ );
3224
3120
  const source = state.source;
3225
3121
  const showAttemptButton = allowItemAttemptButtons && generateNewItemAttempt !== void 0 && !source.isDescription && maxAttemptsAllowed !== 1;
3226
- const render = checkRender(state);
3227
3122
  const hidden = checkHidden(state);
3228
3123
  const newAttemptsLeft = Math.max(maxAttemptsAllowed - itemAttemptNumber, 0);
3229
3124
  const attemptButtonDisabled = maxAttemptsAllowed > 0 && newAttemptsLeft <= 0;
3230
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { hidden: !render || hidden, style: { minHeight: "100px" }, children: [
3125
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { hidden, style: { minHeight: "100px" }, children: [
3231
3126
  /* @__PURE__ */ jsxRuntimeExports.jsx(
3232
3127
  DoenetViewer,
3233
3128
  {
3234
3129
  doenetML: source.doenetML,
3235
- doenetmlVersion: source.version,
3236
- render,
3130
+ doenetmlVersion: doenetmlVersion ?? source.version,
3237
3131
  requestedVariantIndex,
3238
3132
  flags,
3239
3133
  activityId: baseId,
@@ -3243,20 +3137,26 @@ function SingleDocActivity({
3243
3137
  forceShowSolution,
3244
3138
  forceUnsuppressCheckwork,
3245
3139
  doenetViewerUrl,
3140
+ doenetImagesUrl,
3141
+ standaloneUrl,
3142
+ cssUrl,
3246
3143
  fetchExternalDoenetML,
3247
3144
  darkMode,
3248
3145
  showAnswerResponseMenu,
3249
- answerResponseCounts: answerResponseCountsByItem[itemIdx],
3146
+ answerResponseCounts,
3250
3147
  addVirtualKeyboard: false,
3251
3148
  initialState: initialDoenetState,
3252
3149
  initializeCounters: initialCounters,
3150
+ mountPolicy,
3151
+ keepLive: checkKeepLive(state),
3152
+ useSharedCoreWorker,
3253
3153
  reportScoreAndStateCallback,
3254
3154
  initializedCallback: () => {
3255
3155
  setRendered(true);
3256
3156
  hasRenderedCallback(state.id);
3257
3157
  }
3258
3158
  },
3259
- state.attemptNumber
3159
+ `${attemptNumber.toString()}-${stateVersionUsed.toString()}`
3260
3160
  ),
3261
3161
  showAttemptButton ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
3262
3162
  "button",
@@ -3271,7 +3171,8 @@ function SingleDocActivity({
3271
3171
  disabled: attemptButtonDisabled,
3272
3172
  style: {
3273
3173
  marginLeft: "20px",
3274
- backgroundColor: "rgb(237, 242, 247)",
3174
+ backgroundColor: "var(--buttonSurfaceAlt)",
3175
+ color: "var(--canvasText)",
3275
3176
  opacity: attemptButtonDisabled ? 0.4 : "inherit",
3276
3177
  borderRadius: "10px",
3277
3178
  padding: "5px 20px",
@@ -3287,138 +3188,41 @@ function SingleDocActivity({
3287
3188
  ]
3288
3189
  }
3289
3190
  ) : null
3290
- ] }) });
3291
- }
3292
- function Activity({
3293
- flags,
3294
- baseId,
3295
- maxAttemptsAllowed,
3296
- forceDisable = false,
3297
- forceShowCorrectness = false,
3298
- forceShowSolution = false,
3299
- forceUnsuppressCheckwork = false,
3300
- doenetViewerUrl,
3301
- fetchExternalDoenetML,
3302
- darkMode = "light",
3303
- showAnswerResponseMenu = false,
3304
- answerResponseCountsByItem = [],
3191
+ ] });
3192
+ });
3193
+ const Activity = memo(function Activity2({
3305
3194
  state,
3306
- doenetStates,
3307
- loadedStateNum,
3308
- reportScoreAndStateCallback,
3309
- checkRender,
3310
- checkHidden,
3311
- allowItemAttemptButtons = false,
3312
- generateNewItemAttempt,
3313
- hasRenderedCallback,
3314
- reportVisibility = false,
3315
- reportVisibilityCallback,
3316
- itemAttemptNumbers,
3317
- itemSequence,
3318
- itemWord
3195
+ ...props
3319
3196
  }) {
3320
3197
  switch (state.type) {
3321
3198
  case "singleDoc": {
3199
+ const {
3200
+ doenetStates,
3201
+ itemAttemptNumbers,
3202
+ answerResponseCountsByItem = [],
3203
+ itemIndexById,
3204
+ ...leafProps
3205
+ } = props;
3206
+ const itemIdx = itemIndexById.get(state.id) ?? -1;
3322
3207
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
3323
3208
  SingleDocActivity,
3324
3209
  {
3325
- flags,
3326
- baseId,
3327
- maxAttemptsAllowed,
3328
- forceDisable,
3329
- forceShowCorrectness,
3330
- forceShowSolution,
3331
- forceUnsuppressCheckwork,
3332
- doenetViewerUrl,
3333
- fetchExternalDoenetML,
3334
- darkMode,
3335
- showAnswerResponseMenu,
3336
- answerResponseCountsByItem,
3210
+ ...leafProps,
3337
3211
  state,
3338
- doenetStates,
3339
- loadedStateNum,
3340
- reportScoreAndStateCallback,
3341
- checkRender,
3342
- checkHidden,
3343
- allowItemAttemptButtons,
3344
- generateNewItemAttempt,
3345
- hasRenderedCallback,
3346
- reportVisibility,
3347
- reportVisibilityCallback,
3348
- itemAttemptNumbers,
3349
- itemSequence,
3350
- itemWord
3212
+ doenetState: state.doenetStateIdx === null ? null : doenetStates[state.doenetStateIdx] ?? null,
3213
+ itemAttemptNumber: itemAttemptNumbers[itemIdx],
3214
+ answerResponseCounts: answerResponseCountsByItem[itemIdx]
3351
3215
  }
3352
3216
  );
3353
3217
  }
3354
3218
  case "select": {
3355
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
3356
- SelectActivity,
3357
- {
3358
- flags,
3359
- baseId,
3360
- maxAttemptsAllowed,
3361
- forceDisable,
3362
- forceShowCorrectness,
3363
- forceShowSolution,
3364
- forceUnsuppressCheckwork,
3365
- doenetViewerUrl,
3366
- fetchExternalDoenetML,
3367
- darkMode,
3368
- showAnswerResponseMenu,
3369
- answerResponseCountsByItem,
3370
- state,
3371
- doenetStates,
3372
- loadedStateNum,
3373
- reportScoreAndStateCallback,
3374
- checkRender,
3375
- checkHidden,
3376
- allowItemAttemptButtons,
3377
- generateNewItemAttempt,
3378
- hasRenderedCallback,
3379
- reportVisibility,
3380
- reportVisibilityCallback,
3381
- itemAttemptNumbers,
3382
- itemSequence,
3383
- itemWord
3384
- }
3385
- );
3219
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(SelectActivity, { ...props, state });
3386
3220
  }
3387
3221
  case "sequence": {
3388
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
3389
- SequenceActivity,
3390
- {
3391
- flags,
3392
- baseId,
3393
- maxAttemptsAllowed,
3394
- forceDisable,
3395
- forceShowCorrectness,
3396
- forceShowSolution,
3397
- forceUnsuppressCheckwork,
3398
- doenetViewerUrl,
3399
- fetchExternalDoenetML,
3400
- darkMode,
3401
- showAnswerResponseMenu,
3402
- answerResponseCountsByItem,
3403
- state,
3404
- doenetStates,
3405
- loadedStateNum,
3406
- reportScoreAndStateCallback,
3407
- checkRender,
3408
- checkHidden,
3409
- allowItemAttemptButtons,
3410
- generateNewItemAttempt,
3411
- hasRenderedCallback,
3412
- reportVisibility,
3413
- reportVisibilityCallback,
3414
- itemAttemptNumbers,
3415
- itemSequence,
3416
- itemWord
3417
- }
3418
- );
3222
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(SequenceActivity, { ...props, state });
3419
3223
  }
3420
3224
  }
3421
- }
3225
+ });
3422
3226
  function activityDoenetStateReducer(state, action) {
3423
3227
  const activityState = state.activityState;
3424
3228
  switch (action.type) {
@@ -3432,7 +3236,9 @@ function activityDoenetStateReducer(state, action) {
3432
3236
  numActivityVariants: action.numActivityVariants
3433
3237
  }),
3434
3238
  doenetStates: [],
3435
- itemAttemptNumbers: Array(numItems).fill(1)
3239
+ itemAttemptNumbers: Array(numItems).fill(1),
3240
+ stateVersion: state.stateVersion + 1,
3241
+ errMsg: null
3436
3242
  };
3437
3243
  }
3438
3244
  case "set": {
@@ -3449,15 +3255,25 @@ function activityDoenetStateReducer(state, action) {
3449
3255
  };
3450
3256
  window.postMessage(message);
3451
3257
  }
3452
- return action.state;
3258
+ return {
3259
+ ...action.state,
3260
+ stateVersion: state.stateVersion + 1,
3261
+ errMsg: null
3262
+ };
3453
3263
  }
3454
3264
  case "generateNewActivityAttempt": {
3455
- const { state: newActivityState } = generateNewActivityAttempt({
3456
- state: activityState,
3457
- numActivityVariants: action.numActivityVariants,
3458
- initialQuestionCounter: action.initialQuestionCounter,
3459
- parentAttempt: 1
3460
- });
3265
+ let newActivityState;
3266
+ try {
3267
+ ({ state: newActivityState } = generateNewActivityAttempt({
3268
+ state: activityState,
3269
+ numActivityVariants: action.numActivityVariants,
3270
+ initialQuestionCounter: action.initialQuestionCounter,
3271
+ parentAttempt: 1
3272
+ }));
3273
+ } catch (e) {
3274
+ const message = e instanceof Error ? e.message : "";
3275
+ return { ...state, errMsg: `Error in activity: ${message}` };
3276
+ }
3461
3277
  const newItemAttemptNumbers = state.itemAttemptNumbers.map(() => 1);
3462
3278
  if (action.allowSaveState && !action.initialAttempt) {
3463
3279
  const itemScores = extractActivityItemCredit(newActivityState);
@@ -3480,7 +3296,9 @@ function activityDoenetStateReducer(state, action) {
3480
3296
  return {
3481
3297
  activityState: newActivityState,
3482
3298
  doenetStates: [],
3483
- itemAttemptNumbers: newItemAttemptNumbers
3299
+ itemAttemptNumbers: newItemAttemptNumbers,
3300
+ stateVersion: state.stateVersion + 1,
3301
+ errMsg: null
3484
3302
  };
3485
3303
  }
3486
3304
  case "generateSingleDocSubActivityAttempt": {
@@ -3489,17 +3307,25 @@ function activityDoenetStateReducer(state, action) {
3489
3307
  "Should not call generateSingleDocSubActivityAttempt on entire activity"
3490
3308
  );
3491
3309
  }
3492
- const newActivityState = generateNewSingleDocSubAttempt({
3493
- singleDocId: action.docId,
3494
- state: activityState,
3495
- numActivityVariants: action.numActivityVariants,
3496
- initialQuestionCounter: action.initialQuestionCounter
3497
- });
3310
+ const doenetStateIdx = getItemSequence(activityState).indexOf(
3311
+ action.docId
3312
+ );
3313
+ let newActivityState;
3314
+ try {
3315
+ newActivityState = generateNewSingleDocSubAttempt({
3316
+ singleDocId: action.docId,
3317
+ state: activityState,
3318
+ numActivityVariants: action.numActivityVariants,
3319
+ initialQuestionCounter: action.initialQuestionCounter
3320
+ });
3321
+ } catch (e) {
3322
+ const message = e instanceof Error ? e.message : "";
3323
+ return { ...state, errMsg: `Error in activity: ${message}` };
3324
+ }
3498
3325
  const newDoenetMLStates = [...state.doenetStates];
3499
- newDoenetMLStates[action.doenetStateIdx] = null;
3500
- const itemIdx = action.itemSequence.indexOf(action.docId);
3326
+ newDoenetMLStates[doenetStateIdx] = null;
3501
3327
  const newItemAttemptNumbers = [...state.itemAttemptNumbers];
3502
- newItemAttemptNumbers[itemIdx]++;
3328
+ newItemAttemptNumbers[doenetStateIdx]++;
3503
3329
  if (action.allowSaveState) {
3504
3330
  const itemScores = extractActivityItemCredit(newActivityState);
3505
3331
  const itemScoresOld = extractActivityItemCredit(activityState);
@@ -3515,7 +3341,7 @@ function activityDoenetStateReducer(state, action) {
3515
3341
  },
3516
3342
  score: newActivityState.creditAchieved,
3517
3343
  item_scores: itemScores,
3518
- new_doenet_state_idx: action.doenetStateIdx,
3344
+ new_doenet_state_idx: doenetStateIdx,
3519
3345
  subject: "SPLICE.reportScoreAndState",
3520
3346
  activity_id: action.baseId,
3521
3347
  message_id: nanoid(),
@@ -3527,15 +3353,28 @@ function activityDoenetStateReducer(state, action) {
3527
3353
  return {
3528
3354
  activityState: newActivityState,
3529
3355
  doenetStates: newDoenetMLStates,
3530
- itemAttemptNumbers: newItemAttemptNumbers
3356
+ itemAttemptNumbers: newItemAttemptNumbers,
3357
+ // Not a stateVersion bump: the remount is driven by the
3358
+ // item's own attemptNumber.
3359
+ stateVersion: state.stateVersion,
3360
+ errMsg: null
3531
3361
  };
3532
3362
  }
3533
3363
  case "updateSingleState": {
3534
- const newActivityDoenetState = updateSingleDocState(action, state);
3364
+ const doenetStateIdx = getItemSequence(activityState).indexOf(
3365
+ action.docId
3366
+ );
3367
+ if (doenetStateIdx === -1) {
3368
+ return state;
3369
+ }
3370
+ const newActivityDoenetState = updateSingleDocState(
3371
+ action,
3372
+ doenetStateIdx,
3373
+ state
3374
+ );
3535
3375
  if (action.allowSaveState) {
3536
3376
  const newActivityState = newActivityDoenetState.activityState;
3537
3377
  const itemScores = extractActivityItemCredit(newActivityState);
3538
- const itemUpdated = action.itemSequence.indexOf(action.docId) + 1;
3539
3378
  const message = {
3540
3379
  state: {
3541
3380
  activityState: pruneActivityStateForSave(newActivityState),
@@ -3545,8 +3384,8 @@ function activityDoenetStateReducer(state, action) {
3545
3384
  },
3546
3385
  score: newActivityState.creditAchieved,
3547
3386
  item_scores: itemScores,
3548
- item_updated: itemUpdated,
3549
- new_doenet_state_idx: action.doenetStateIdx,
3387
+ item_updated: doenetStateIdx + 1,
3388
+ new_doenet_state_idx: doenetStateIdx,
3550
3389
  subject: "SPLICE.reportScoreAndState",
3551
3390
  activity_id: action.baseId,
3552
3391
  message_id: nanoid()
@@ -3558,7 +3397,7 @@ function activityDoenetStateReducer(state, action) {
3558
3397
  }
3559
3398
  throw Error("Invalid activity action");
3560
3399
  }
3561
- function updateSingleDocState(action, activityDoenetState) {
3400
+ function updateSingleDocState(action, doenetStateIdx, activityDoenetState) {
3562
3401
  const allStates = gatherStates(activityDoenetState.activityState);
3563
3402
  const newSingleDocState = allStates[action.docId] = {
3564
3403
  ...allStates[action.docId]
@@ -3570,8 +3409,8 @@ function updateSingleDocState(action, activityDoenetState) {
3570
3409
  }
3571
3410
  newSingleDocState.creditAchieved = action.creditAchieved;
3572
3411
  const doenetStates = [...activityDoenetState.doenetStates];
3573
- doenetStates[action.doenetStateIdx] = action.doenetState;
3574
- newSingleDocState.doenetStateIdx = action.doenetStateIdx;
3412
+ doenetStates[doenetStateIdx] = action.doenetState;
3413
+ newSingleDocState.doenetStateIdx = doenetStateIdx;
3575
3414
  const rootActivityState = propagateStateChangeToRoot({
3576
3415
  allStates,
3577
3416
  id: newSingleDocState.id
@@ -3579,9 +3418,19 @@ function updateSingleDocState(action, activityDoenetState) {
3579
3418
  return {
3580
3419
  activityState: rootActivityState,
3581
3420
  doenetStates,
3582
- itemAttemptNumbers: activityDoenetState.itemAttemptNumbers
3421
+ itemAttemptNumbers: activityDoenetState.itemAttemptNumbers,
3422
+ stateVersion: activityDoenetState.stateVersion,
3423
+ errMsg: null
3583
3424
  };
3584
3425
  }
3426
+ function useContentStable(value, key) {
3427
+ const [stable, setStable] = useState({ value, key });
3428
+ if (stable.key !== key) {
3429
+ setStable({ value, key });
3430
+ return value;
3431
+ }
3432
+ return stable.value;
3433
+ }
3585
3434
  function Viewer({
3586
3435
  source,
3587
3436
  flags,
@@ -3600,29 +3449,39 @@ function Viewer({
3600
3449
  addVirtualKeyboard: _addVirtualKeyboard = true,
3601
3450
  externalVirtualKeyboardProvided: _externalVirtualKeyboardProvided = false,
3602
3451
  doenetViewerUrl,
3452
+ doenetImagesUrl,
3453
+ standaloneUrl,
3454
+ cssUrl,
3455
+ doenetmlVersion,
3603
3456
  fetchExternalDoenetML,
3604
3457
  darkMode = "light",
3605
3458
  showAnswerResponseMenu = false,
3606
3459
  answerResponseCountsByItem = [],
3607
3460
  showTitle = true,
3608
- itemWord = "item"
3461
+ itemWord = "item",
3462
+ mountPolicy,
3463
+ useSharedCoreWorker = false
3609
3464
  }) {
3610
- const [errMsg, setErrMsg] = useState(null);
3611
3465
  const initialPass = useRef(true);
3612
- const { numActivityVariants, sourceHash, numItems } = useMemo(() => {
3466
+ const { numActivityVariants, sourceHash, numItems, sourceErrMsg } = useMemo(() => {
3613
3467
  try {
3614
3468
  validateIds(source);
3615
3469
  const docStructure = gatherDocumentStructure(source);
3616
3470
  const sourceHash2 = createSourceHash(source);
3617
3471
  const numItems2 = getNumItems(source);
3618
- return { ...docStructure, sourceHash: sourceHash2, numItems: numItems2 };
3472
+ return {
3473
+ ...docStructure,
3474
+ sourceHash: sourceHash2,
3475
+ numItems: numItems2,
3476
+ sourceErrMsg: null
3477
+ };
3619
3478
  } catch (e) {
3620
3479
  const message = e instanceof Error ? e.message : "";
3621
- setErrMsg(`Error in activity source: ${message}`);
3622
3480
  return {
3623
3481
  numActivityVariants: {},
3624
3482
  sourceHash: "",
3625
- numItems: 0
3483
+ numItems: 0,
3484
+ sourceErrMsg: `Error in activity source: ${message}`
3626
3485
  };
3627
3486
  }
3628
3487
  }, [source]);
@@ -3636,39 +3495,37 @@ function Viewer({
3636
3495
  },
3637
3496
  initializeActivityAndDoenetState
3638
3497
  );
3639
- const [loadedStateNum, setLoadedStateNum] = useState(0);
3640
3498
  const activityState = activityDoenetState.activityState;
3641
- const itemSequence = getItemSequence(activityState);
3499
+ const stateVersion = activityDoenetState.stateVersion;
3500
+ const runtimeErrMsg = activityDoenetState.errMsg;
3501
+ const computedItemSequence = useMemo(
3502
+ () => getItemSequence(activityState),
3503
+ [activityState]
3504
+ );
3505
+ const itemSequence = useContentStable(
3506
+ computedItemSequence,
3507
+ JSON.stringify(computedItemSequence)
3508
+ );
3509
+ const itemIndexById = useMemo(
3510
+ () => new Map(itemSequence.map((id, idx) => [id, idx])),
3511
+ [itemSequence]
3512
+ );
3642
3513
  const [currentItemIdx, setCurrentItemIdx] = useState(0);
3643
3514
  const currentItemId = itemSequence[currentItemIdx];
3644
3515
  const [itemsRendered, setItemsRendered] = useState([]);
3645
- const [itemsToRender, setItemsToRender] = useState([]);
3646
- const [itemsVisible, setItemsVisible] = useState([]);
3647
3516
  const [newAttemptNum, setNewAttemptNum] = useState(0);
3648
3517
  const dialogRef = useRef(null);
3649
3518
  const newItemAttemptInfo = useRef({ id: "", initialQuestionCounter: 0 });
3650
3519
  const attemptNumber = activityState.attemptNumber;
3651
- function addItemToRender(id) {
3652
- if (!itemsToRender.includes(id)) {
3653
- setItemsToRender((was) => {
3654
- if (was.includes(id)) {
3655
- return was;
3656
- }
3657
- const obj = [...was];
3658
- obj.push(id);
3659
- return obj;
3660
- });
3661
- }
3662
- }
3663
- const checkRender = useCallback(
3520
+ const checkKeepLive = useCallback(
3664
3521
  (state) => {
3665
- if (state.type === "singleDoc") {
3666
- return itemsToRender.includes(state.id);
3667
- } else {
3668
- return true;
3522
+ if (!paginate || state.type !== "singleDoc") {
3523
+ return false;
3669
3524
  }
3525
+ const itemIdx = itemIndexById.get(state.id);
3526
+ return itemIdx !== void 0 && Math.abs(itemIdx - currentItemIdx) <= 1;
3670
3527
  },
3671
- [itemsToRender]
3528
+ [paginate, itemIndexById, currentItemIdx]
3672
3529
  );
3673
3530
  const checkHidden = useCallback(
3674
3531
  (state) => {
@@ -3683,7 +3540,7 @@ function Viewer({
3683
3540
  useEffect(() => {
3684
3541
  if (initialPass.current) {
3685
3542
  initialPass.current = false;
3686
- } else {
3543
+ } else if (sourceErrMsg === null) {
3687
3544
  activityDoenetStateDispatch({
3688
3545
  type: "initialize",
3689
3546
  source,
@@ -3691,8 +3548,17 @@ function Viewer({
3691
3548
  numActivityVariants
3692
3549
  });
3693
3550
  }
3694
- }, [activityId, source, initialVariantIndex, numActivityVariants]);
3551
+ }, [
3552
+ activityId,
3553
+ source,
3554
+ initialVariantIndex,
3555
+ numActivityVariants,
3556
+ sourceErrMsg
3557
+ ]);
3695
3558
  useEffect(() => {
3559
+ if (sourceErrMsg !== null) {
3560
+ return;
3561
+ }
3696
3562
  const listenersAdded = [];
3697
3563
  function requestLoadState() {
3698
3564
  const message_id = nanoid();
@@ -3724,7 +3590,6 @@ function Viewer({
3724
3590
  allowSaveState: flags.allowSaveState,
3725
3591
  baseId: activityId
3726
3592
  });
3727
- setLoadedStateNum((was) => was + 1);
3728
3593
  }
3729
3594
  }
3730
3595
  window.removeEventListener("message", loadStateListener);
@@ -3733,21 +3598,15 @@ function Viewer({
3733
3598
  window.addEventListener("message", loadStateListener);
3734
3599
  listenersAdded.push(loadStateListener);
3735
3600
  }
3736
- try {
3737
- activityDoenetStateDispatch({
3738
- type: "generateNewActivityAttempt",
3739
- numActivityVariants,
3740
- initialQuestionCounter: 1,
3741
- allowSaveState: flags.allowSaveState,
3742
- baseId: activityId,
3743
- sourceHash,
3744
- initialAttempt: true
3745
- });
3746
- setLoadedStateNum((was) => was + 1);
3747
- } catch (e) {
3748
- const message = e instanceof Error ? e.message : "";
3749
- setErrMsg(`Error in activity: ${message}`);
3750
- }
3601
+ activityDoenetStateDispatch({
3602
+ type: "generateNewActivityAttempt",
3603
+ numActivityVariants,
3604
+ initialQuestionCounter: 1,
3605
+ allowSaveState: flags.allowSaveState,
3606
+ baseId: activityId,
3607
+ sourceHash,
3608
+ initialAttempt: true
3609
+ });
3751
3610
  if (flags.allowLoadState) {
3752
3611
  requestLoadState();
3753
3612
  }
@@ -3763,7 +3622,8 @@ function Viewer({
3763
3622
  userId,
3764
3623
  source,
3765
3624
  numActivityVariants,
3766
- sourceHash
3625
+ sourceHash,
3626
+ sourceErrMsg
3767
3627
  ]);
3768
3628
  function clickNext() {
3769
3629
  setCurrentItemIdx((was) => Math.min(numItems - 1, was + 1));
@@ -3771,33 +3631,35 @@ function Viewer({
3771
3631
  function clickPrevious() {
3772
3632
  setCurrentItemIdx((was) => Math.max(0, was - 1));
3773
3633
  }
3774
- function reportScoreAndStateCallback(msg) {
3775
- if (isSingleDocReportStateMessage(msg)) {
3776
- activityDoenetStateDispatch({
3777
- type: "updateSingleState",
3778
- docId: msg.docId,
3779
- doenetState: msg.state,
3780
- doenetStateIdx: itemSequence.indexOf(msg.docId),
3781
- itemSequence,
3782
- creditAchieved: msg.score,
3783
- allowSaveState: flags.allowSaveState,
3784
- baseId: activityId,
3785
- sourceHash
3786
- });
3787
- }
3788
- }
3789
- function generateNewItemAttemptPrompt(id, initialQuestionCounter) {
3790
- newItemAttemptInfo.current = { id, initialQuestionCounter };
3791
- setNewAttemptNum((itemSequence.indexOf(id) ?? 0) + 1);
3792
- dialogRef.current?.showModal();
3793
- }
3634
+ const reportScoreAndStateCallback = useCallback(
3635
+ (msg) => {
3636
+ if (isSingleDocReportStateMessage(msg)) {
3637
+ activityDoenetStateDispatch({
3638
+ type: "updateSingleState",
3639
+ docId: msg.docId,
3640
+ doenetState: msg.state,
3641
+ creditAchieved: msg.score,
3642
+ allowSaveState: flags.allowSaveState,
3643
+ baseId: activityId,
3644
+ sourceHash
3645
+ });
3646
+ }
3647
+ },
3648
+ [flags.allowSaveState, activityId, sourceHash]
3649
+ );
3650
+ const generateNewItemAttemptPrompt = useCallback(
3651
+ (id, initialQuestionCounter) => {
3652
+ newItemAttemptInfo.current = { id, initialQuestionCounter };
3653
+ setNewAttemptNum((itemIndexById.get(id) ?? 0) + 1);
3654
+ dialogRef.current?.showModal();
3655
+ },
3656
+ [itemIndexById]
3657
+ );
3794
3658
  function generateNewItemAttempt() {
3795
3659
  const { id, initialQuestionCounter } = newItemAttemptInfo.current;
3796
3660
  activityDoenetStateDispatch({
3797
3661
  type: "generateSingleDocSubActivityAttempt",
3798
3662
  docId: id,
3799
- doenetStateIdx: itemSequence.indexOf(id),
3800
- itemSequence,
3801
3663
  numActivityVariants,
3802
3664
  initialQuestionCounter,
3803
3665
  allowSaveState: flags.allowSaveState,
@@ -3814,52 +3676,12 @@ function Viewer({
3814
3676
  return arr;
3815
3677
  }
3816
3678
  });
3817
- setItemsToRender((was) => {
3818
- const idx = was.indexOf(id);
3819
- if (idx === -1) {
3820
- return was;
3821
- } else {
3822
- const arr = [...was];
3823
- arr.splice(idx, 1);
3824
- return arr;
3825
- }
3826
- });
3827
- }
3828
- function hasRenderedCallback(id) {
3829
- setItemsRendered((was) => {
3830
- if (was.includes(id)) {
3831
- return was;
3832
- }
3833
- const obj = [...was];
3834
- obj.push(id);
3835
- return obj;
3836
- });
3837
- }
3838
- function reportVisibilityCallback(id, isVisible) {
3839
- setItemsVisible((was) => {
3840
- if (isVisible) {
3841
- if (was.includes(id)) {
3842
- return was;
3843
- } else {
3844
- const obj = [...was];
3845
- obj.push(id);
3846
- return obj;
3847
- }
3848
- } else {
3849
- const idx = was.indexOf(id);
3850
- if (idx === -1) {
3851
- return was;
3852
- } else {
3853
- const obj = [...was];
3854
- obj.splice(idx, 1);
3855
- return obj;
3856
- }
3857
- }
3858
- });
3859
3679
  }
3680
+ const hasRenderedCallback = useCallback((id) => {
3681
+ setItemsRendered((was) => was.includes(id) ? was : [...was, id]);
3682
+ }, []);
3860
3683
  function generateActivityAttempt() {
3861
3684
  setItemsRendered([]);
3862
- setItemsToRender([]);
3863
3685
  setCurrentItemIdx(0);
3864
3686
  activityDoenetStateDispatch({
3865
3687
  type: "generateNewActivityAttempt",
@@ -3870,8 +3692,8 @@ function Viewer({
3870
3692
  sourceHash
3871
3693
  });
3872
3694
  }
3873
- if (errMsg !== null) {
3874
- const errorIcon = /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: "1em", color: "#C1292E" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(MdError, {}) });
3695
+ const errorIcon = /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: "1em", color: "#C1292E" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(MdError, {}) });
3696
+ if (sourceErrMsg !== null) {
3875
3697
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
3876
3698
  "div",
3877
3699
  {
@@ -3883,33 +3705,11 @@ function Viewer({
3883
3705
  children: [
3884
3706
  errorIcon,
3885
3707
  " ",
3886
- errMsg
3708
+ sourceErrMsg
3887
3709
  ]
3888
3710
  }
3889
3711
  );
3890
3712
  }
3891
- if (paginate) {
3892
- if (!itemsRendered.includes(currentItemId)) {
3893
- addItemToRender(currentItemId);
3894
- } else {
3895
- const nextItemId = itemSequence[currentItemIdx + 1];
3896
- if (currentItemIdx < numItems - 1 && !itemsRendered.includes(nextItemId)) {
3897
- addItemToRender(nextItemId);
3898
- } else {
3899
- const prevItemId = itemSequence[currentItemIdx - 1];
3900
- if (currentItemIdx > 0 && !itemsRendered.includes(prevItemId)) {
3901
- addItemToRender(prevItemId);
3902
- }
3903
- }
3904
- }
3905
- } else {
3906
- for (const id of itemSequence) {
3907
- if (!itemsRendered.includes(id) && itemsVisible.includes(id)) {
3908
- addItemToRender(id);
3909
- break;
3910
- }
3911
- }
3912
- }
3913
3713
  const activityAttemptsLeft = Math.max(
3914
3714
  maxAttemptsAllowed - attemptNumber,
3915
3715
  0
@@ -3950,7 +3750,8 @@ function Viewer({
3950
3750
  },
3951
3751
  style: {
3952
3752
  marginLeft: "30px",
3953
- backgroundColor: "lightgray",
3753
+ backgroundColor: "var(--buttonSurface)",
3754
+ color: "var(--canvasText)",
3954
3755
  borderRadius: "10px",
3955
3756
  padding: "5px 20px"
3956
3757
  },
@@ -3971,7 +3772,8 @@ function Viewer({
3971
3772
  },
3972
3773
  style: {
3973
3774
  marginLeft: "30px",
3974
- backgroundColor: "lightgray",
3775
+ backgroundColor: "var(--buttonSurface)",
3776
+ color: "var(--canvasText)",
3975
3777
  borderRadius: "10px",
3976
3778
  padding: "5px 20px"
3977
3779
  },
@@ -3991,7 +3793,8 @@ function Viewer({
3991
3793
  style: {
3992
3794
  marginLeft: "20px",
3993
3795
  marginRight: "10px",
3994
- backgroundColor: "lightgray",
3796
+ backgroundColor: "var(--buttonSurface)",
3797
+ color: "var(--canvasText)",
3995
3798
  borderRadius: "10px",
3996
3799
  padding: "5px 20px"
3997
3800
  },
@@ -4009,7 +3812,8 @@ function Viewer({
4009
3812
  onClick: clickNext,
4010
3813
  style: {
4011
3814
  marginLeft: "10px",
4012
- backgroundColor: "lightgray",
3815
+ backgroundColor: "var(--buttonSurface)",
3816
+ color: "var(--canvasText)",
4013
3817
  borderRadius: "10px",
4014
3818
  padding: "5px 20px"
4015
3819
  },
@@ -4028,7 +3832,8 @@ function Viewer({
4028
3832
  disabled: newAttemptDisabled,
4029
3833
  style: {
4030
3834
  marginLeft: "30px",
4031
- backgroundColor: "rgb(237, 242, 247)",
3835
+ backgroundColor: "var(--buttonSurfaceAlt)",
3836
+ color: "var(--canvasText)",
4032
3837
  opacity: newAttemptDisabled ? 0.4 : "inherit",
4033
3838
  borderRadius: "10px",
4034
3839
  padding: "5px 20px",
@@ -4042,6 +3847,22 @@ function Viewer({
4042
3847
  }
4043
3848
  ) : null
4044
3849
  ] }) }),
3850
+ runtimeErrMsg !== null ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
3851
+ "div",
3852
+ {
3853
+ style: {
3854
+ fontSize: "1.3em",
3855
+ marginLeft: "20px",
3856
+ marginTop: "20px"
3857
+ },
3858
+ "data-test": "Activity Error",
3859
+ children: [
3860
+ errorIcon,
3861
+ " ",
3862
+ runtimeErrMsg
3863
+ ]
3864
+ }
3865
+ ) : null,
4045
3866
  /* @__PURE__ */ jsxRuntimeExports.jsx(
4046
3867
  "div",
4047
3868
  {
@@ -4061,28 +3882,66 @@ function Viewer({
4061
3882
  forceShowSolution,
4062
3883
  forceUnsuppressCheckwork,
4063
3884
  doenetViewerUrl,
3885
+ doenetImagesUrl,
3886
+ standaloneUrl,
3887
+ cssUrl,
3888
+ doenetmlVersion,
4064
3889
  fetchExternalDoenetML,
4065
3890
  darkMode,
4066
3891
  showAnswerResponseMenu,
4067
3892
  answerResponseCountsByItem,
4068
3893
  state: activityState,
4069
3894
  doenetStates: activityDoenetState.doenetStates,
4070
- loadedStateNum,
3895
+ stateVersion,
3896
+ mountPolicy,
3897
+ useSharedCoreWorker,
4071
3898
  reportScoreAndStateCallback,
4072
- checkRender,
4073
3899
  checkHidden,
3900
+ checkKeepLive,
4074
3901
  allowItemAttemptButtons: itemLevelAttempts,
4075
3902
  generateNewItemAttempt: generateNewItemAttemptPrompt,
4076
3903
  hasRenderedCallback,
4077
- reportVisibility: !paginate,
4078
- reportVisibilityCallback,
4079
3904
  itemAttemptNumbers: activityDoenetState.itemAttemptNumbers,
4080
- itemSequence,
3905
+ itemIndexById,
4081
3906
  itemWord
4082
3907
  }
4083
3908
  )
4084
3909
  ] });
4085
3910
  }
3911
+ const DEFAULT_THEME = "light";
3912
+ const noop = () => {
3913
+ };
3914
+ function canMatchMedia() {
3915
+ return typeof window !== "undefined" && typeof window.matchMedia === "function";
3916
+ }
3917
+ function getSystemTheme() {
3918
+ if (!canMatchMedia()) {
3919
+ return DEFAULT_THEME;
3920
+ }
3921
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
3922
+ }
3923
+ function subscribeToSystemTheme(onChange) {
3924
+ if (!canMatchMedia()) {
3925
+ return noop;
3926
+ }
3927
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
3928
+ mq.addEventListener("change", onChange);
3929
+ return () => {
3930
+ mq.removeEventListener("change", onChange);
3931
+ };
3932
+ }
3933
+ function subscribeToPinnedTheme() {
3934
+ return noop;
3935
+ }
3936
+ function useResolvedTheme(setting) {
3937
+ const isSystem = setting === "system";
3938
+ const getPinnedSnapshot = () => setting;
3939
+ return useSyncExternalStore(
3940
+ isSystem ? subscribeToSystemTheme : subscribeToPinnedTheme,
3941
+ isSystem ? getSystemTheme : getPinnedSnapshot,
3942
+ isSystem ? () => DEFAULT_THEME : getPinnedSnapshot
3943
+ );
3944
+ }
4086
3945
  const defaultFlags = {
4087
3946
  showCorrectness: true,
4088
3947
  readOnly: false,
@@ -4097,9 +3956,10 @@ const defaultFlags = {
4097
3956
  autoSubmit: false
4098
3957
  };
4099
3958
  const rngClass = seedrandom.alea;
3959
+ const NO_FLAGS = {};
4100
3960
  function ActivityViewer({
4101
3961
  source,
4102
- flags: specifiedFlags = {},
3962
+ flags: specifiedFlags = NO_FLAGS,
4103
3963
  activityId = "a",
4104
3964
  userId = null,
4105
3965
  requestedVariantIndex,
@@ -4115,31 +3975,73 @@ function ActivityViewer({
4115
3975
  addVirtualKeyboard = true,
4116
3976
  externalVirtualKeyboardProvided = false,
4117
3977
  doenetViewerUrl,
3978
+ doenetImagesUrl,
3979
+ standaloneUrl,
3980
+ cssUrl,
3981
+ doenetmlVersion,
4118
3982
  fetchExternalDoenetML,
4119
- darkMode = "light",
3983
+ darkMode = "system",
4120
3984
  showAnswerResponseMenu = false,
4121
3985
  answerResponseCountsByItem = [],
4122
3986
  includeVariantSelector: _includeVariantSelector = false,
4123
3987
  showTitle = true,
4124
- itemWord = "item"
3988
+ itemWord = "item",
3989
+ reportWarningsCallback,
3990
+ mountPolicy,
3991
+ useSharedCoreWorker = false
4125
3992
  }) {
4126
3993
  const [initialVariantIndex, setInitialVariantIndex] = useState(null);
4127
- const thisPropSet = {
4128
- source: JSON.stringify(source),
4129
- activityId,
4130
- requestedVariantIndex
4131
- };
4132
- const lastPropSet = useRef({ source: "" });
4133
- const flags = { ...defaultFlags, ...specifiedFlags };
4134
- let foundPropChange = false;
4135
- let key;
4136
- for (key in thisPropSet) {
4137
- if (thisPropSet[key] !== lastPropSet.current[key]) {
4138
- foundPropChange = true;
3994
+ const resolvedTheme = useResolvedTheme(darkMode);
3995
+ const warnings = useMemo(() => {
3996
+ let versions;
3997
+ try {
3998
+ versions = collectDoenetmlVersions(source);
3999
+ } catch {
4000
+ return [];
4139
4001
  }
4140
- }
4141
- lastPropSet.current = thisPropSet;
4142
- if (foundPropChange) {
4002
+ if (versions.length > 1) {
4003
+ return [{ type: "mixedDoenetmlVersions", versions }];
4004
+ }
4005
+ return [];
4006
+ }, [source]);
4007
+ const reportWarningsCallbackRef = useRef(reportWarningsCallback);
4008
+ useEffect(() => {
4009
+ reportWarningsCallbackRef.current = reportWarningsCallback;
4010
+ });
4011
+ const lastReportedWarningsKey = useRef(null);
4012
+ useEffect(() => {
4013
+ const warningsKey = JSON.stringify(warnings);
4014
+ if (warningsKey === lastReportedWarningsKey.current) {
4015
+ return;
4016
+ }
4017
+ lastReportedWarningsKey.current = warningsKey;
4018
+ if (warnings.length === 0) {
4019
+ return;
4020
+ }
4021
+ for (const warning of warnings) {
4022
+ console.warn(
4023
+ `ActivityViewer: this assignment mixes DoenetML versions (${warning.versions.join(
4024
+ ", "
4025
+ )}). Each distinct version loads its own multi-MB standalone bundle; normalize the documents' \`version\` fields to avoid the multiplied download/parse cost (saved state survives, as its hash ignores \`version\`).`
4026
+ );
4027
+ }
4028
+ reportWarningsCallbackRef.current?.(warnings);
4029
+ }, [warnings]);
4030
+ const propSetKey = useMemo(
4031
+ () => JSON.stringify([source, activityId, requestedVariantIndex]),
4032
+ [source, activityId, requestedVariantIndex]
4033
+ );
4034
+ const [lastPropSetKey, setLastPropSetKey] = useState(null);
4035
+ const flags = useMemo(
4036
+ () => ({ ...defaultFlags, ...specifiedFlags }),
4037
+ [specifiedFlags]
4038
+ );
4039
+ const resolvedMountPolicy = useContentStable(
4040
+ useMemo(() => ({ mode: "windowed", ...mountPolicy }), [mountPolicy]),
4041
+ JSON.stringify(mountPolicy ?? {})
4042
+ );
4043
+ if (propSetKey !== lastPropSetKey) {
4044
+ setLastPropSetKey(propSetKey);
4143
4045
  if (requestedVariantIndex === void 0) {
4144
4046
  const rng = rngClass((/* @__PURE__ */ new Date()).toString());
4145
4047
  setInitialVariantIndex(Math.floor(rng() * 1e6) + 1);
@@ -4152,7 +4054,7 @@ function ActivityViewer({
4152
4054
  if (initialVariantIndex === null) {
4153
4055
  return null;
4154
4056
  }
4155
- return /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorBoundary, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4057
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorBoundary, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "assignment-viewer-root", "data-theme": resolvedTheme, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4156
4058
  Viewer,
4157
4059
  {
4158
4060
  source,
@@ -4172,14 +4074,20 @@ function ActivityViewer({
4172
4074
  addVirtualKeyboard,
4173
4075
  externalVirtualKeyboardProvided,
4174
4076
  doenetViewerUrl,
4077
+ doenetImagesUrl,
4078
+ standaloneUrl,
4079
+ cssUrl,
4080
+ doenetmlVersion,
4175
4081
  fetchExternalDoenetML,
4176
- darkMode,
4082
+ darkMode: resolvedTheme,
4177
4083
  showAnswerResponseMenu,
4178
4084
  answerResponseCountsByItem,
4179
4085
  showTitle,
4180
- itemWord
4086
+ itemWord,
4087
+ mountPolicy: resolvedMountPolicy,
4088
+ useSharedCoreWorker
4181
4089
  }
4182
- ) });
4090
+ ) }) });
4183
4091
  }
4184
4092
  class ErrorBoundary extends Component {
4185
4093
  constructor(props) {