@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
|
@@ -1,27 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tool
|
|
2
|
+
* Tool registry: the registration contract every tool declares itself with,
|
|
3
|
+
* the shape validation `register` and `override` run, and the `ToolRegistry`
|
|
4
|
+
* class.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
+
* Layout: the exported contract types come first, ending with
|
|
7
|
+
* `ToolRegistration`, then `assertToolRegistrationShape`, then the class. Its
|
|
8
|
+
* members run from mutation and change observation, through queries and the
|
|
9
|
+
* filters for the second and third passes of tool resolution (relevance in
|
|
10
|
+
* `filterVisibleInContext`, applicability in `filterDecidedToolIds`; the policy
|
|
11
|
+
* engine owns the first), to module loading with its undefined-element
|
|
12
|
+
* warnings and the toolbar and surface render helpers.
|
|
6
13
|
*/
|
|
7
14
|
import { dynamicMessageKey } from "@pie-players/pie-players-shared/i18n/provider";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* descriptor's answer, then the config's `provider.id`, then the tool id. `null`
|
|
12
|
-
* when the registration carries no provider.
|
|
13
|
-
*
|
|
14
|
-
* The coordinator registers providers under this id and the section player looks
|
|
15
|
-
* for them in a host-supplied coordinator by it, so the two resolve it one way.
|
|
16
|
-
*/
|
|
17
|
-
export function resolveToolProviderId(registration, config) {
|
|
18
|
-
const descriptor = registration.provider;
|
|
19
|
-
if (!descriptor)
|
|
20
|
-
return null;
|
|
21
|
-
return (descriptor.getProviderId?.(config) ??
|
|
22
|
-
config?.provider?.id ??
|
|
23
|
-
registration.toolId);
|
|
24
|
-
}
|
|
15
|
+
import { resolveToolTag, } from "../tools/tool-tag-map.js";
|
|
16
|
+
import { PENDING_INPUT_WARNING_DELAY_MS, warnOncePerDocument, } from "../runtime/page-warnings.js";
|
|
17
|
+
const logToolCallbackFailure = (toolId, phase, error) => console.error(`[ToolRegistry] Tool '${toolId}' ${phase} check threw:`, error);
|
|
25
18
|
/**
|
|
26
19
|
* A registration's display name in the interface locale.
|
|
27
20
|
*
|
|
@@ -163,11 +156,6 @@ function assertToolRegistrationShape(registration) {
|
|
|
163
156
|
registration.singletonScope !== "section") {
|
|
164
157
|
throw new Error(`Invalid tool registration "${registration.toolId}": selection-gateway tools must declare singletonScope "section".`);
|
|
165
158
|
}
|
|
166
|
-
if (registration.pnpSupportIds !== undefined &&
|
|
167
|
-
(!Array.isArray(registration.pnpSupportIds) ||
|
|
168
|
-
registration.pnpSupportIds.some((pnpId) => typeof pnpId !== "string" || pnpId.trim().length === 0))) {
|
|
169
|
-
throw new Error(`Invalid tool registration "${registration.toolId}": "pnpSupportIds" must be an array of non-empty strings.`);
|
|
170
|
-
}
|
|
171
159
|
if (registration.activation !== "region" &&
|
|
172
160
|
typeof registration.isVisibleInContext !== "function") {
|
|
173
161
|
throw new Error(`Invalid tool registration "${registration.toolId}": "isVisibleInContext" must be a function.`);
|
|
@@ -186,12 +174,6 @@ function assertToolRegistrationShape(registration) {
|
|
|
186
174
|
typeof registration.requiresAuthoredContent.resolve !== "function") {
|
|
187
175
|
throw new Error(`Invalid tool registration "${registration.toolId}": "requiresAuthoredContent" must be an object with a "resolve" function.`);
|
|
188
176
|
}
|
|
189
|
-
if (!registration.pnpSupportIds?.length) {
|
|
190
|
-
// A content dependency's second job is keeping the capability out of a
|
|
191
|
-
// wholesale grant, and a host filters that by support id. Declaring one
|
|
192
|
-
// with no id to filter on would silently drop that guarantee.
|
|
193
|
-
throw new Error(`Invalid tool registration "${registration.toolId}": "requiresAuthoredContent" requires at least one entry in "pnpSupportIds", which is what a host filters a default grant list on.`);
|
|
194
|
-
}
|
|
195
177
|
}
|
|
196
178
|
if (registration.renderToolbar !== undefined) {
|
|
197
179
|
if (typeof registration.renderToolbar !== "function") {
|
|
@@ -209,10 +191,12 @@ function assertToolRegistrationShape(registration) {
|
|
|
209
191
|
*/
|
|
210
192
|
export class ToolRegistry {
|
|
211
193
|
tools = new Map();
|
|
212
|
-
pnpIndex = new Map(); // pnpSupportId → Set<toolId>
|
|
213
194
|
componentOverrides = {};
|
|
195
|
+
watchedUndefinedToolElements = new Set();
|
|
214
196
|
moduleLoaders = new Map();
|
|
215
197
|
loadedToolModules = new Set();
|
|
198
|
+
warnedLoaderReplacements = new Set();
|
|
199
|
+
warnedUnregisteredAllowedTools = new Set();
|
|
216
200
|
moduleLoadPromises = new Map();
|
|
217
201
|
changeListeners = new Set();
|
|
218
202
|
emitChange(event) {
|
|
@@ -240,22 +224,19 @@ export class ToolRegistry {
|
|
|
240
224
|
};
|
|
241
225
|
}
|
|
242
226
|
/**
|
|
243
|
-
* Normalize a single tool
|
|
227
|
+
* Normalize a single tool id (trims surrounding whitespace).
|
|
244
228
|
*/
|
|
245
229
|
normalizeToolId(toolId) {
|
|
246
|
-
return
|
|
230
|
+
return toolId.trim();
|
|
247
231
|
}
|
|
248
232
|
/**
|
|
249
|
-
* Normalize a list of tool
|
|
233
|
+
* Normalize a list of tool ids (trims surrounding whitespace).
|
|
250
234
|
*/
|
|
251
235
|
normalizeToolIds(toolIds) {
|
|
252
236
|
return toolIds.map((toolId) => this.normalizeToolId(toolId));
|
|
253
237
|
}
|
|
254
238
|
/**
|
|
255
|
-
* Register a tool
|
|
256
|
-
*
|
|
257
|
-
* @param registration - Tool registration
|
|
258
|
-
* @throws Error if toolId is already registered
|
|
239
|
+
* Register a tool. Throws when its `toolId` is already registered.
|
|
259
240
|
*/
|
|
260
241
|
register(registration) {
|
|
261
242
|
assertToolRegistrationShape(registration);
|
|
@@ -263,114 +244,44 @@ export class ToolRegistry {
|
|
|
263
244
|
throw new Error(`Tool '${registration.toolId}' is already registered`);
|
|
264
245
|
}
|
|
265
246
|
this.tools.set(registration.toolId, registration);
|
|
266
|
-
// Index PNP support IDs
|
|
267
|
-
if (registration.pnpSupportIds) {
|
|
268
|
-
for (const pnpId of registration.pnpSupportIds) {
|
|
269
|
-
if (!this.pnpIndex.has(pnpId)) {
|
|
270
|
-
this.pnpIndex.set(pnpId, new Set());
|
|
271
|
-
}
|
|
272
|
-
this.pnpIndex.get(pnpId).add(registration.toolId);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
247
|
this.emitChange({ kind: "register", toolIds: [registration.toolId] });
|
|
276
248
|
}
|
|
277
249
|
/**
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
* @param registration - New tool registration (must have existing toolId)
|
|
250
|
+
* Replace an existing tool registration. Throws when its `toolId` is not
|
|
251
|
+
* registered.
|
|
281
252
|
*/
|
|
282
253
|
override(registration) {
|
|
283
254
|
assertToolRegistrationShape(registration);
|
|
284
255
|
if (!this.tools.has(registration.toolId)) {
|
|
285
256
|
throw new Error(`Cannot override non-existent tool '${registration.toolId}'`);
|
|
286
257
|
}
|
|
287
|
-
// Remove old PNP index entries
|
|
288
|
-
const oldReg = this.tools.get(registration.toolId);
|
|
289
|
-
if (oldReg.pnpSupportIds) {
|
|
290
|
-
for (const pnpId of oldReg.pnpSupportIds) {
|
|
291
|
-
this.pnpIndex.get(pnpId)?.delete(registration.toolId);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
// Add new registration
|
|
295
258
|
this.tools.set(registration.toolId, registration);
|
|
296
|
-
// Re-index PNP support IDs
|
|
297
|
-
if (registration.pnpSupportIds) {
|
|
298
|
-
for (const pnpId of registration.pnpSupportIds) {
|
|
299
|
-
if (!this.pnpIndex.has(pnpId)) {
|
|
300
|
-
this.pnpIndex.set(pnpId, new Set());
|
|
301
|
-
}
|
|
302
|
-
this.pnpIndex.get(pnpId).add(registration.toolId);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
259
|
this.emitChange({ kind: "override", toolIds: [registration.toolId] });
|
|
306
260
|
}
|
|
307
261
|
/**
|
|
308
|
-
* Unregister a tool
|
|
309
|
-
*
|
|
310
|
-
* @param toolId - Tool ID to remove
|
|
262
|
+
* Unregister a tool. An unknown id is a no-op and emits no change.
|
|
311
263
|
*/
|
|
312
264
|
unregister(toolId) {
|
|
313
|
-
|
|
314
|
-
if (!reg)
|
|
265
|
+
if (!this.tools.has(toolId))
|
|
315
266
|
return;
|
|
316
|
-
// Remove PNP index entries
|
|
317
|
-
if (reg.pnpSupportIds) {
|
|
318
|
-
for (const pnpId of reg.pnpSupportIds) {
|
|
319
|
-
this.pnpIndex.get(pnpId)?.delete(toolId);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
267
|
this.tools.delete(toolId);
|
|
323
268
|
this.emitChange({ kind: "unregister", toolIds: [toolId] });
|
|
324
269
|
}
|
|
325
|
-
/**
|
|
326
|
-
* Get a tool registration by ID
|
|
327
|
-
*
|
|
328
|
-
* @param toolId - Tool ID
|
|
329
|
-
* @returns Tool registration or undefined
|
|
330
|
-
*/
|
|
331
270
|
get(toolId) {
|
|
332
271
|
return this.tools.get(toolId);
|
|
333
272
|
}
|
|
334
|
-
/**
|
|
335
|
-
* Check if a tool is registered
|
|
336
|
-
*
|
|
337
|
-
* @param toolId - Tool ID
|
|
338
|
-
* @returns true if registered
|
|
339
|
-
*/
|
|
340
273
|
has(toolId) {
|
|
341
274
|
return this.tools.has(toolId);
|
|
342
275
|
}
|
|
343
|
-
/**
|
|
344
|
-
* Get all registered tool IDs
|
|
345
|
-
*
|
|
346
|
-
* @returns Array of tool IDs
|
|
347
|
-
*/
|
|
276
|
+
/** Registered tool ids, in registration order. */
|
|
348
277
|
getAllToolIds() {
|
|
349
278
|
return Array.from(this.tools.keys());
|
|
350
279
|
}
|
|
351
|
-
/**
|
|
352
|
-
* Get all tool registrations
|
|
353
|
-
*
|
|
354
|
-
* @returns Array of tool registrations
|
|
355
|
-
*/
|
|
280
|
+
/** Registrations, in registration order. */
|
|
356
281
|
getAllTools() {
|
|
357
282
|
return Array.from(this.tools.values());
|
|
358
283
|
}
|
|
359
|
-
/**
|
|
360
|
-
* Find tool IDs that support a given PNP support ID
|
|
361
|
-
*
|
|
362
|
-
* @param pnpSupportId - PNP support ID (e.g., 'calculator')
|
|
363
|
-
* @returns Set of tool IDs that support this PNP ID
|
|
364
|
-
*/
|
|
365
|
-
getToolsByPNPSupport(pnpSupportId) {
|
|
366
|
-
return this.pnpIndex.get(pnpSupportId) || new Set();
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* Get tools that support a specific level
|
|
370
|
-
*
|
|
371
|
-
* @param level - Tool level (assessment, section, item, passage, element)
|
|
372
|
-
* @returns Array of tool registrations that support this level
|
|
373
|
-
*/
|
|
284
|
+
/** Registrations whose `supportedLevels` include `level`. */
|
|
374
285
|
getToolsByLevel(level) {
|
|
375
286
|
return this.getAllTools().filter((tool) => tool.supportedLevels.includes(level));
|
|
376
287
|
}
|
|
@@ -401,21 +312,18 @@ export class ToolRegistry {
|
|
|
401
312
|
tool.surfaces?.includes(surface));
|
|
402
313
|
}
|
|
403
314
|
/**
|
|
404
|
-
* Support ids
|
|
315
|
+
* Support ids, which are tool ids, of the capabilities that need authored
|
|
316
|
+
* content.
|
|
405
317
|
*
|
|
406
|
-
* What a host filters a default grant list on
|
|
407
|
-
*
|
|
408
|
-
*
|
|
318
|
+
* What a host filters a default grant list on: granting one of these
|
|
319
|
+
* wholesale grants an accommodation to learners with no documented need
|
|
320
|
+
* for it.
|
|
409
321
|
*/
|
|
410
322
|
getContentDependentSupportIds() {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
for (const supportId of tool.pnpSupportIds || [])
|
|
416
|
-
ids.add(supportId);
|
|
417
|
-
}
|
|
418
|
-
return [...ids].sort();
|
|
323
|
+
return this.getAllTools()
|
|
324
|
+
.filter((tool) => tool.requiresAuthoredContent)
|
|
325
|
+
.map((tool) => tool.toolId)
|
|
326
|
+
.sort();
|
|
419
327
|
}
|
|
420
328
|
/**
|
|
421
329
|
* Filter tool IDs by activation type.
|
|
@@ -424,36 +332,37 @@ export class ToolRegistry {
|
|
|
424
332
|
return toolIds.filter((toolId) => this.getToolActivation(toolId) === activation);
|
|
425
333
|
}
|
|
426
334
|
/**
|
|
427
|
-
*
|
|
335
|
+
* Pass 2 of tool resolution: the registrations among `allowedToolIds` (the
|
|
336
|
+
* ids policy allowed in pass 1) that support `context.level` and answer
|
|
337
|
+
* `isVisibleInContext` with `true`.
|
|
428
338
|
*
|
|
429
|
-
*
|
|
430
|
-
*
|
|
431
|
-
*
|
|
432
|
-
* @param allowedToolIds - Tool IDs that passed Pass 1 (orchestrator approval)
|
|
433
|
-
* @param context - Context to evaluate
|
|
434
|
-
* @returns Array of visible tool registrations
|
|
339
|
+
* @param onFailure - Receives a relevance check's throw; the tool is then not
|
|
340
|
+
* visible. Logs when omitted.
|
|
435
341
|
*/
|
|
436
|
-
filterVisibleInContext(allowedToolIds, context) {
|
|
342
|
+
filterVisibleInContext(allowedToolIds, context, onFailure = logToolCallbackFailure) {
|
|
437
343
|
const visible = [];
|
|
438
344
|
for (const toolId of allowedToolIds) {
|
|
439
345
|
const tool = this.get(toolId);
|
|
440
346
|
if (!tool) {
|
|
441
|
-
|
|
347
|
+
// Toolbars filter on every render; one warning per id carries it.
|
|
348
|
+
if (!this.warnedUnregisteredAllowedTools.has(toolId)) {
|
|
349
|
+
this.warnedUnregisteredAllowedTools.add(toolId);
|
|
350
|
+
console.warn(`Tool '${toolId}' is allowed but not registered`);
|
|
351
|
+
}
|
|
442
352
|
continue;
|
|
443
353
|
}
|
|
444
|
-
// Check if tool supports this level
|
|
445
354
|
if (!tool.supportedLevels.includes(context.level)) {
|
|
446
355
|
continue;
|
|
447
356
|
}
|
|
448
|
-
//
|
|
449
|
-
//
|
|
357
|
+
// A region capability declares no answer and has no toolbar presence,
|
|
358
|
+
// so it is never visible here.
|
|
450
359
|
try {
|
|
451
360
|
if (tool.isVisibleInContext?.(context)) {
|
|
452
361
|
visible.push(tool);
|
|
453
362
|
}
|
|
454
363
|
}
|
|
455
364
|
catch (error) {
|
|
456
|
-
|
|
365
|
+
onFailure(toolId, "tool-visibility", error);
|
|
457
366
|
}
|
|
458
367
|
}
|
|
459
368
|
return visible;
|
|
@@ -470,8 +379,10 @@ export class ToolRegistry {
|
|
|
470
379
|
*
|
|
471
380
|
* @param toolId - Tool to ask
|
|
472
381
|
* @param contexts - Every context the tool could act on at this placement
|
|
382
|
+
* @param onFailure - Receives the gate's throw; the tool then counts as
|
|
383
|
+
* applicable. Logs when omitted.
|
|
473
384
|
*/
|
|
474
|
-
isApplicableToAnyContext(toolId, contexts) {
|
|
385
|
+
isApplicableToAnyContext(toolId, contexts, onFailure = logToolCallbackFailure) {
|
|
475
386
|
const tool = this.get(toolId);
|
|
476
387
|
if (!tool?.isApplicableToContent)
|
|
477
388
|
return true;
|
|
@@ -482,24 +393,70 @@ export class ToolRegistry {
|
|
|
482
393
|
return tool.isApplicableToContent?.(context) ?? true;
|
|
483
394
|
}
|
|
484
395
|
catch (error) {
|
|
485
|
-
|
|
396
|
+
onFailure(toolId, "tool-applicability", error);
|
|
486
397
|
// A gate that throws has not established that the tool is useless.
|
|
487
398
|
return true;
|
|
488
399
|
}
|
|
489
400
|
});
|
|
490
401
|
}
|
|
491
402
|
/**
|
|
492
|
-
*
|
|
493
|
-
*
|
|
403
|
+
* Passes 2 and 3 over a policy decision's tools: the ids a surface at `level`
|
|
404
|
+
* shows against `contexts`, in decision order. This is the rule a toolbar
|
|
405
|
+
* applies, for a host that builds its own tool surface.
|
|
494
406
|
*
|
|
495
|
-
*
|
|
407
|
+
* - A tool that does not support `level` is dropped.
|
|
408
|
+
* - Relevance (`isVisibleInContext`) keeps a tool visible in any one context.
|
|
409
|
+
* It does not run for a granted entry (`required` or `alwaysAvailable`),
|
|
410
|
+
* at section level, whose relevance is item-dependent, or when `contexts`
|
|
411
|
+
* is empty, since content that has not resolved cannot rule a tool out.
|
|
412
|
+
* - Applicability (`isApplicableToContent`) then removes a tool that can act
|
|
413
|
+
* on none of `contexts`, granted or not. It does not run at section level.
|
|
414
|
+
*
|
|
415
|
+
* @param entries - The decision's surviving entries (Pass 1), such as
|
|
416
|
+
* `ToolPolicyDecision.visibleTools`
|
|
417
|
+
* @param level - The level of the surface the tools render on
|
|
418
|
+
* @param contexts - Every context the tools could act on at that surface
|
|
419
|
+
* @param onFailure - Receives a relevance or applicability check's throw.
|
|
420
|
+
* Logs when omitted.
|
|
421
|
+
*/
|
|
422
|
+
filterDecidedToolIds(entries, level, contexts, onFailure = logToolCallbackFailure) {
|
|
423
|
+
const granted = new Set();
|
|
424
|
+
const decided = [];
|
|
425
|
+
for (const entry of entries) {
|
|
426
|
+
const toolId = this.normalizeToolId(entry.toolId);
|
|
427
|
+
if (!toolId)
|
|
428
|
+
continue;
|
|
429
|
+
if (entry.required || entry.alwaysAvailable)
|
|
430
|
+
granted.add(toolId);
|
|
431
|
+
if (decided.includes(toolId))
|
|
432
|
+
continue;
|
|
433
|
+
if (!this.get(toolId)?.supportedLevels.includes(level))
|
|
434
|
+
continue;
|
|
435
|
+
decided.push(toolId);
|
|
436
|
+
}
|
|
437
|
+
if (level === "section")
|
|
438
|
+
return decided;
|
|
439
|
+
const relevant = new Set(contexts.length === 0
|
|
440
|
+
? decided
|
|
441
|
+
: decided.filter((toolId) => granted.has(toolId)));
|
|
442
|
+
for (const context of contexts) {
|
|
443
|
+
const ungranted = decided.filter((toolId) => !relevant.has(toolId));
|
|
444
|
+
for (const tool of this.filterVisibleInContext(ungranted, context, onFailure)) {
|
|
445
|
+
relevant.add(tool.toolId);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return decided.filter((toolId) => relevant.has(toolId) &&
|
|
449
|
+
this.isApplicableToAnyContext(toolId, contexts, onFailure));
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Plain metadata for every registration, in registration order, for UIs
|
|
453
|
+
* such as a PNP configuration screen.
|
|
496
454
|
*/
|
|
497
455
|
getToolMetadata() {
|
|
498
456
|
return this.getAllTools().map((tool) => ({
|
|
499
457
|
toolId: tool.toolId,
|
|
500
458
|
name: tool.name,
|
|
501
459
|
description: tool.description,
|
|
502
|
-
pnpSupportIds: tool.pnpSupportIds || [],
|
|
503
460
|
supportedLevels: tool.supportedLevels,
|
|
504
461
|
activation: tool.activation || "toolbar-toggle",
|
|
505
462
|
singletonScope: tool.singletonScope || null,
|
|
@@ -508,25 +465,6 @@ export class ToolRegistry {
|
|
|
508
465
|
contentDependencyDescription: tool.requiresAuthoredContent?.description ?? null,
|
|
509
466
|
}));
|
|
510
467
|
}
|
|
511
|
-
/**
|
|
512
|
-
* Generate PNP support IDs from enabled tools
|
|
513
|
-
* Useful for creating PNP profiles
|
|
514
|
-
*
|
|
515
|
-
* @param enabledToolIds - Tool IDs to enable
|
|
516
|
-
* @returns Array of unique PNP support IDs
|
|
517
|
-
*/
|
|
518
|
-
generatePNPSupportsFromTools(enabledToolIds) {
|
|
519
|
-
const pnpSupports = new Set();
|
|
520
|
-
for (const toolId of enabledToolIds) {
|
|
521
|
-
const tool = this.get(toolId);
|
|
522
|
-
if (tool?.pnpSupportIds) {
|
|
523
|
-
for (const pnpId of tool.pnpSupportIds) {
|
|
524
|
-
pnpSupports.add(pnpId);
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
return Array.from(pnpSupports);
|
|
529
|
-
}
|
|
530
468
|
/**
|
|
531
469
|
* Clear all registrations (useful for testing)
|
|
532
470
|
*/
|
|
@@ -535,7 +473,6 @@ export class ToolRegistry {
|
|
|
535
473
|
if (toolIds.length === 0)
|
|
536
474
|
return;
|
|
537
475
|
this.tools.clear();
|
|
538
|
-
this.pnpIndex.clear();
|
|
539
476
|
this.emitChange({ kind: "clear", toolIds });
|
|
540
477
|
}
|
|
541
478
|
/**
|
|
@@ -550,6 +487,8 @@ export class ToolRegistry {
|
|
|
550
487
|
/**
|
|
551
488
|
* Register lazy module loaders by toolId.
|
|
552
489
|
* Toolbars call ensureToolModuleLoaded(toolId) before instance creation.
|
|
490
|
+
* A tool whose module has loaded, or is loading, keeps its loader: its
|
|
491
|
+
* elements are already defined, so a replacement could never take effect.
|
|
553
492
|
*/
|
|
554
493
|
setToolModuleLoaders(loaders) {
|
|
555
494
|
const entries = Object.entries(loaders).filter((entry) => entry[1] !== undefined);
|
|
@@ -563,6 +502,11 @@ export class ToolRegistry {
|
|
|
563
502
|
for (const [toolId, loader] of entries) {
|
|
564
503
|
if (this.moduleLoaders.get(toolId) === loader)
|
|
565
504
|
continue;
|
|
505
|
+
if (this.loadedToolModules.has(toolId) ||
|
|
506
|
+
this.moduleLoadPromises.has(toolId)) {
|
|
507
|
+
this.warnLoaderReplacedAfterLoad(toolId);
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
566
510
|
this.moduleLoaders.set(toolId, loader);
|
|
567
511
|
changedToolIds.push(toolId);
|
|
568
512
|
}
|
|
@@ -570,6 +514,12 @@ export class ToolRegistry {
|
|
|
570
514
|
this.emitChange({ kind: "module-loaders", toolIds: changedToolIds });
|
|
571
515
|
}
|
|
572
516
|
}
|
|
517
|
+
warnLoaderReplacedAfterLoad(toolId) {
|
|
518
|
+
if (this.warnedLoaderReplacements.has(toolId))
|
|
519
|
+
return;
|
|
520
|
+
this.warnedLoaderReplacements.add(toolId);
|
|
521
|
+
console.warn(`[ToolRegistry] Ignored a new module loader for "${toolId}": its module already loaded, so the elements it defines stay in place. Register loaders before the tool first renders.`);
|
|
522
|
+
}
|
|
573
523
|
/**
|
|
574
524
|
* Ensure tool module side-effects are loaded exactly once.
|
|
575
525
|
* Safe to call repeatedly; concurrent callers share the same promise.
|
|
@@ -583,11 +533,14 @@ export class ToolRegistry {
|
|
|
583
533
|
return;
|
|
584
534
|
}
|
|
585
535
|
const loader = this.moduleLoaders.get(toolId);
|
|
586
|
-
if (!loader)
|
|
536
|
+
if (!loader) {
|
|
537
|
+
this.watchUndefinedToolElement(toolId);
|
|
587
538
|
return;
|
|
539
|
+
}
|
|
588
540
|
const loadPromise = (async () => {
|
|
589
541
|
await loader();
|
|
590
542
|
this.loadedToolModules.add(toolId);
|
|
543
|
+
this.warnIfLoadedToolElementUndefined(toolId);
|
|
591
544
|
})();
|
|
592
545
|
this.moduleLoadPromises.set(toolId, loadPromise);
|
|
593
546
|
try {
|
|
@@ -598,10 +551,70 @@ export class ToolRegistry {
|
|
|
598
551
|
}
|
|
599
552
|
}
|
|
600
553
|
/**
|
|
601
|
-
*
|
|
554
|
+
* The undefined element tag a tool would render, or `null` when the tool is
|
|
555
|
+
* built by a component factory, its element is defined, or there is no DOM.
|
|
556
|
+
*/
|
|
557
|
+
undefinedToolElementTag(toolId) {
|
|
558
|
+
if (typeof customElements === "undefined" || typeof document === "undefined") {
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
561
|
+
const overrides = this.componentOverrides;
|
|
562
|
+
if (overrides.toolComponentFactories?.[toolId])
|
|
563
|
+
return null;
|
|
564
|
+
let tagName;
|
|
565
|
+
try {
|
|
566
|
+
tagName = resolveToolTag(toolId, overrides);
|
|
567
|
+
}
|
|
568
|
+
catch {
|
|
569
|
+
// Element creation reports the missing tag mapping.
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
572
|
+
return customElements.get(tagName) ? null : tagName;
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Warn when a tool's module loader finished without defining the element the
|
|
576
|
+
* tag map points the tool at, as when a host remaps the tag but keeps a
|
|
577
|
+
* loader that defines the packaged element. The tool otherwise renders blank.
|
|
578
|
+
*/
|
|
579
|
+
warnIfLoadedToolElementUndefined(toolId) {
|
|
580
|
+
const tagName = this.undefinedToolElementTag(toolId);
|
|
581
|
+
if (!tagName)
|
|
582
|
+
return;
|
|
583
|
+
warnOncePerDocument(document, `loadedToolElementUndefined.${toolId}.${tagName}`, `[ToolRegistry] Tool "${toolId}" renders <${tagName}>, but its module loader finished without defining it, so the tool renders blank. The tag map and the loader disagree: map "${toolId}" to the tag its loader defines, or register a loader that defines <${tagName}>. Reported once per page.`);
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Warn when a tool with no module loader still has no element definition
|
|
587
|
+
* after the pending-input delay. A registry built without loaders otherwise
|
|
588
|
+
* renders the tool as an unknown element with no error.
|
|
589
|
+
*/
|
|
590
|
+
watchUndefinedToolElement(toolId) {
|
|
591
|
+
if (this.watchedUndefinedToolElements.has(toolId))
|
|
592
|
+
return;
|
|
593
|
+
const tagName = this.undefinedToolElementTag(toolId);
|
|
594
|
+
if (!tagName)
|
|
595
|
+
return;
|
|
596
|
+
this.watchedUndefinedToolElements.add(toolId);
|
|
597
|
+
const doc = document;
|
|
598
|
+
const timer = setTimeout(() => {
|
|
599
|
+
if (customElements.get(tagName))
|
|
600
|
+
return;
|
|
601
|
+
warnOncePerDocument(doc, `undefinedToolElement.${toolId}`, `[ToolRegistry] Tool "${toolId}" renders <${tagName}>, which is still undefined after ${PENDING_INPUT_WARNING_DELAY_MS / 1000} s, and its registry has no module loader for it. Register one with setToolModuleLoaders or import the tool's package before it renders; a registry from createPackagedToolRegistry has the packaged loaders unless its toolModuleLoaders replaced them. Reported once per page.`);
|
|
602
|
+
}, PENDING_INPUT_WARNING_DELAY_MS);
|
|
603
|
+
void customElements.whenDefined(tagName).then(() => clearTimeout(timer));
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Load a set of tool modules, each to completion, and resolve with the error
|
|
607
|
+
* of every tool whose module failed, by tool id. One failure leaves the other
|
|
608
|
+
* tools loaded and usable. A failed tool is retried on the next call.
|
|
602
609
|
*/
|
|
603
610
|
async ensureToolModulesLoaded(toolIds) {
|
|
604
|
-
await Promise.
|
|
611
|
+
const results = await Promise.allSettled(toolIds.map((toolId) => this.ensureToolModuleLoaded(toolId)));
|
|
612
|
+
const failures = new Map();
|
|
613
|
+
results.forEach((result, index) => {
|
|
614
|
+
if (result.status === "rejected")
|
|
615
|
+
failures.set(toolIds[index], result.reason);
|
|
616
|
+
});
|
|
617
|
+
return failures;
|
|
605
618
|
}
|
|
606
619
|
/**
|
|
607
620
|
* Whether a tool module has already been loaded.
|
|
@@ -625,10 +638,7 @@ export class ToolRegistry {
|
|
|
625
638
|
}
|
|
626
639
|
const mergedContext = {
|
|
627
640
|
...toolbarContext,
|
|
628
|
-
componentOverrides:
|
|
629
|
-
...(this.componentOverrides || {}),
|
|
630
|
-
...(toolbarContext.componentOverrides || {}),
|
|
631
|
-
},
|
|
641
|
+
componentOverrides: this.mergeComponentOverrides(toolbarContext.componentOverrides),
|
|
632
642
|
};
|
|
633
643
|
return tool.renderToolbar(context, mergedContext);
|
|
634
644
|
}
|
|
@@ -651,10 +661,14 @@ export class ToolRegistry {
|
|
|
651
661
|
}
|
|
652
662
|
return tool.renderSurface({
|
|
653
663
|
...context,
|
|
654
|
-
componentOverrides:
|
|
655
|
-
...(this.componentOverrides || {}),
|
|
656
|
-
...(context.componentOverrides || {}),
|
|
657
|
-
},
|
|
664
|
+
componentOverrides: this.mergeComponentOverrides(context.componentOverrides),
|
|
658
665
|
});
|
|
659
666
|
}
|
|
667
|
+
/** The registry's component overrides, with the caller's taking precedence. */
|
|
668
|
+
mergeComponentOverrides(contextOverrides) {
|
|
669
|
+
return {
|
|
670
|
+
...(this.componentOverrides || {}),
|
|
671
|
+
...(contextOverrides || {}),
|
|
672
|
+
};
|
|
673
|
+
}
|
|
660
674
|
}
|