@pie-players/pie-assessment-toolkit 0.3.40 → 0.3.42

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.
@@ -5292,9 +5292,15 @@ function assertNonEmptyString(value, fieldName) {
5292
5292
  }
5293
5293
  var SCRIPTABLE_ICON_PATTERNS = [
5294
5294
  { pattern: /<script\b/i, reason: "contains a <script> tag" },
5295
- { pattern: /\son[a-z]+\s*=/i, reason: "contains an inline event handler (on*=) attribute" },
5295
+ {
5296
+ pattern: /\son[a-z]+\s*=/i,
5297
+ reason: "contains an inline event handler (on*=) attribute"
5298
+ },
5296
5299
  { pattern: /javascript:/i, reason: "contains a javascript: URL" },
5297
- { pattern: /<foreignObject\b/i, reason: "contains a <foreignObject> element" }
5300
+ {
5301
+ pattern: /<foreignObject\b/i,
5302
+ reason: "contains a <foreignObject> element"
5303
+ }
5298
5304
  ];
5299
5305
  function assertIconStringIsSafe(toolId, icon, fieldName) {
5300
5306
  const trimmed = icon.trimStart();
@@ -6526,6 +6532,35 @@ var createToolElement = (toolId, context, toolbarContext, overrides) => {
6526
6532
  };
6527
6533
 
6528
6534
  // dist/tools/registrations/calculator.js
6535
+ function normalizeCalculatorType(value) {
6536
+ return value === "basic" || value === "scientific" ? value : null;
6537
+ }
6538
+ function getCalculatorRenderParams(toolbarContext) {
6539
+ const params = toolbarContext.getToolRenderParams?.("calculator") ?? {};
6540
+ const calculatorType = normalizeCalculatorType(params.calculatorType);
6541
+ const availableTypesRaw = params.availableTypes;
6542
+ const availableTypes = Array.isArray(availableTypesRaw) ? availableTypesRaw.map((value) => normalizeCalculatorType(value)).filter((value) => value !== null) : calculatorType ? [calculatorType] : null;
6543
+ return {
6544
+ calculatorType,
6545
+ availableTypes,
6546
+ displayName: calculatorType === "scientific" ? "Scientific Calculator" : calculatorType === "basic" ? "Basic Calculator" : "Calculator"
6547
+ };
6548
+ }
6549
+ function applyCalculatorParamsToElement(element2, calculatorType, availableTypes) {
6550
+ const calculatorElement = element2;
6551
+ if (calculatorType) {
6552
+ calculatorElement.calculatorType = calculatorType;
6553
+ element2.setAttribute("calculator-type", calculatorType);
6554
+ } else {
6555
+ delete calculatorElement.calculatorType;
6556
+ element2.removeAttribute("calculator-type");
6557
+ }
6558
+ if (availableTypes && availableTypes.length > 0) {
6559
+ calculatorElement.availableTypes = availableTypes;
6560
+ } else {
6561
+ delete calculatorElement.availableTypes;
6562
+ }
6563
+ }
6529
6564
  var calculatorToolRegistration = {
6530
6565
  toolId: "calculator",
6531
6566
  name: "Calculator",
@@ -6563,18 +6598,22 @@ var calculatorToolRegistration = {
6563
6598
  return hasMathContent(context);
6564
6599
  },
6565
6600
  renderToolbar(context, toolbarContext) {
6601
+ const { calculatorType, availableTypes, displayName } = getCalculatorRenderParams(toolbarContext);
6566
6602
  const fullToolId = createScopedToolId(this.toolId, toolbarContext.scope.level, toolbarContext.scope.scopeId);
6567
6603
  const componentOverrides = toolbarContext.componentOverrides;
6568
6604
  const overlay = createToolElement(this.toolId, context, toolbarContext, componentOverrides);
6569
6605
  overlay.setAttribute("tool-id", fullToolId);
6570
6606
  overlay.toolkitCoordinator = toolbarContext.toolkitCoordinator;
6607
+ applyCalculatorParamsToElement(overlay, calculatorType, availableTypes);
6608
+ const openLabel = calculatorType === null ? "Open scientific calculator" : `Open ${displayName.toLowerCase()}`;
6609
+ const closeLabel = calculatorType === null ? "Close scientific calculator" : `Close ${displayName.toLowerCase()}`;
6571
6610
  const button = {
6572
6611
  toolId: this.toolId,
6573
- label: this.name,
6612
+ label: displayName,
6574
6613
  icon: typeof this.icon === "function" ? this.icon(context) : this.icon,
6575
6614
  disabled: false,
6576
- ariaLabel: "Open scientific calculator",
6577
- tooltip: "Calculator",
6615
+ ariaLabel: openLabel,
6616
+ tooltip: displayName,
6578
6617
  onClick: () => toolbarContext.toggleTool(this.toolId),
6579
6618
  active: toolbarContext.isToolVisible(fullToolId)
6580
6619
  };
@@ -6602,8 +6641,9 @@ var calculatorToolRegistration = {
6602
6641
  sync: () => {
6603
6642
  const active = toolbarContext.isToolVisible(fullToolId);
6604
6643
  button.active = active;
6605
- button.ariaLabel = active ? "Close scientific calculator" : "Open scientific calculator";
6606
- button.tooltip = active ? "Close calculator" : "Calculator";
6644
+ button.label = displayName;
6645
+ button.ariaLabel = active ? closeLabel : openLabel;
6646
+ button.tooltip = active ? `Close ${displayName.toLowerCase()}` : displayName;
6607
6647
  if (lastVisibleState !== active) {
6608
6648
  overlay.visible = active;
6609
6649
  lastVisibleState = active;
@@ -6611,6 +6651,7 @@ var calculatorToolRegistration = {
6611
6651
  if (overlay.toolkitCoordinator !== toolbarContext.toolkitCoordinator) {
6612
6652
  overlay.toolkitCoordinator = toolbarContext.toolkitCoordinator;
6613
6653
  }
6654
+ applyCalculatorParamsToElement(overlay, calculatorType, availableTypes);
6614
6655
  },
6615
6656
  subscribeActive: (callback) => {
6616
6657
  if (!toolbarContext.subscribeVisibility)
@@ -7546,6 +7587,7 @@ function ItemToolBar($$anchor, $$props) {
7546
7587
  let shellContext = state(null);
7547
7588
  let moduleLoadVersion = state(0);
7548
7589
  let policyChangeVersion = state(0);
7590
+ let toolContextResolverChangeVersion = state(0);
7549
7591
  user_effect(() => {
7550
7592
  if (!get2(toolbarRootElement))
7551
7593
  return;
@@ -7573,6 +7615,19 @@ function ItemToolBar($$anchor, $$props) {
7573
7615
  } catch {}
7574
7616
  };
7575
7617
  });
7618
+ user_effect(() => {
7619
+ const coord = get2(runtimeContext)?.toolkitCoordinator;
7620
+ if (!coord || typeof coord.onToolContextResolverChange !== "function")
7621
+ return;
7622
+ const unsubscribe = coord.onToolContextResolverChange(() => {
7623
+ set(toolContextResolverChangeVersion, get2(toolContextResolverChangeVersion) + 1);
7624
+ });
7625
+ return () => {
7626
+ try {
7627
+ unsubscribe?.();
7628
+ } catch {}
7629
+ };
7630
+ });
7576
7631
  const effectiveToolCoordinator = user_derived(() => get2(runtimeContext)?.toolCoordinator);
7577
7632
  const effectiveTTSService = user_derived(() => get2(runtimeContext)?.ttsService);
7578
7633
  const effectiveElementToolStateStore = user_derived(() => get2(runtimeContext)?.elementToolStateStore);
@@ -7708,26 +7763,100 @@ function ItemToolBar($$anchor, $$props) {
7708
7763
  elementId: model.id
7709
7764
  }));
7710
7765
  });
7766
+ const resolutionToolbarContext = user_derived(() => {
7767
+ const toolkitCoordinator = get2(runtimeContext)?.toolkitCoordinator || null;
7768
+ const toInstanceToolId = (toolId) => parseScopedToolId(toolId) ? toolId : createScopedToolId(toolId, get2(effectiveLevel), get2(effectiveScopeId));
7769
+ return {
7770
+ scope: {
7771
+ level: get2(effectiveLevel),
7772
+ scopeId: get2(effectiveScopeId),
7773
+ assessmentId: get2(runtimeContext)?.assessmentId,
7774
+ sectionId: get2(effectiveSectionId),
7775
+ itemId: get2(effectiveItemId),
7776
+ canonicalItemId: get2(effectiveCanonicalItemId),
7777
+ contentKind: get2(effectiveContentKind)
7778
+ },
7779
+ itemId: get2(effectiveScopeId),
7780
+ catalogId: get2(effectiveCatalogId),
7781
+ language: language(),
7782
+ ui: { size: size() },
7783
+ getScopeElement: () => scopeElement() || get2(shellContext)?.scopeElement || null,
7784
+ getGlobalElementId: () => {
7785
+ if (!get2(effectiveElementToolStateStore) || !get2(effectiveAssessmentId) || !get2(effectiveSectionId) || !get2(effectiveCanonicalItemId))
7786
+ return null;
7787
+ return get2(effectiveElementToolStateStore).getGlobalElementId(get2(effectiveAssessmentId), get2(effectiveSectionId), get2(effectiveCanonicalItemId), get2(effectiveCanonicalItemId));
7788
+ },
7789
+ toolCoordinator: get2(effectiveToolCoordinator) || null,
7790
+ toolkitCoordinator,
7791
+ ttsService: get2(effectiveTTSService) || null,
7792
+ elementToolStateStore: get2(effectiveElementToolStateStore) || null,
7793
+ toggleTool: (toolId) => {
7794
+ if (!get2(effectiveToolCoordinator))
7795
+ return;
7796
+ const instanceToolId = toInstanceToolId(toolId);
7797
+ if (!get2(effectiveToolCoordinator).getToolState(instanceToolId)) {
7798
+ get2(effectiveToolCoordinator).registerTool(instanceToolId, toolId);
7799
+ }
7800
+ get2(effectiveToolCoordinator).toggleTool(instanceToolId);
7801
+ },
7802
+ isToolVisible: (toolId) => {
7803
+ if (!get2(effectiveToolCoordinator))
7804
+ return false;
7805
+ return get2(effectiveToolCoordinator).isToolVisible(toInstanceToolId(toolId));
7806
+ },
7807
+ subscribeVisibility: get2(effectiveToolCoordinator) ? (listener) => get2(effectiveToolCoordinator).subscribe(listener) : null
7808
+ };
7809
+ });
7810
+ const hostResolvedToolContextById = user_derived(() => {
7811
+ get2(toolContextResolverChangeVersion);
7812
+ const coord = get2(runtimeContext)?.toolkitCoordinator;
7813
+ if (!coord || typeof coord.hasToolContextResolver !== "function" || typeof coord.resolveToolContext !== "function") {
7814
+ return {};
7815
+ }
7816
+ const resolved = {};
7817
+ for (const toolId of get2(allowedToolIds)) {
7818
+ const registration = get2(effectiveToolRegistry).get(toolId);
7819
+ if (!registration?.supportedLevels.includes(get2(effectiveLevel)))
7820
+ continue;
7821
+ if (!coord.hasToolContextResolver(toolId))
7822
+ continue;
7823
+ const result = coord.resolveToolContext({
7824
+ toolId,
7825
+ context: get2(renderContext),
7826
+ toolbarContext: get2(resolutionToolbarContext)
7827
+ });
7828
+ if (result) {
7829
+ resolved[toolId] = result;
7830
+ }
7831
+ }
7832
+ return resolved;
7833
+ });
7711
7834
  const visibleToolIds = user_derived(() => {
7712
7835
  const levelCompatibleToolIds = get2(allowedToolIds).filter((toolId) => {
7713
7836
  const registration = get2(effectiveToolRegistry).get(toolId);
7714
7837
  return registration ? registration.supportedLevels.includes(get2(effectiveLevel)) : false;
7715
7838
  });
7839
+ const hostResolvedEntries = Object.values(get2(hostResolvedToolContextById));
7840
+ const hostResolvedToolIds = new Set(hostResolvedEntries.map((entry) => entry.toolId));
7841
+ const visible = new Set(hostResolvedEntries.filter((entry) => entry.visible).map((entry) => entry.toolId));
7842
+ const toolOwnedToolIds = levelCompatibleToolIds.filter((toolId) => !hostResolvedToolIds.has(toolId));
7716
7843
  if (get2(effectiveLevel) === "section") {
7717
- return get2(allowedToolIds);
7844
+ toolOwnedToolIds.forEach((toolId) => visible.add(toolId));
7845
+ return Array.from(visible);
7718
7846
  }
7719
7847
  if (!get2(contentReady)) {
7720
- return levelCompatibleToolIds;
7848
+ toolOwnedToolIds.forEach((toolId) => visible.add(toolId));
7849
+ return Array.from(visible);
7721
7850
  }
7722
7851
  if (!get2(toolContext) && get2(elementContexts).length === 0) {
7723
- return levelCompatibleToolIds;
7852
+ toolOwnedToolIds.forEach((toolId) => visible.add(toolId));
7853
+ return Array.from(visible);
7724
7854
  }
7725
- const visible = new Set;
7726
7855
  if (get2(toolContext)) {
7727
- get2(effectiveToolRegistry).filterVisibleInContext(levelCompatibleToolIds, get2(toolContext)).forEach((tool) => visible.add(tool.toolId));
7856
+ get2(effectiveToolRegistry).filterVisibleInContext(toolOwnedToolIds, get2(toolContext)).forEach((tool) => visible.add(tool.toolId));
7728
7857
  }
7729
7858
  for (const context of get2(elementContexts)) {
7730
- get2(effectiveToolRegistry).filterVisibleInContext(levelCompatibleToolIds, context).forEach((tool) => visible.add(tool.toolId));
7859
+ get2(effectiveToolRegistry).filterVisibleInContext(toolOwnedToolIds, context).forEach((tool) => visible.add(tool.toolId));
7731
7860
  }
7732
7861
  return Array.from(visible);
7733
7862
  });
@@ -7795,7 +7924,9 @@ function ItemToolBar($$anchor, $$props) {
7795
7924
  return false;
7796
7925
  return get2(effectiveToolCoordinator).isToolVisible(toInstanceToolId(toolId));
7797
7926
  },
7798
- subscribeVisibility: get2(effectiveToolCoordinator) ? (listener) => get2(effectiveToolCoordinator).subscribe(listener) : null
7927
+ subscribeVisibility: get2(effectiveToolCoordinator) ? (listener) => get2(effectiveToolCoordinator).subscribe(listener) : null,
7928
+ getResolvedToolContext: (toolId) => get2(hostResolvedToolContextById)[toolId] ?? null,
7929
+ getToolRenderParams: (toolId) => get2(hostResolvedToolContextById)[toolId]?.params ?? null
7799
7930
  };
7800
7931
  });
