@pie-players/pie-assessment-toolkit 0.3.74 → 1.0.0-beta.0
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 +658 -714
- package/custom-elements.json +1315 -0
- package/dist/attempt/TestSession.d.ts +1 -21
- package/dist/attempt/TestSession.js +16 -102
- package/dist/components/ItemScope.custom-element.d.ts +1 -0
- package/dist/components/ItemScope.custom-element.js +1 -0
- package/dist/components/ItemToolBar.custom-element.js +1 -1
- package/dist/components/PieAssessmentToolkit.custom-element.js +14 -14
- package/dist/components/SectionToolBar.custom-element.js +1 -1
- package/dist/components/chunks/ItemToolBar-3zrc4p3d.js +4 -0
- package/dist/components/chunks/ItemToolBar-66hcwmfw.js +2 -0
- package/dist/components/chunks/ItemToolBar-9gqhfxtn.js +40 -0
- package/dist/components/chunks/ItemToolBar-hzc5wyjp.js +2 -0
- package/dist/components/chunks/{ItemToolBar-gmrryv2v.js → ItemToolBar-kkj69ptr.js} +1 -1
- package/dist/components/chunks/ItemToolBar-vdkyn9f3.js +2 -0
- package/dist/components/chunks/sre-engine-bj997w4b.js +1 -0
- package/dist/components/item-scope-element.d.ts +2 -0
- package/dist/components/item-scope-element.js +2 -0
- package/dist/context/assessment-toolkit-context.d.ts +33 -6
- package/dist/index.d.ts +25 -50
- package/dist/index.js +15 -45
- package/dist/policy/core/PolicySource.d.ts +2 -2
- package/dist/policy/core/ToolPolicyEngine.d.ts +118 -34
- package/dist/policy/core/ToolPolicyEngine.js +203 -56
- package/dist/policy/core/compose-decision.d.ts +28 -7
- package/dist/policy/core/compose-decision.js +95 -74
- package/dist/policy/core/decision-types.d.ts +98 -55
- package/dist/policy/core/feature-decision.d.ts +28 -19
- package/dist/policy/core/feature-decision.js +15 -11
- package/dist/policy/core/pnp-policy-inputs.d.ts +29 -35
- package/dist/policy/core/pnp-policy-inputs.js +40 -88
- package/dist/policy/core/provenance.d.ts +10 -5
- package/dist/policy/core/provenance.js +1 -1
- package/dist/policy/core/tool-parameters.d.ts +11 -0
- package/dist/policy/core/tool-parameters.js +15 -0
- package/dist/policy/engine.d.ts +7 -23
- package/dist/policy/engine.js +5 -20
- package/dist/policy/sources/PnpPolicySource.d.ts +43 -68
- package/dist/policy/sources/PnpPolicySource.js +133 -132
- package/dist/runtime/SectionControllerBinding.d.ts +283 -0
- package/dist/runtime/SectionControllerBinding.js +456 -0
- package/dist/runtime/SectionRuntimeEngine.d.ts +26 -289
- package/dist/runtime/SectionRuntimeEngine.js +30 -372
- package/dist/runtime/adapter/SectionEngineAdapter.d.ts +19 -80
- package/dist/runtime/adapter/SectionEngineAdapter.js +19 -92
- package/dist/runtime/adapter/dom-event-bridge.d.ts +15 -30
- package/dist/runtime/adapter/dom-event-bridge.js +17 -44
- package/dist/runtime/adapter/subscriber-fanout.d.ts +1 -3
- package/dist/runtime/adapter/subscriber-fanout.js +2 -6
- package/dist/runtime/catalog-context.d.ts +19 -0
- package/dist/runtime/catalog-context.js +28 -0
- package/dist/runtime/composition-emit-scheduler.d.ts +12 -18
- package/dist/runtime/composition-emit-scheduler.js +10 -16
- package/dist/runtime/content-language.d.ts +32 -0
- package/dist/runtime/content-language.js +46 -0
- package/dist/runtime/content-region.d.ts +13 -0
- package/dist/runtime/content-region.js +29 -0
- package/dist/runtime/content-registration.d.ts +13 -0
- package/dist/runtime/content-registration.js +25 -0
- package/dist/runtime/core/SectionEngineCore.d.ts +2 -4
- package/dist/runtime/core/SectionEngineCore.js +2 -6
- package/dist/runtime/core/cohort.d.ts +1 -6
- package/dist/runtime/core/cohort.js +1 -10
- package/dist/runtime/core/engine-input.d.ts +18 -30
- package/dist/runtime/core/engine-input.js +11 -12
- package/dist/runtime/core/engine-output.d.ts +17 -40
- package/dist/runtime/core/engine-output.js +9 -21
- package/dist/runtime/core/engine-readiness.d.ts +5 -37
- package/dist/runtime/core/engine-readiness.js +8 -41
- package/dist/runtime/core/engine-resolver.d.ts +46 -74
- package/dist/runtime/core/engine-resolver.js +18 -43
- package/dist/runtime/core/engine-state.d.ts +28 -36
- package/dist/runtime/core/engine-state.js +18 -21
- package/dist/runtime/core/engine-transition.d.ts +14 -19
- package/dist/runtime/core/engine-transition.js +74 -53
- package/dist/runtime/engine.d.ts +16 -19
- package/dist/runtime/engine.js +12 -19
- package/dist/runtime/page-warnings.d.ts +13 -0
- package/dist/runtime/page-warnings.js +22 -0
- package/dist/runtime/policy-input-forwarding.d.ts +25 -0
- package/dist/runtime/policy-input-forwarding.js +25 -0
- package/dist/runtime/registration-events.d.ts +22 -14
- package/dist/runtime/runtime-event-claim.d.ts +8 -0
- package/dist/runtime/runtime-event-claim.js +15 -0
- package/dist/runtime/runtime-id.d.ts +1 -1
- package/dist/runtime/runtime-id.js +1 -1
- package/dist/runtime/shell-event-bridge.d.ts +45 -0
- package/dist/runtime/shell-event-bridge.js +127 -0
- package/dist/runtime/shell-registration.d.ts +73 -0
- package/dist/runtime/shell-registration.js +95 -0
- package/dist/runtime/shell-scope.d.ts +68 -0
- package/dist/runtime/shell-scope.js +194 -0
- package/dist/runtime/tool-host-contract.d.ts +8 -14
- package/dist/runtime/tool-host-contract.js +12 -12
- package/dist/runtime/unclaimed-registration-watch.d.ts +7 -0
- package/dist/runtime/unclaimed-registration-watch.js +23 -0
- package/dist/services/AccessibilityCatalogResolver.d.ts +21 -31
- package/dist/services/AccessibilityCatalogResolver.js +29 -42
- package/dist/services/ElementToolStateStore.d.ts +42 -67
- package/dist/services/ElementToolStateStore.js +65 -71
- package/dist/services/HighlightCoordinator.d.ts +28 -40
- package/dist/services/HighlightCoordinator.js +459 -381
- package/dist/services/RangeSerializer.d.ts +11 -33
- package/dist/services/RangeSerializer.js +88 -59
- package/dist/services/SSMLExtractor.d.ts +8 -31
- package/dist/services/SSMLExtractor.js +18 -40
- package/dist/services/TTSService.d.ts +159 -119
- package/dist/services/TTSService.js +937 -822
- package/dist/services/ToolCoordinator.d.ts +42 -102
- package/dist/services/ToolCoordinator.js +178 -256
- package/dist/services/ToolRegistry.d.ts +152 -133
- package/dist/services/ToolRegistry.js +192 -178
- package/dist/services/ToolkitCoordinator.d.ts +233 -237
- package/dist/services/ToolkitCoordinator.js +615 -557
- package/dist/services/audio-handoff.d.ts +6 -7
- package/dist/services/audio-handoff.js +20 -14
- package/dist/services/catalog-owner.d.ts +0 -1
- package/dist/services/framework-error-bus.d.ts +3 -6
- package/dist/services/framework-error-bus.js +3 -6
- package/dist/services/framework-error.d.ts +51 -13
- package/dist/services/framework-error.js +19 -20
- package/dist/services/interfaces.d.ts +189 -184
- package/dist/services/section-controller-types.d.ts +7 -7
- package/dist/services/section-session-assignment.d.ts +21 -0
- package/dist/services/section-session-assignment.js +80 -0
- package/dist/services/spoken-audio-cards.d.ts +2 -6
- package/dist/services/spoken-audio-cards.js +2 -6
- package/dist/services/tool-config-defaults.d.ts +3 -3
- package/dist/services/tool-config-defaults.js +3 -3
- package/dist/services/tool-config-validation.d.ts +9 -12
- package/dist/services/tool-config-validation.js +47 -87
- package/dist/services/tool-context.d.ts +7 -31
- package/dist/services/tool-context.js +99 -65
- package/dist/services/tool-failure.d.ts +30 -0
- package/dist/services/tool-failure.js +11 -0
- package/dist/services/tool-icons.d.ts +5 -5
- package/dist/services/tool-icons.js +6 -5
- package/dist/services/tool-instance-id.d.ts +8 -7
- package/dist/services/tool-instance-id.js +12 -24
- package/dist/services/tool-providers/TTSToolProvider.d.ts +20 -58
- package/dist/services/tool-providers/TTSToolProvider.js +32 -85
- package/dist/services/tool-providers/ToolProviderApi.d.ts +10 -11
- package/dist/services/tool-providers/ToolProviderRegistry.d.ts +29 -60
- package/dist/services/tool-providers/ToolProviderRegistry.js +128 -144
- package/dist/services/tool-request.d.ts +31 -12
- package/dist/services/tool-request.js +31 -16
- package/dist/services/toolbar-decided-tools.d.ts +16 -0
- package/dist/services/toolbar-decided-tools.js +29 -0
- package/dist/services/toolbar-items.js +2 -5
- package/dist/services/tools-config-normalizer.d.ts +33 -29
- package/dist/services/tools-config-normalizer.js +26 -28
- package/dist/services/tts/browser-provider.d.ts +39 -6
- package/dist/services/tts/browser-provider.js +280 -160
- package/dist/services/tts/catalog-span-alignment.js +14 -7
- package/dist/services/tts/flat-tree.d.ts +64 -0
- package/dist/services/tts/flat-tree.js +269 -0
- package/dist/services/tts/generated-speech/assemble-plan.d.ts +3 -1
- package/dist/services/tts/generated-speech/assemble-plan.js +3 -1
- package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +2 -0
- package/dist/services/tts/generated-speech/dom/build-dom-plan.js +1 -1
- package/dist/services/tts/generated-speech/dom/to-chunk-inputs.d.ts +2 -2
- package/dist/services/tts/generated-speech/index.d.ts +1 -1
- package/dist/services/tts/generated-speech/index.js +1 -1
- package/dist/services/tts/generated-speech/math-speech-cache.d.ts +3 -1
- package/dist/services/tts/generated-speech/math-speech-cache.js +5 -1
- package/dist/services/tts/generated-speech/types.d.ts +1 -1
- package/dist/services/tts/generated-speech/types.js +1 -1
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +22 -16
- package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +6 -4
- package/dist/services/tts/highlight-pipeline/readable-region.js +3 -2
- package/dist/services/tts/highlight-pipeline/types.d.ts +5 -0
- package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +12 -0
- package/dist/services/tts/highlight-pipeline/visible-map-range.js +73 -7
- package/dist/services/tts/highlight-target-resolver.d.ts +12 -0
- package/dist/services/tts/math-aware-text-processing.d.ts +5 -1
- package/dist/services/tts/math-aware-text-processing.js +42 -47
- package/dist/services/tts/math-control-names.d.ts +21 -0
- package/dist/services/tts/math-control-names.js +142 -0
- package/dist/services/tts/math-speech.d.ts +12 -1
- package/dist/services/tts/math-speech.js +54 -18
- package/dist/services/tts/mathml-sanitization.d.ts +7 -0
- package/dist/services/tts/mathml-sanitization.js +9 -1
- package/dist/services/tts/provider-options.d.ts +52 -0
- package/dist/services/tts/provider-options.js +1 -0
- package/dist/services/tts/ssml/spoken-text.d.ts +2 -26
- package/dist/services/tts/ssml/spoken-text.js +29 -0
- package/dist/services/tts/start-failure.d.ts +4 -0
- package/dist/services/tts/start-failure.js +19 -0
- package/dist/services/tts/text-processing.d.ts +9 -0
- package/dist/services/tts/text-processing.js +46 -33
- package/dist/services/tts/text-segmentation.d.ts +12 -0
- package/dist/services/tts/text-segmentation.js +60 -10
- package/dist/services/tts-runtime-config.d.ts +54 -26
- package/dist/services/tts-runtime-config.js +27 -112
- package/dist/tools/content-capability-resolution.d.ts +9 -11
- package/dist/tools/content-capability-resolution.js +8 -45
- package/dist/tools/registration.d.ts +59 -0
- package/dist/tools/registration.js +55 -0
- package/dist/tools/registrations/toolbar-registration-helpers.d.ts +6 -0
- package/dist/tools/registrations/toolbar-registration-helpers.js +8 -2
- package/dist/tools/tool-surface-host.d.ts +6 -10
- package/dist/tools/tool-surface-host.js +45 -50
- package/dist/tools/tool-tag-map.d.ts +5 -15
- package/dist/tools/tool-tag-map.js +6 -20
- package/dist/utils/structural-equality.d.ts +11 -0
- package/dist/utils/structural-equality.js +47 -0
- package/package.json +15 -29
- package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts +0 -49
- package/dist/attempt/adapters/activity-to-test-attempt-session.js +0 -138
- package/dist/components/chunks/ItemToolBar-b9dz5jvd.js +0 -40
- package/dist/components/chunks/ItemToolBar-h4xap0t0.js +0 -4
- package/dist/components/chunks/ItemToolBar-wr2myaah.js +0 -2
- package/dist/components/chunks/sre-engine-wf9s43gg.js +0 -1
- package/dist/context/runtime-context-consumer.d.ts +0 -13
- package/dist/context/runtime-context-consumer.js +0 -18
- package/dist/item-loader.d.ts +0 -44
- package/dist/item-loader.js +0 -61
- package/dist/policy/core/engine-context.d.ts +0 -29
- package/dist/policy/core/engine-context.js +0 -27
- package/dist/policy/core/policy-source-tag.d.ts +0 -31
- package/dist/policy/core/policy-source-tag.js +0 -28
- package/dist/policy/internal.d.ts +0 -25
- package/dist/policy/internal.js +0 -25
- package/dist/runtime/adapter/coordinator-bridge.d.ts +0 -92
- package/dist/runtime/adapter/coordinator-bridge.js +0 -126
- package/dist/runtime/adapter/framework-error-bridge.d.ts +0 -36
- package/dist/runtime/adapter/framework-error-bridge.js +0 -46
- package/dist/runtime/adapter/instrumentation-bridge.d.ts +0 -31
- package/dist/runtime/adapter/instrumentation-bridge.js +0 -38
- package/dist/runtime/core/engine-stage-derivation.d.ts +0 -18
- package/dist/runtime/core/engine-stage-derivation.js +0 -29
- package/dist/runtime/engine-context.d.ts +0 -39
- package/dist/runtime/engine-context.js +0 -31
- package/dist/runtime/internal.d.ts +0 -43
- package/dist/runtime/internal.js +0 -45
- package/dist/runtime/section-runtime-engine-host-context.d.ts +0 -74
- package/dist/runtime/section-runtime-engine-host-context.js +0 -57
- package/dist/runtime/stage-emit-gate.d.ts +0 -92
- package/dist/runtime/stage-emit-gate.js +0 -82
- package/dist/services/ContextVariableStore.d.ts +0 -113
- package/dist/services/ContextVariableStore.js +0 -206
- package/dist/services/I18nService.d.ts +0 -47
- package/dist/services/I18nService.js +0 -84
- package/dist/services/ThemeProvider.d.ts +0 -95
- package/dist/services/ThemeProvider.js +0 -334
- package/dist/services/defaultPersonalNeedsProfile.d.ts +0 -21
- package/dist/services/defaultPersonalNeedsProfile.js +0 -26
- package/dist/services/pnp-standard-features.d.ts +0 -238
- package/dist/services/pnp-standard-features.js +0 -338
- package/dist/services/tts-control-events.d.ts +0 -13
- package/dist/services/tts-control-events.js +0 -7
- package/dist/tools/client.d.ts +0 -10
- package/dist/tools/client.js +0 -10
- package/dist/tools/internal.d.ts +0 -44
- package/dist/tools/internal.js +0 -39
- package/dist/utils/logger.d.ts +0 -6
- package/dist/utils/logger.js +0 -10
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { commitPendingSessions, createPieLogger, normalizeItemSessionChange, } from "@pie-players/pie-players-shared";
|
|
2
|
+
import { PIE_INTERNAL_CONTENT_LOADED_EVENT, PIE_INTERNAL_ITEM_PLAYER_ERROR_EVENT, PIE_INTERNAL_ITEM_SESSION_CHANGED_EVENT, PIE_ITEM_SESSION_CHANGED_EVENT, } from "./registration-events.js";
|
|
3
|
+
import { dispatchCrossBoundaryEvent } from "./tool-host-contract.js";
|
|
4
|
+
/**
|
|
5
|
+
* Elements repeat themselves, a `complete` echo most often, so a session event
|
|
6
|
+
* is forwarded only when it differs from the last one, apart from when and
|
|
7
|
+
* through which runtime it was stamped.
|
|
8
|
+
*/
|
|
9
|
+
function sessionEventFingerprint(detail) {
|
|
10
|
+
const semanticDetail = detail && typeof detail === "object"
|
|
11
|
+
? { ...detail }
|
|
12
|
+
: detail;
|
|
13
|
+
if (semanticDetail && typeof semanticDetail === "object") {
|
|
14
|
+
delete semanticDetail.timestamp;
|
|
15
|
+
delete semanticDetail.sourceRuntimeId;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
return JSON.stringify(semanticDetail);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return String(semanticDetail);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const isCommit = (detail) => Boolean(detail &&
|
|
25
|
+
typeof detail === "object" &&
|
|
26
|
+
detail.sessionCommitReason);
|
|
27
|
+
/**
|
|
28
|
+
* A shell's translation of its player's events into the runtime's, shared by
|
|
29
|
+
* `<pie-item-scope>` and `<pie-passage-shell>`. The dedupe
|
|
30
|
+
* state lives as long as the bridge, so a prop change does not forget what was
|
|
31
|
+
* forwarded.
|
|
32
|
+
*/
|
|
33
|
+
export function createShellEventBridge(options) {
|
|
34
|
+
const { host, kind, identity, mode, send } = options;
|
|
35
|
+
const logger = createPieLogger(host.localName || "pie-shell", () => false);
|
|
36
|
+
let lastForwardedFingerprint = "";
|
|
37
|
+
const forwardSession = (detail) => {
|
|
38
|
+
const { itemId, canonicalItemId } = identity();
|
|
39
|
+
if (!itemId)
|
|
40
|
+
return;
|
|
41
|
+
const internal = {
|
|
42
|
+
itemId,
|
|
43
|
+
session: detail,
|
|
44
|
+
};
|
|
45
|
+
// The runtime keeps item state in step from every change; the public
|
|
46
|
+
// stream carries responses only.
|
|
47
|
+
send(PIE_INTERNAL_ITEM_SESSION_CHANGED_EVENT, internal);
|
|
48
|
+
const normalized = normalizeItemSessionChange({
|
|
49
|
+
itemId,
|
|
50
|
+
sessionDetail: detail,
|
|
51
|
+
});
|
|
52
|
+
if (normalized.intent === "metadata-only" || !normalized.session)
|
|
53
|
+
return;
|
|
54
|
+
const payload = {
|
|
55
|
+
itemId,
|
|
56
|
+
canonicalItemId: canonicalItemId || itemId,
|
|
57
|
+
session: normalized.session,
|
|
58
|
+
...(normalized.sessionCommitReason
|
|
59
|
+
? { sessionCommitReason: normalized.sessionCommitReason }
|
|
60
|
+
: {}),
|
|
61
|
+
};
|
|
62
|
+
dispatchCrossBoundaryEvent(host, PIE_ITEM_SESSION_CHANGED_EVENT, payload);
|
|
63
|
+
};
|
|
64
|
+
const onSessionChanged = (event) => {
|
|
65
|
+
if (mode() !== "section")
|
|
66
|
+
return;
|
|
67
|
+
event.stopPropagation();
|
|
68
|
+
// A passage carries no response: a passage id on the section's session
|
|
69
|
+
// stream names no item a host has.
|
|
70
|
+
if (kind !== "item")
|
|
71
|
+
return;
|
|
72
|
+
const detail = event.detail;
|
|
73
|
+
const fingerprint = sessionEventFingerprint(detail);
|
|
74
|
+
// A commit is the response's last chance to reach the section.
|
|
75
|
+
if (!isCommit(detail) && fingerprint === lastForwardedFingerprint)
|
|
76
|
+
return;
|
|
77
|
+
lastForwardedFingerprint = fingerprint;
|
|
78
|
+
forwardSession(detail);
|
|
79
|
+
};
|
|
80
|
+
const onLoadComplete = (event) => {
|
|
81
|
+
if (mode() === "section")
|
|
82
|
+
event.stopPropagation();
|
|
83
|
+
const { itemId, canonicalItemId, contentKind } = identity();
|
|
84
|
+
if (!itemId)
|
|
85
|
+
return;
|
|
86
|
+
const detail = {
|
|
87
|
+
itemId,
|
|
88
|
+
canonicalItemId: canonicalItemId || itemId,
|
|
89
|
+
contentKind,
|
|
90
|
+
detail: event.detail,
|
|
91
|
+
};
|
|
92
|
+
send(PIE_INTERNAL_CONTENT_LOADED_EVENT, detail);
|
|
93
|
+
};
|
|
94
|
+
const onPlayerError = (event) => {
|
|
95
|
+
if (mode() === "section")
|
|
96
|
+
event.stopPropagation();
|
|
97
|
+
const { itemId, canonicalItemId, contentKind } = identity();
|
|
98
|
+
if (!itemId)
|
|
99
|
+
return;
|
|
100
|
+
const detail = {
|
|
101
|
+
itemId,
|
|
102
|
+
canonicalItemId: canonicalItemId || itemId,
|
|
103
|
+
contentKind,
|
|
104
|
+
error: event.detail,
|
|
105
|
+
};
|
|
106
|
+
send(PIE_INTERNAL_ITEM_PLAYER_ERROR_EVENT, detail);
|
|
107
|
+
};
|
|
108
|
+
host.addEventListener("session-changed", onSessionChanged);
|
|
109
|
+
host.addEventListener("load-complete", onLoadComplete);
|
|
110
|
+
host.addEventListener("player-error", onPlayerError);
|
|
111
|
+
return {
|
|
112
|
+
disconnect: () => {
|
|
113
|
+
// An escape from the commit must not strand the listeners it needed, so
|
|
114
|
+
// the unbinding runs either way. A plain player commits its own.
|
|
115
|
+
try {
|
|
116
|
+
if (kind === "item" && mode() === "section") {
|
|
117
|
+
commitPendingSessions(host, { reason: "teardown", logger });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
host.removeEventListener("session-changed", onSessionChanged);
|
|
122
|
+
host.removeEventListener("load-complete", onLoadComplete);
|
|
123
|
+
host.removeEventListener("player-error", onPlayerError);
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { ItemSettings } from "@pie-players/pie-players-shared/types";
|
|
2
|
+
/**
|
|
3
|
+
* Registration dispatch for the shells, through their shell scope.
|
|
4
|
+
*
|
|
5
|
+
* Registration is a statement of fact to the runtime, and the runtime takes it
|
|
6
|
+
* literally: a `pie-register` makes the toolkit unregister and re-register the
|
|
7
|
+
* content's accessibility catalogs, re-run `sectionBinding.register`, and
|
|
8
|
+
* re-notify the section controller.
|
|
9
|
+
*
|
|
10
|
+
* A shell's effects re-run whenever its props are re-applied, which happens on
|
|
11
|
+
* every update of its parent card's template. Dispatching from such an effect
|
|
12
|
+
* (with `pie-unregister` in its cleanup) announces a teardown and rebuild of
|
|
13
|
+
* state that has not moved, leaves the content without catalogs between the
|
|
14
|
+
* two, and closes a feedback loop: a reader that re-renders on a catalog change
|
|
15
|
+
* re-applies the shell's props, which re-registers, which changes catalogs
|
|
16
|
+
* again, until Svelte abandons the update at its depth limit with the DOM
|
|
17
|
+
* half-applied — while the elements are all present, so assertions about the
|
|
18
|
+
* rendered output still pass.
|
|
19
|
+
*
|
|
20
|
+
* So the dispatcher keeps the last identity it announced and says nothing when
|
|
21
|
+
* the next one matches.
|
|
22
|
+
*/
|
|
23
|
+
export type ShellRegistrationIdentity = {
|
|
24
|
+
kind: "item" | "passage";
|
|
25
|
+
host: HTMLElement;
|
|
26
|
+
itemId: string;
|
|
27
|
+
canonicalItemId: string;
|
|
28
|
+
contentKind: string;
|
|
29
|
+
item: unknown;
|
|
30
|
+
/** The item's policy settings, compared structurally like its catalogs. */
|
|
31
|
+
settings: ItemSettings | null;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* The runtime a registration is addressed to: its id, and the element it
|
|
35
|
+
* listens on, which a shell whose host has left the document dispatches on.
|
|
36
|
+
*/
|
|
37
|
+
export type ShellRuntimeAddress = {
|
|
38
|
+
runtimeId: string;
|
|
39
|
+
eventTarget: EventTarget;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Where a shell's event to `runtime` goes: from the shell host while it is in
|
|
43
|
+
* the document, so the event bubbles through the tree like any other, and on
|
|
44
|
+
* the runtime's own element once it is not.
|
|
45
|
+
*/
|
|
46
|
+
export declare function shellEventTarget(host: HTMLElement, runtime: ShellRuntimeAddress | undefined): EventTarget;
|
|
47
|
+
export type ShellRegistrationDispatcher = {
|
|
48
|
+
/**
|
|
49
|
+
* Announce `identity` if it says anything new, and retire the live
|
|
50
|
+
* registration when there is nothing left to describe — a shell without a host
|
|
51
|
+
* or an id no longer stands for content, and leaving its registration behind
|
|
52
|
+
* would strand it in the runtime.
|
|
53
|
+
*
|
|
54
|
+
* Never dispatches `pie-unregister` before a re-register to the same
|
|
55
|
+
* runtime: both registration paths in the toolkit are keyed by element and
|
|
56
|
+
* replace what is there, so the unregister only ever created the gap. A
|
|
57
|
+
* registration moving to a different runtime is retired first, addressed
|
|
58
|
+
* to the runtime that holds it.
|
|
59
|
+
*/
|
|
60
|
+
sync: (identity: ShellRegistrationIdentity | null, runtime?: ShellRuntimeAddress) => void;
|
|
61
|
+
/**
|
|
62
|
+
* Retire the live registration, replaying the identity and runtime it was
|
|
63
|
+
* made under rather than whatever the props say now — by the time a
|
|
64
|
+
* registration is retired the props may already describe its replacement,
|
|
65
|
+
* and unregistering under the new identity would leave the old one live.
|
|
66
|
+
* A host already out of the document retires on the runtime's element.
|
|
67
|
+
*
|
|
68
|
+
* Belongs in a teardown that runs on teardown only. Attaching it to an effect
|
|
69
|
+
* that re-runs on prop changes is what made the churn.
|
|
70
|
+
*/
|
|
71
|
+
retire: () => void;
|
|
72
|
+
};
|
|
73
|
+
export declare function createShellRegistrationDispatcher(): ShellRegistrationDispatcher;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { catalogSourceSignature, } from "../services/catalog-owner.js";
|
|
2
|
+
import { structurallyEqual } from "../utils/structural-equality.js";
|
|
3
|
+
import { PIE_REGISTER_EVENT, PIE_UNREGISTER_EVENT, } from "./registration-events.js";
|
|
4
|
+
import { dispatchCrossBoundaryEvent } from "./tool-host-contract.js";
|
|
5
|
+
/**
|
|
6
|
+
* Everything a registration says except the item's catalogs.
|
|
7
|
+
*/
|
|
8
|
+
function sameOwner(a, b) {
|
|
9
|
+
return (a.kind === b.kind &&
|
|
10
|
+
a.host === b.host &&
|
|
11
|
+
a.itemId === b.itemId &&
|
|
12
|
+
a.canonicalItemId === b.canonicalItemId &&
|
|
13
|
+
a.contentKind === b.contentKind &&
|
|
14
|
+
structurallyEqual(a.settings, b.settings));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The item's part of a registration is its catalogs, which is all the runtime
|
|
18
|
+
* reads from it. Object identity is no guide: the toolkit and the layout kernel
|
|
19
|
+
* hold the composition in deep reactive state, so every republish, each answer
|
|
20
|
+
* included, hands a shell a new proxy of unchanged content.
|
|
21
|
+
*/
|
|
22
|
+
function catalogsOf(identity) {
|
|
23
|
+
return catalogSourceSignature(identity.item, identity.kind);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Where a shell's event to `runtime` goes: from the shell host while it is in
|
|
27
|
+
* the document, so the event bubbles through the tree like any other, and on
|
|
28
|
+
* the runtime's own element once it is not.
|
|
29
|
+
*/
|
|
30
|
+
export function shellEventTarget(host, runtime) {
|
|
31
|
+
return host.isConnected || !runtime ? host : runtime.eventTarget;
|
|
32
|
+
}
|
|
33
|
+
function dispatch(eventName, identity, runtime) {
|
|
34
|
+
const detail = {
|
|
35
|
+
kind: identity.kind,
|
|
36
|
+
itemId: identity.itemId,
|
|
37
|
+
canonicalItemId: identity.canonicalItemId,
|
|
38
|
+
contentKind: identity.contentKind,
|
|
39
|
+
item: identity.item,
|
|
40
|
+
...(identity.settings ? { settings: identity.settings } : {}),
|
|
41
|
+
element: identity.host,
|
|
42
|
+
...(runtime ? { runtimeId: runtime.runtimeId } : {}),
|
|
43
|
+
};
|
|
44
|
+
dispatchCrossBoundaryEvent(shellEventTarget(identity.host, runtime), eventName, detail);
|
|
45
|
+
}
|
|
46
|
+
export function createShellRegistrationDispatcher() {
|
|
47
|
+
/**
|
|
48
|
+
* What the runtime was last told, and the catalogs it was told about.
|
|
49
|
+
* Deliberately not reactive: it is read to decide whether to dispatch, and
|
|
50
|
+
* making it reactive would put that decision inside the graph it exists to
|
|
51
|
+
* keep quiet.
|
|
52
|
+
*/
|
|
53
|
+
let dispatched = null;
|
|
54
|
+
let dispatchedCatalogs = null;
|
|
55
|
+
let dispatchedRuntime;
|
|
56
|
+
function retire() {
|
|
57
|
+
const previous = dispatched;
|
|
58
|
+
if (!previous)
|
|
59
|
+
return;
|
|
60
|
+
const previousRuntime = dispatchedRuntime;
|
|
61
|
+
dispatched = null;
|
|
62
|
+
dispatchedCatalogs = null;
|
|
63
|
+
dispatchedRuntime = undefined;
|
|
64
|
+
dispatch(PIE_UNREGISTER_EVENT, previous, previousRuntime);
|
|
65
|
+
}
|
|
66
|
+
function sync(identity, runtime) {
|
|
67
|
+
if (!identity) {
|
|
68
|
+
retire();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (dispatched && dispatchedRuntime?.runtimeId !== runtime?.runtimeId) {
|
|
72
|
+
retire();
|
|
73
|
+
}
|
|
74
|
+
if (dispatched && sameOwner(dispatched, identity)) {
|
|
75
|
+
// A re-render re-applies the object last seen, so identity settles most
|
|
76
|
+
// runs without serializing anything.
|
|
77
|
+
if (dispatched.item === identity.item)
|
|
78
|
+
return;
|
|
79
|
+
const catalogs = catalogsOf(identity);
|
|
80
|
+
if (catalogs !== null && catalogs === dispatchedCatalogs) {
|
|
81
|
+
dispatched = identity;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
dispatched = identity;
|
|
85
|
+
dispatchedCatalogs = catalogs;
|
|
86
|
+
dispatch(PIE_REGISTER_EVENT, identity, runtime);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
dispatched = identity;
|
|
90
|
+
dispatchedCatalogs = catalogsOf(identity);
|
|
91
|
+
dispatchedRuntime = runtime;
|
|
92
|
+
dispatch(PIE_REGISTER_EVENT, identity, runtime);
|
|
93
|
+
}
|
|
94
|
+
return { sync, retire };
|
|
95
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type ShellContextKind } from "../context/assessment-toolkit-context.js";
|
|
2
|
+
import type { ItemSettings } from "@pie-players/pie-players-shared/types";
|
|
3
|
+
import type { TTSHighlightTargetResolver } from "../services/tts/highlight-target-resolver.js";
|
|
4
|
+
/** What a shell says about the content it holds. */
|
|
5
|
+
export interface ShellScopeState {
|
|
6
|
+
/** The shell element: it provides the contexts and dispatches the registration. */
|
|
7
|
+
host: HTMLElement;
|
|
8
|
+
kind: ShellContextKind;
|
|
9
|
+
itemId: string;
|
|
10
|
+
canonicalItemId?: string;
|
|
11
|
+
contentKind: string;
|
|
12
|
+
regionPolicy?: string;
|
|
13
|
+
/** The region the shell's tools act on; the shell itself when unset. */
|
|
14
|
+
scopeElement?: HTMLElement | null;
|
|
15
|
+
ttsHighlightTargetResolver?: TTSHighlightTargetResolver | null;
|
|
16
|
+
item?: unknown;
|
|
17
|
+
/** An item's policy settings, registered with the runtime alongside it. */
|
|
18
|
+
settings?: ItemSettings | null;
|
|
19
|
+
}
|
|
20
|
+
export interface ShellScope {
|
|
21
|
+
/**
|
|
22
|
+
* Publishes `state`, or withdraws the scope when there is none. A state that
|
|
23
|
+
* says nothing new publishes nothing.
|
|
24
|
+
*/
|
|
25
|
+
publish: (state: ShellScopeState | null) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Retires the runtime registration under the identity it was made with.
|
|
28
|
+
* Teardown only, after anything that still has to reach the runtime.
|
|
29
|
+
*/
|
|
30
|
+
retire: () => void;
|
|
31
|
+
/** Stops providing the contexts, and drops the events `send` holds. */
|
|
32
|
+
disconnect: () => void;
|
|
33
|
+
/**
|
|
34
|
+
* Dispatches `type` from the shell host with the id of the runtime that
|
|
35
|
+
* answered the scope in its detail, so that runtime claims it by id. Once
|
|
36
|
+
* the host has left the document, as in a teardown, the event is dispatched
|
|
37
|
+
* on the runtime's element instead, which the scope captured when the
|
|
38
|
+
* runtime answered. Events sent before a runtime has answered, or while the
|
|
39
|
+
* runtime's element is out of the document and the host is in it, are held,
|
|
40
|
+
* the newest {@link MAX_HELD_SHELL_EVENTS}, and delivered in order to the
|
|
41
|
+
* runtime that answers next. `disconnect` and `publish(null)` drop them.
|
|
42
|
+
*/
|
|
43
|
+
send: (type: string, detail: object) => void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A shell's publication of its content: the identity, as the shell context the
|
|
47
|
+
* tools inside it read, the region they act on, as the region scope, and the
|
|
48
|
+
* content itself, registered with the runtime, which files its accessibility
|
|
49
|
+
* catalogs under it. The section player's item and passage shells and
|
|
50
|
+
* `<pie-item-scope>` all publish through this, so a tool resolves its target
|
|
51
|
+
* and its catalogs the same way inside any of them.
|
|
52
|
+
*
|
|
53
|
+
* Content republished unchanged arrives as a new object, the composition being
|
|
54
|
+
* deep reactive state, so items compare by id and catalogs, the measure the
|
|
55
|
+
* registration applies.
|
|
56
|
+
*
|
|
57
|
+
* The registration waits for the runtime the shell sits in to answer through
|
|
58
|
+
* `assessmentToolkitHostRuntimeContext`, and carries that runtime's id, which
|
|
59
|
+
* the runtime claims it by. A shell mounted before its toolkit, which a host
|
|
60
|
+
* composing the elements itself can do, would otherwise register with no one
|
|
61
|
+
* listening, and nothing replays a registration. The document's context root
|
|
62
|
+
* replays the request instead, once the toolkit's provider connects. A
|
|
63
|
+
* different runtime answering later, a nearer toolkit taking over, moves the
|
|
64
|
+
* registration: the old runtime is told to unregister by its id, and the new
|
|
65
|
+
* one gets the registration and the content's last load, which only the old
|
|
66
|
+
* one heard.
|
|
67
|
+
*/
|
|
68
|
+
export declare function createShellScope(): ShellScope;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { ContextProvider, } from "@pie-players/pie-context";
|
|
2
|
+
import { assessmentToolkitRegionScopeContext, assessmentToolkitShellContext, } from "../context/assessment-toolkit-context.js";
|
|
3
|
+
import { catalogSourceSignature, } from "../services/catalog-owner.js";
|
|
4
|
+
import { PIE_INTERNAL_CONTENT_LOADED_EVENT } from "./registration-events.js";
|
|
5
|
+
import { createShellRegistrationDispatcher, shellEventTarget, } from "./shell-registration.js";
|
|
6
|
+
import { connectHostRuntimeContext, dispatchCrossBoundaryEvent, } from "./tool-host-contract.js";
|
|
7
|
+
/**
|
|
8
|
+
* The newest events kept until a runtime answers; an older one is dropped. A
|
|
9
|
+
* scope in plain mode sends only load and error events, a section shell its
|
|
10
|
+
* session events too.
|
|
11
|
+
*/
|
|
12
|
+
const MAX_HELD_SHELL_EVENTS = 50;
|
|
13
|
+
function provide(host, context, value) {
|
|
14
|
+
const provider = new ContextProvider(host, { context, initialValue: value });
|
|
15
|
+
provider.connect();
|
|
16
|
+
return { provider, value };
|
|
17
|
+
}
|
|
18
|
+
function republish(provided, value, same) {
|
|
19
|
+
if (same(provided.value, value))
|
|
20
|
+
return;
|
|
21
|
+
provided.value = value;
|
|
22
|
+
provided.provider.setValue(value);
|
|
23
|
+
}
|
|
24
|
+
const sameItem = (a, b, kind) => {
|
|
25
|
+
if (a === b)
|
|
26
|
+
return true;
|
|
27
|
+
if (!a || !b)
|
|
28
|
+
return false;
|
|
29
|
+
if (a.id !== b.id)
|
|
30
|
+
return false;
|
|
31
|
+
const catalogs = catalogSourceSignature(a, kind);
|
|
32
|
+
return (catalogs !== null &&
|
|
33
|
+
catalogs === catalogSourceSignature(b, kind));
|
|
34
|
+
};
|
|
35
|
+
const sameShellContext = (a, b) => a.kind === b.kind &&
|
|
36
|
+
a.itemId === b.itemId &&
|
|
37
|
+
a.canonicalItemId === b.canonicalItemId &&
|
|
38
|
+
a.contentKind === b.contentKind &&
|
|
39
|
+
a.regionPolicy === b.regionPolicy &&
|
|
40
|
+
a.scopeElement === b.scopeElement &&
|
|
41
|
+
sameItem(a.item, b.item, a.kind);
|
|
42
|
+
const sameRegionScope = (a, b) => a.scopeElement === b.scopeElement &&
|
|
43
|
+
a.ttsHighlightTargetResolver === b.ttsHighlightTargetResolver;
|
|
44
|
+
const setAttributeIfChanged = (host, name, value) => {
|
|
45
|
+
if (host.getAttribute(name) !== value)
|
|
46
|
+
host.setAttribute(name, value);
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* A shell's publication of its content: the identity, as the shell context the
|
|
50
|
+
* tools inside it read, the region they act on, as the region scope, and the
|
|
51
|
+
* content itself, registered with the runtime, which files its accessibility
|
|
52
|
+
* catalogs under it. The section player's item and passage shells and
|
|
53
|
+
* `<pie-item-scope>` all publish through this, so a tool resolves its target
|
|
54
|
+
* and its catalogs the same way inside any of them.
|
|
55
|
+
*
|
|
56
|
+
* Content republished unchanged arrives as a new object, the composition being
|
|
57
|
+
* deep reactive state, so items compare by id and catalogs, the measure the
|
|
58
|
+
* registration applies.
|
|
59
|
+
*
|
|
60
|
+
* The registration waits for the runtime the shell sits in to answer through
|
|
61
|
+
* `assessmentToolkitHostRuntimeContext`, and carries that runtime's id, which
|
|
62
|
+
* the runtime claims it by. A shell mounted before its toolkit, which a host
|
|
63
|
+
* composing the elements itself can do, would otherwise register with no one
|
|
64
|
+
* listening, and nothing replays a registration. The document's context root
|
|
65
|
+
* replays the request instead, once the toolkit's provider connects. A
|
|
66
|
+
* different runtime answering later, a nearer toolkit taking over, moves the
|
|
67
|
+
* registration: the old runtime is told to unregister by its id, and the new
|
|
68
|
+
* one gets the registration and the content's last load, which only the old
|
|
69
|
+
* one heard.
|
|
70
|
+
*/
|
|
71
|
+
export function createShellScope() {
|
|
72
|
+
const registration = createShellRegistrationDispatcher();
|
|
73
|
+
const contextVersion = Date.now();
|
|
74
|
+
let host = null;
|
|
75
|
+
let shell = null;
|
|
76
|
+
let region = null;
|
|
77
|
+
let stopFindingRuntime = null;
|
|
78
|
+
let runtime = null;
|
|
79
|
+
let identity = null;
|
|
80
|
+
let held = [];
|
|
81
|
+
let loaded = null;
|
|
82
|
+
const syncRegistration = () => {
|
|
83
|
+
if (runtime !== null)
|
|
84
|
+
registration.sync(identity, runtime);
|
|
85
|
+
};
|
|
86
|
+
// A runtime element that left the document while the shell stays in it no
|
|
87
|
+
// longer sits above the shell, so nothing would claim the shell's events.
|
|
88
|
+
const runtimeDeparted = (shellHost, address) => shellHost.isConnected &&
|
|
89
|
+
address.eventTarget.isConnected === false;
|
|
90
|
+
function send(type, detail) {
|
|
91
|
+
if (type === PIE_INTERNAL_CONTENT_LOADED_EVENT)
|
|
92
|
+
loaded = detail;
|
|
93
|
+
if (host && runtime !== null && !runtimeDeparted(host, runtime)) {
|
|
94
|
+
dispatchCrossBoundaryEvent(shellEventTarget(host, runtime), type, {
|
|
95
|
+
...detail,
|
|
96
|
+
runtimeId: runtime.runtimeId,
|
|
97
|
+
});
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
held.push({ type, detail });
|
|
101
|
+
if (held.length > MAX_HELD_SHELL_EVENTS)
|
|
102
|
+
held.shift();
|
|
103
|
+
}
|
|
104
|
+
// The subscription re-answers whenever the runtime republishes its context:
|
|
105
|
+
// under the same id it only delivers what the scope held meanwhile.
|
|
106
|
+
function onRuntime(value) {
|
|
107
|
+
const moved = runtime !== null && value.runtimeId !== runtime.runtimeId;
|
|
108
|
+
if (value.runtimeId !== runtime?.runtimeId) {
|
|
109
|
+
runtime = { runtimeId: value.runtimeId, eventTarget: value.eventTarget };
|
|
110
|
+
syncRegistration();
|
|
111
|
+
}
|
|
112
|
+
const pending = held;
|
|
113
|
+
held = [];
|
|
114
|
+
for (const event of pending)
|
|
115
|
+
send(event.type, event.detail);
|
|
116
|
+
const replayed = pending.some((event) => event.type === PIE_INTERNAL_CONTENT_LOADED_EVENT);
|
|
117
|
+
if (moved && loaded && !replayed) {
|
|
118
|
+
send(PIE_INTERNAL_CONTENT_LOADED_EVENT, loaded);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function disconnect() {
|
|
122
|
+
for (const provided of [shell, region])
|
|
123
|
+
provided?.provider.disconnect();
|
|
124
|
+
stopFindingRuntime?.();
|
|
125
|
+
stopFindingRuntime = null;
|
|
126
|
+
runtime = null;
|
|
127
|
+
held = [];
|
|
128
|
+
loaded = null;
|
|
129
|
+
identity = null;
|
|
130
|
+
shell = null;
|
|
131
|
+
region = null;
|
|
132
|
+
host = null;
|
|
133
|
+
}
|
|
134
|
+
function publish(state) {
|
|
135
|
+
if (!state) {
|
|
136
|
+
registration.sync(null);
|
|
137
|
+
disconnect();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (state.host !== host)
|
|
141
|
+
disconnect();
|
|
142
|
+
host = state.host;
|
|
143
|
+
const canonicalItemId = state.canonicalItemId || state.itemId;
|
|
144
|
+
const regionPolicy = state.regionPolicy || "default";
|
|
145
|
+
const scopeElement = state.scopeElement || state.host;
|
|
146
|
+
const shellValue = {
|
|
147
|
+
kind: state.kind,
|
|
148
|
+
itemId: state.itemId,
|
|
149
|
+
canonicalItemId,
|
|
150
|
+
contentKind: state.contentKind,
|
|
151
|
+
regionPolicy,
|
|
152
|
+
scopeElement,
|
|
153
|
+
item: state.item ?? null,
|
|
154
|
+
contextVersion,
|
|
155
|
+
};
|
|
156
|
+
if (shell)
|
|
157
|
+
republish(shell, shellValue, sameShellContext);
|
|
158
|
+
else
|
|
159
|
+
shell = provide(host, assessmentToolkitShellContext, shellValue);
|
|
160
|
+
const regionValue = {
|
|
161
|
+
scopeElement,
|
|
162
|
+
ttsHighlightTargetResolver: state.ttsHighlightTargetResolver ?? null,
|
|
163
|
+
};
|
|
164
|
+
if (region)
|
|
165
|
+
republish(region, regionValue, sameRegionScope);
|
|
166
|
+
else
|
|
167
|
+
region = provide(host, assessmentToolkitRegionScopeContext, regionValue);
|
|
168
|
+
setAttributeIfChanged(host, "data-item-id", state.itemId);
|
|
169
|
+
setAttributeIfChanged(host, "data-canonical-item-id", canonicalItemId);
|
|
170
|
+
setAttributeIfChanged(host, "data-pie-shell-root", state.kind);
|
|
171
|
+
setAttributeIfChanged(host, "data-region-policy", regionPolicy);
|
|
172
|
+
const previousIdentity = identity;
|
|
173
|
+
identity = state.itemId
|
|
174
|
+
? {
|
|
175
|
+
kind: state.kind,
|
|
176
|
+
host,
|
|
177
|
+
itemId: state.itemId,
|
|
178
|
+
canonicalItemId,
|
|
179
|
+
contentKind: state.contentKind,
|
|
180
|
+
item: state.item ?? null,
|
|
181
|
+
settings: state.settings ?? null,
|
|
182
|
+
}
|
|
183
|
+
: null;
|
|
184
|
+
if (previousIdentity &&
|
|
185
|
+
(previousIdentity.itemId !== identity?.itemId ||
|
|
186
|
+
previousIdentity.canonicalItemId !== identity?.canonicalItemId)) {
|
|
187
|
+
loaded = null;
|
|
188
|
+
}
|
|
189
|
+
// Answers at once when the runtime is already there.
|
|
190
|
+
stopFindingRuntime ??= connectHostRuntimeContext(host, onRuntime);
|
|
191
|
+
syncRegistration();
|
|
192
|
+
}
|
|
193
|
+
return { publish, retire: registration.retire, disconnect, send };
|
|
194
|
+
}
|
|
@@ -1,29 +1,27 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { type RegionScopeContextListener, type RuntimeContextListener, type ShellContextListener } from "../context/runtime-context-consumer.js";
|
|
1
|
+
import { type AssessmentToolkitHostRuntimeContext, type AssessmentToolkitRegionScopeContext, type AssessmentToolkitRuntimeContext, type AssessmentToolkitShellContext } from "../context/assessment-toolkit-context.js";
|
|
3
2
|
import type { ZIndexLayer } from "../services/ToolCoordinator.js";
|
|
4
3
|
import type { ToolCoordinatorApi } from "../services/interfaces.js";
|
|
5
4
|
type BaseEventInit = Pick<EventInit, "bubbles" | "composed" | "cancelable">;
|
|
6
|
-
/**
|
|
7
|
-
* Contract helper for all toolkit/tool events that must cross custom-element boundaries.
|
|
8
|
-
*/
|
|
9
|
-
export declare function createCrossBoundaryEvent<T>(name: string, detail: T, init?: Partial<BaseEventInit>): CustomEvent<T>;
|
|
10
5
|
/**
|
|
11
6
|
* Dispatches a cross-boundary event with toolkit defaults.
|
|
12
7
|
*/
|
|
13
8
|
export declare function dispatchCrossBoundaryEvent<T>(target: EventTarget, name: string, detail: T, init?: Partial<BaseEventInit>): boolean;
|
|
14
9
|
/**
|
|
15
10
|
* Shared runtime-context connection contract for tools and shells.
|
|
16
|
-
*
|
|
11
|
+
* A provider that connects after the tool answers it through the document's
|
|
12
|
+
* context root.
|
|
17
13
|
*/
|
|
18
|
-
export declare function connectToolRuntimeContext(host: HTMLElement, onValue:
|
|
14
|
+
export declare function connectToolRuntimeContext(host: HTMLElement, onValue: (value: AssessmentToolkitRuntimeContext) => void): () => void;
|
|
19
15
|
/**
|
|
20
16
|
* Shared shell-context connection contract for tools needing item/passage scope.
|
|
21
17
|
*/
|
|
22
|
-
export declare function connectToolShellContext(host: HTMLElement, onValue:
|
|
18
|
+
export declare function connectToolShellContext(host: HTMLElement, onValue: (value: AssessmentToolkitShellContext) => void): () => void;
|
|
23
19
|
/**
|
|
24
20
|
* Shared region-scope context contract for tools targeting content subregions.
|
|
25
21
|
*/
|
|
26
|
-
export declare function connectToolRegionScopeContext(host: HTMLElement, onValue:
|
|
22
|
+
export declare function connectToolRegionScopeContext(host: HTMLElement, onValue: (value: AssessmentToolkitRegionScopeContext) => void): () => void;
|
|
23
|
+
/** Connects a shell or toolkit element to the host runtime context above it. */
|
|
24
|
+
export declare function connectHostRuntimeContext(host: HTMLElement, onValue: (value: AssessmentToolkitHostRuntimeContext) => void): () => void;
|
|
27
25
|
/** A tool's registration with the coordinator in its runtime context. */
|
|
28
26
|
export interface ToolCoordinatorRegistration {
|
|
29
27
|
/**
|
|
@@ -42,8 +40,4 @@ export interface ToolCoordinatorRegistration {
|
|
|
42
40
|
release(): void;
|
|
43
41
|
}
|
|
44
42
|
export declare function createToolCoordinatorRegistration(name: string, layer: ZIndexLayer): ToolCoordinatorRegistration;
|
|
45
|
-
/**
|
|
46
|
-
* Guard utility: narrows unknown context payloads when needed by callers.
|
|
47
|
-
*/
|
|
48
|
-
export declare function isContextValueDefined<T extends UnknownContext>(value: unknown): value is T["__context__"];
|
|
49
43
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { connectContextWithRetry } from "@pie-players/pie-context";
|
|
2
|
+
import { assessmentToolkitHostRuntimeContext, assessmentToolkitRegionScopeContext, assessmentToolkitRuntimeContext, assessmentToolkitShellContext, } from "../context/assessment-toolkit-context.js";
|
|
2
3
|
const CROSS_BOUNDARY_EVENT_INIT = {
|
|
3
4
|
bubbles: true,
|
|
4
5
|
composed: true,
|
|
@@ -7,7 +8,7 @@ const CROSS_BOUNDARY_EVENT_INIT = {
|
|
|
7
8
|
/**
|
|
8
9
|
* Contract helper for all toolkit/tool events that must cross custom-element boundaries.
|
|
9
10
|
*/
|
|
10
|
-
|
|
11
|
+
function createCrossBoundaryEvent(name, detail, init = {}) {
|
|
11
12
|
return new CustomEvent(name, {
|
|
12
13
|
detail,
|
|
13
14
|
...CROSS_BOUNDARY_EVENT_INIT,
|
|
@@ -22,22 +23,27 @@ export function dispatchCrossBoundaryEvent(target, name, detail, init = {}) {
|
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* Shared runtime-context connection contract for tools and shells.
|
|
25
|
-
*
|
|
26
|
+
* A provider that connects after the tool answers it through the document's
|
|
27
|
+
* context root.
|
|
26
28
|
*/
|
|
27
29
|
export function connectToolRuntimeContext(host, onValue) {
|
|
28
|
-
return
|
|
30
|
+
return connectContextWithRetry(host, assessmentToolkitRuntimeContext, onValue);
|
|
29
31
|
}
|
|
30
32
|
/**
|
|
31
33
|
* Shared shell-context connection contract for tools needing item/passage scope.
|
|
32
34
|
*/
|
|
33
35
|
export function connectToolShellContext(host, onValue) {
|
|
34
|
-
return
|
|
36
|
+
return connectContextWithRetry(host, assessmentToolkitShellContext, onValue);
|
|
35
37
|
}
|
|
36
38
|
/**
|
|
37
39
|
* Shared region-scope context contract for tools targeting content subregions.
|
|
38
40
|
*/
|
|
39
41
|
export function connectToolRegionScopeContext(host, onValue) {
|
|
40
|
-
return
|
|
42
|
+
return connectContextWithRetry(host, assessmentToolkitRegionScopeContext, onValue);
|
|
43
|
+
}
|
|
44
|
+
/** Connects a shell or toolkit element to the host runtime context above it. */
|
|
45
|
+
export function connectHostRuntimeContext(host, onValue) {
|
|
46
|
+
return connectContextWithRetry(host, assessmentToolkitHostRuntimeContext, onValue);
|
|
41
47
|
}
|
|
42
48
|
export function createToolCoordinatorRegistration(name, layer) {
|
|
43
49
|
let registered = null;
|
|
@@ -60,9 +66,3 @@ export function createToolCoordinatorRegistration(name, layer) {
|
|
|
60
66
|
release,
|
|
61
67
|
};
|
|
62
68
|
}
|
|
63
|
-
/**
|
|
64
|
-
* Guard utility: narrows unknown context payloads when needed by callers.
|
|
65
|
-
*/
|
|
66
|
-
export function isContextValueDefined(value) {
|
|
67
|
-
return value !== null && value !== undefined;
|
|
68
|
-
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A toolkit stops every registration it claims, so one that reaches the
|
|
3
|
+
* document went unclaimed: its item's catalogs are not filed and it does not
|
|
4
|
+
* count toward its section's loading. Installed from a toolkit's host at
|
|
5
|
+
* connect, once per document across every copy of the toolkit.
|
|
6
|
+
*/
|
|
7
|
+
export declare function watchForUnclaimedRegistrations(doc: Document | null | undefined): void;
|