7801
7932
  let renderedTools = user_derived(() => {
@@ -4662,9 +4662,15 @@ function assertNonEmptyString(value, fieldName) {
4662
4662
  }
4663
4663
  var SCRIPTABLE_ICON_PATTERNS = [
4664
4664
  { pattern: /<script\b/i, reason: "contains a <script> tag" },
4665
- { pattern: /\son[a-z]+\s*=/i, reason: "contains an inline event handler (on*=) attribute" },
4665
+ {
4666
+ pattern: /\son[a-z]+\s*=/i,
4667
+ reason: "contains an inline event handler (on*=) attribute"
4668
+ },
4666
4669
  { pattern: /javascript:/i, reason: "contains a javascript: URL" },
4667
- { pattern: /<foreignObject\b/i, reason: "contains a <foreignObject> element" }
4670
+ {
4671
+ pattern: /<foreignObject\b/i,
4672
+ reason: "contains a <foreignObject> element"
4673
+ }
4668
4674
  ];
4669
4675
  function assertIconStringIsSafe(toolId, icon, fieldName) {
4670
4676
  const trimmed = icon.trimStart();
@@ -5880,6 +5886,35 @@ var createToolElement = (toolId, context, toolbarContext, overrides) => {
5880
5886
  };
5881
5887
 
5882
5888
  // dist/tools/registrations/calculator.js
5889
+ function normalizeCalculatorType(value) {
5890
+ return value === "basic" || value === "scientific" ? value : null;
5891
+ }
5892
+ function getCalculatorRenderParams(toolbarContext) {
5893
+ const params = toolbarContext.getToolRenderParams?.("calculator") ?? {};
5894
+ const calculatorType = normalizeCalculatorType(params.calculatorType);
5895
+ const availableTypesRaw = params.availableTypes;
5896
+ const availableTypes = Array.isArray(availableTypesRaw) ? availableTypesRaw.map((value) => normalizeCalculatorType(value)).filter((value) => value !== null) : calculatorType ? [calculatorType] : null;
5897
+ return {
5898
+ calculatorType,
5899
+ availableTypes,
5900
+ displayName: calculatorType === "scientific" ? "Scientific Calculator" : calculatorType === "basic" ? "Basic Calculator" : "Calculator"
5901
+ };
5902
+ }
5903
+ function applyCalculatorParamsToElement(element2, calculatorType, availableTypes) {
5904
+ const calculatorElement = element2;
5905
+ if (calculatorType) {
5906
+ calculatorElement.calculatorType = calculatorType;
5907
+ element2.setAttribute("calculator-type", calculatorType);
5908
+ } else {
5909
+ delete calculatorElement.calculatorType;
5910
+ element2.removeAttribute("calculator-type");
5911
+ }
5912
+ if (availableTypes && availableTypes.length > 0) {
5913
+ calculatorElement.availableTypes = availableTypes;
5914
+ } else {
5915
+ delete calculatorElement.availableTypes;
5916
+ }
5917
+ }
5883
5918
  var calculatorToolRegistration = {
5884
5919
  toolId: "calculator",
5885
5920
  name: "Calculator",
@@ -5917,18 +5952,22 @@ var calculatorToolRegistration = {
5917
5952
  return hasMathContent(context);
5918
5953
  },
5919
5954
  renderToolbar(context, toolbarContext) {
5955
+ const { calculatorType, availableTypes, displayName } = getCalculatorRenderParams(toolbarContext);
5920
5956
  const fullToolId = createScopedToolId(this.toolId, toolbarContext.scope.level, toolbarContext.scope.scopeId);
5921
5957
  const componentOverrides = toolbarContext.componentOverrides;
5922
5958
  const overlay = createToolElement(this.toolId, context, toolbarContext, componentOverrides);
5923
5959
  overlay.setAttribute("tool-id", fullToolId);
5924
5960
  overlay.toolkitCoordinator = toolbarContext.toolkitCoordinator;
5961
+ applyCalculatorParamsToElement(overlay, calculatorType, availableTypes);
5962
+ const openLabel = calculatorType === null ? "Open scientific calculator" : `Open ${displayName.toLowerCase()}`;
5963
+ const closeLabel = calculatorType === null ? "Close scientific calculator" : `Close ${displayName.toLowerCase()}`;
5925
5964
  const button = {
5926
5965
  toolId: this.toolId,
5927
- label: this.name,
5966
+ label: displayName,
5928
5967
  icon: typeof this.icon === "function" ? this.icon(context) : this.icon,
5929
5968
  disabled: false,
5930
- ariaLabel: "Open scientific calculator",
5931
- tooltip: "Calculator",
5969
+ ariaLabel: openLabel,
5970
+ tooltip: displayName,
5932
5971
  onClick: () => toolbarContext.toggleTool(this.toolId),
5933
5972
  active: toolbarContext.isToolVisible(fullToolId)
5934
5973
  };
@@ -5956,8 +5995,9 @@ var calculatorToolRegistration = {
5956
5995
  sync: () => {
5957
5996
  const active = toolbarContext.isToolVisible(fullToolId);
5958
5997
  button.active = active;
5959
- button.ariaLabel = active ? "Close scientific calculator" : "Open scientific calculator";
5960
- button.tooltip = active ? "Close calculator" : "Calculator";
5998
+ button.label = displayName;
5999
+ button.ariaLabel = active ? closeLabel : openLabel;
6000
+ button.tooltip = active ? `Close ${displayName.toLowerCase()}` : displayName;
5961
6001
  if (lastVisibleState !== active) {
5962
6002
  overlay.visible = active;
5963
6003
  lastVisibleState = active;
@@ -5965,6 +6005,7 @@ var calculatorToolRegistration = {
5965
6005
  if (overlay.toolkitCoordinator !== toolbarContext.toolkitCoordinator) {
5966
6006
  overlay.toolkitCoordinator = toolbarContext.toolkitCoordinator;
5967
6007
  }
6008
+ applyCalculatorParamsToElement(overlay, calculatorType, availableTypes);
5968
6009
  },
5969
6010
  subscribeActive: (callback) => {
5970
6011
  if (!toolbarContext.subscribeVisibility)
@@ -10145,6 +10186,8 @@ class ToolkitCoordinator {
10145
10186
  coordinatorReadyNotified = false;
10146
10187
  providerInitPromises = new Map;
10147
10188
  toolRegistry;
10189
+ toolContextResolvers = new Map;
10190
+ toolContextResolverChangeListeners = new Set;
10148
10191
  sectionControllers = new Map;
10149
10192
  sectionControllerKeys = new Map;
10150
10193
  sectionControllerInitPromises = new Map;
@@ -10204,6 +10247,7 @@ class ToolkitCoordinator {
10204
10247
  this.assessmentId = resolvedConfig.assessmentId;
10205
10248
  this.config = resolvedConfig;
10206
10249
  this.toolRegistry = resolvedConfig.toolRegistry ?? createPackagedToolRegistry();
10250
+ this.installToolContextResolvers(resolvedConfig.toolContextResolvers);
10207
10251
  this.hooks = resolvedConfig.hooks ?? {};
10208
10252
  this.lazyInit = config.lazyInit === true;
10209
10253
  this.pnpEnforcementOverride = this.resolveConfiguredPnpEnforcement();
@@ -11023,7 +11067,9 @@ class ToolkitCoordinator {
11023
11067
  finish();
11024
11068
  };
11025
11069
  if (typeof synth.addEventListener === "function" && typeof synth.removeEventListener === "function") {
11026
- synth.addEventListener("voiceschanged", onVoicesChanged, { once: true });
11070
+ synth.addEventListener("voiceschanged", onVoicesChanged, {
11071
+ once: true
11072
+ });
11027
11073
  }
11028
11074
  });
11029
11075
  const voicesAfterWait = synth.getVoices();
@@ -11099,6 +11145,33 @@ class ToolkitCoordinator {
11099
11145
  throw new Error(`Unknown tool id "${toolId}".`);
11100
11146
  }
11101
11147
  }
11148
+ installToolContextResolvers(resolvers) {
11149
+ if (!resolvers)
11150
+ return;
11151
+ for (const [toolId, resolver] of Object.entries(resolvers)) {
11152
+ if (resolver == null)
11153
+ continue;
11154
+ this.assertCanonicalToolId(toolId);
11155
+ if (typeof resolver !== "function") {
11156
+ throw new Error(`Invalid tool context resolver for "${toolId}": expected a function.`);
11157
+ }
11158
+ this.toolContextResolvers.set(toolId, resolver);
11159
+ }
11160
+ }
11161
+ setToolContextResolvers(resolvers) {
11162
+ this.toolContextResolvers.clear();
11163
+ this.installToolContextResolvers(resolvers ?? undefined);
11164
+ this.notifyToolContextResolverChange();
11165
+ }
11166
+ notifyToolContextResolverChange() {
11167
+ for (const listener of this.toolContextResolverChangeListeners) {
11168
+ try {
11169
+ listener();
11170
+ } catch (error) {
11171
+ console.warn("[ToolkitCoordinator] Tool context resolver listener threw:", error);
11172
+ }
11173
+ }
11174
+ }
11102
11175
  isToolEnabled(toolId) {
11103
11176
  this.assertCanonicalToolId(toolId);
11104
11177
  const toolConfig = this.config.tools?.providers?.[toolId];
@@ -11225,6 +11298,62 @@ class ToolkitCoordinator {
11225
11298
  registerPolicySource(source2) {
11226
11299
  return this.policyEngine.registerPolicySource(source2);
11227
11300
  }
11301
+ registerToolContextResolver(toolId, resolver) {
11302
+ this.assertCanonicalToolId(toolId);
11303
+ if (typeof resolver !== "function") {
11304
+ throw new Error(`Invalid tool context resolver for "${toolId}": expected a function.`);
11305
+ }
11306
+ this.toolContextResolvers.set(toolId, resolver);
11307
+ this.notifyToolContextResolverChange();
11308
+ return () => {
11309
+ if (this.toolContextResolvers.get(toolId) !== resolver)
11310
+ return;
11311
+ this.toolContextResolvers.delete(toolId);
11312
+ this.notifyToolContextResolverChange();
11313
+ };
11314
+ }
11315
+ hasToolContextResolver(toolId) {
11316
+ this.assertCanonicalToolId(toolId);
11317
+ return this.toolContextResolvers.has(toolId);
11318
+ }
11319
+ resolveToolContext(context) {
11320
+ this.assertCanonicalToolId(context.toolId);
11321
+ const resolver = this.toolContextResolvers.get(context.toolId);
11322
+ if (!resolver)
11323
+ return null;
11324
+ let result;
11325
+ try {
11326
+ result = resolver(context);
11327
+ } catch (error) {
11328
+ console.error(`[ToolkitCoordinator] Tool context resolver for "${context.toolId}" threw:`, error);
11329
+ return {
11330
+ toolId: context.toolId,
11331
+ visible: false,
11332
+ params: {},
11333
+ reason: "Tool context resolver threw."
11334
+ };
11335
+ }
11336
+ if (!result) {
11337
+ return {
11338
+ toolId: context.toolId,
11339
+ visible: true,
11340
+ params: {}
11341
+ };
11342
+ }
11343
+ const params = result.params && typeof result.params === "object" && !Array.isArray(result.params) ? result.params : {};
11344
+ return {
11345
+ toolId: context.toolId,
11346
+ visible: result.visible !== false,
11347
+ params,
11348
+ reason: typeof result.reason === "string" ? result.reason : undefined
11349
+ };
11350
+ }
11351
+ onToolContextResolverChange(listener) {
11352
+ this.toolContextResolverChangeListeners.add(listener);
11353
+ return () => {
11354
+ this.toolContextResolverChangeListeners.delete(listener);
11355
+ };
11356
+ }
11228
11357
  resolveEffectivePnpEnforcement() {
11229
11358
  if (this.pnpEnforcementOverride !== null) {
11230
11359
  return this.pnpEnforcementOverride;
@@ -12406,11 +12535,12 @@ function PieAssessmentToolkit($$anchor, $$props) {
12406
12535
  preloaded: "pie-item-player",
12407
12536
  custom: ""
12408
12537
  };
12409
- let assessmentId = prop($$props, "assessmentId", 7, ""), section = prop($$props, "section", 7, null), sectionId = prop($$props, "sectionId", 7, ""), attemptId = prop($$props, "attemptId", 7, ""), env = prop($$props, "env", 23, () => ({})), lazyInit = prop($$props, "lazyInit", 7, true), toolConfigStrictness = prop($$props, "toolConfigStrictness", 7, "error"), tools = prop($$props, "tools", 23, () => ({})), enabledTools = prop($$props, "enabledTools", 7, ""), toolRegistry = prop($$props, "toolRegistry", 7, null), accessibility = prop($$props, "accessibility", 23, () => ({})), player = prop($$props, "player", 7, null), playerType = prop($$props, "playerType", 7, ""), coordinator = prop($$props, "coordinator", 7, null), createSectionController = prop($$props, "createSectionController", 7, null), onFrameworkError = prop($$props, "onFrameworkError", 7, null), errorRenderer = prop($$props, "errorRenderer", 7, null), onStageChange = prop($$props, "onStageChange", 7, null), assessment = prop($$props, "assessment", 7, null), currentItemRef = prop($$props, "currentItemRef", 7, null), pnpEnforcement = prop($$props, "pnpEnforcement", 7, null), isolation = prop($$props, "isolation", 7, "inherit");
12538
+ let assessmentId = prop($$props, "assessmentId", 7, ""), section = prop($$props, "section", 7, null), sectionId = prop($$props, "sectionId", 7, ""), attemptId = prop($$props, "attemptId", 7, ""), env = prop($$props, "env", 23, () => ({})), lazyInit = prop($$props, "lazyInit", 7, true), toolConfigStrictness = prop($$props, "toolConfigStrictness", 7, "error"), tools = prop($$props, "tools", 23, () => ({})), toolContextResolvers = prop($$props, "toolContextResolvers", 7, null), enabledTools = prop($$props, "enabledTools", 7, ""), toolRegistry = prop($$props, "toolRegistry", 7, null), accessibility = prop($$props, "accessibility", 23, () => ({})), player = prop($$props, "player", 7, null), playerType = prop($$props, "playerType", 7, ""), coordinator = prop($$props, "coordinator", 7, null), createSectionController = prop($$props, "createSectionController", 7, null), onFrameworkError = prop($$props, "onFrameworkError", 7, null), errorRenderer = prop($$props, "errorRenderer", 7, null), onStageChange = prop($$props, "onStageChange", 7, null), assessment = prop($$props, "assessment", 7, null), currentItemRef = prop($$props, "currentItemRef", 7, null), pnpEnforcement = prop($$props, "pnpEnforcement", 7, null), isolation = prop($$props, "isolation", 7, "inherit");
12410
12539
  let anchor = state(null);
12411
12540
  let ownedCoordinator = state(null);
12412
12541
  let inheritedRuntime = state(null);
12413
12542
  let lastOwnership = null;
12543
+ let lastAppliedToolContextResolvers = null;
12414
12544
  let provider = null;
12415
12545
  let contextRoot = null;
12416
12546
  let hostRuntimeProvider = null;
@@ -12681,15 +12811,22 @@ function PieAssessmentToolkit($$anchor, $$props) {
12681
12811
  }
12682
12812
  function getOwnedBootstrapFailureKey() {
12683
12813
  let toolsSignature = "";
12814
+ let toolContextResolverSignature = "";
12684
12815
  try {
12685
12816
  toolsSignature = JSON.stringify(buildEffectiveToolsInput());
12686
12817
  } catch {
12687
12818
  toolsSignature = "[unserializable-tools]";
12688
12819
  }
12820
+ try {
12821
+ toolContextResolverSignature = JSON.stringify(Object.entries(toolContextResolvers() ?? {}).map(([toolId, resolver]) => [toolId, typeof resolver]));
12822
+ } catch {
12823
+ toolContextResolverSignature = "[unserializable-resolvers]";
12824
+ }
12689
12825
  return [
12690
12826
  assessmentId() || "",
12691
12827
  String(toolConfigStrictness() || "error"),
12692
- toolsSignature
12828
+ toolsSignature,
12829
+ toolContextResolverSignature
12693
12830
  ].join("|");
12694
12831
  }
12695
12832
  function coercePnpEnforcement(value) {
@@ -12715,6 +12852,7 @@ function PieAssessmentToolkit($$anchor, $$props) {
12715
12852
  deferToolConfigValidation: true,
12716
12853
  tools: validatedTools,
12717
12854
  toolRegistry: toolRegistry(),
12855
+ toolContextResolvers: toolContextResolvers(),
12718
12856
  accessibility: accessibility(),
12719
12857
  frameworkErrorBus
12720
12858
  });
@@ -12730,6 +12868,7 @@ function PieAssessmentToolkit($$anchor, $$props) {
12730
12868
  coordinator();
12731
12869
  isolation();
12732
12870
  get2(inheritedRuntime);
12871
+ toolContextResolvers();
12733
12872
  untrack(() => {
12734
12873
  if (!get2(host))
12735
12874
  return;
@@ -12737,12 +12876,24 @@ function PieAssessmentToolkit($$anchor, $$props) {
12737
12876
  if (get2(ownedCoordinator)) {
12738
12877
  set(ownedCoordinator, null);
12739
12878
  }
12879
+ lastAppliedToolContextResolvers = null;
12740
12880
  return;
12741
12881
  }
12742
12882
  if (isolation() !== "force" && get2(inheritedRuntime)?.coordinator) {
12743
12883
  if (get2(ownedCoordinator)) {
12744
12884
  set(ownedCoordinator, null);
12745
12885
  }
12886
+ lastAppliedToolContextResolvers = null;
12887
+ return;
12888
+ }
12889
+ if (get2(ownedCoordinator) && lastAppliedToolContextResolvers !== toolContextResolvers()) {
12890
+ try {
12891
+ get2(ownedCoordinator).setToolContextResolvers(toolContextResolvers());
12892
+ lastAppliedToolContextResolvers = toolContextResolvers();
12893
+ } catch (error) {
12894
+ set(runtimeError, error, true);
12895
+ reportFrameworkError({ kind: "coordinator-init", phase: "coordinator-ready", error });
12896
+ }
12746
12897
  return;
12747
12898
  }
12748
12899
  if (!get2(ownedCoordinator)) {
@@ -12753,6 +12904,7 @@ function PieAssessmentToolkit($$anchor, $$props) {
12753
12904
  try {
12754
12905
  const validatedTools = validateToolsConfigForBootstrap();
12755
12906
  set(ownedCoordinator, buildOwnedCoordinator(validatedTools), true);
12907
+ lastAppliedToolContextResolvers = toolContextResolvers();
12756
12908
  lastOwnedBootstrapFailureKey = "";
12757
12909
  set(frameworkErrorModel, null);
12758
12910
  set(frameworkErrorTitle, "Unable to initialize assessment toolkit.");
@@ -13355,6 +13507,13 @@ function PieAssessmentToolkit($$anchor, $$props) {
13355
13507
  tools($$value);
13356
13508
  flushSync();
13357
13509
  },
13510
+ get toolContextResolvers() {
13511
+ return toolContextResolvers();
13512
+ },
13513
+ set toolContextResolvers($$value) {
13514
+ toolContextResolvers($$value);
13515
+ flushSync();
13516
+ },
13358
13517
  get enabledTools() {
13359
13518
  return enabledTools();
13360
13519
  },
@@ -13504,6 +13663,7 @@ __pieDefineSafely("pie-assessment-toolkit", create_custom_element(PieAssessmentT
13504
13663
  lazyInit: { attribute: "lazy-init", type: "Boolean" },
13505
13664
  toolConfigStrictness: { attribute: "tool-config-strictness", type: "String" },
13506
13665
  tools: { attribute: "tools", type: "Object" },
13666
+ toolContextResolvers: { type: "Object" },
13507
13667
  enabledTools: { attribute: "enabled-tools", type: "String" },
13508
13668
  toolRegistry: { type: "Object" },
13509
13669
  accessibility: { type: "Object" },