@pie-players/pie-assessment-toolkit 0.3.30 → 0.3.32

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.
Files changed (179) hide show
  1. package/README.md +289 -5
  2. package/dist/components/ItemToolBar.custom-element.js +165 -92
  3. package/dist/components/PieAssessmentToolkit.custom-element.js +604 -107
  4. package/dist/components/SectionToolBar.custom-element.js +0 -36
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/policy/core/PolicySource.d.ts +64 -0
  9. package/dist/policy/core/PolicySource.d.ts.map +1 -0
  10. package/dist/policy/core/PolicySource.js +17 -0
  11. package/dist/policy/core/PolicySource.js.map +1 -0
  12. package/dist/policy/core/ToolPolicyEngine.d.ts +123 -0
  13. package/dist/policy/core/ToolPolicyEngine.d.ts.map +1 -0
  14. package/dist/policy/core/ToolPolicyEngine.js +217 -0
  15. package/dist/policy/core/ToolPolicyEngine.js.map +1 -0
  16. package/dist/policy/core/compose-decision.d.ts +37 -0
  17. package/dist/policy/core/compose-decision.d.ts.map +1 -0
  18. package/dist/policy/core/compose-decision.js +332 -0
  19. package/dist/policy/core/compose-decision.js.map +1 -0
  20. package/dist/policy/core/decision-types.d.ts +142 -0
  21. package/dist/policy/core/decision-types.d.ts.map +1 -0
  22. package/dist/policy/core/decision-types.js +13 -0
  23. package/dist/policy/core/decision-types.js.map +1 -0
  24. package/dist/policy/core/engine-context.d.ts +30 -0
  25. package/dist/policy/core/engine-context.d.ts.map +1 -0
  26. package/dist/policy/core/engine-context.js +28 -0
  27. package/dist/policy/core/engine-context.js.map +1 -0
  28. package/dist/policy/core/policy-source-tag.d.ts +32 -0
  29. package/dist/policy/core/policy-source-tag.d.ts.map +1 -0
  30. package/dist/policy/core/policy-source-tag.js +29 -0
  31. package/dist/policy/core/policy-source-tag.js.map +1 -0
  32. package/dist/policy/core/provenance.d.ts +163 -0
  33. package/dist/policy/core/provenance.d.ts.map +1 -0
  34. package/dist/policy/core/provenance.js +232 -0
  35. package/dist/policy/core/provenance.js.map +1 -0
  36. package/dist/policy/core/qti-inputs.d.ts +67 -0
  37. package/dist/policy/core/qti-inputs.d.ts.map +1 -0
  38. package/dist/policy/core/qti-inputs.js +139 -0
  39. package/dist/policy/core/qti-inputs.js.map +1 -0
  40. package/dist/policy/engine.d.ts +28 -0
  41. package/dist/policy/engine.d.ts.map +1 -0
  42. package/dist/policy/engine.js +24 -0
  43. package/dist/policy/engine.js.map +1 -0
  44. package/dist/policy/internal.d.ts +26 -0
  45. package/dist/policy/internal.d.ts.map +1 -0
  46. package/dist/policy/internal.js +26 -0
  47. package/dist/policy/internal.js.map +1 -0
  48. package/dist/policy/sources/QtiPolicySource.d.ts +146 -0
  49. package/dist/policy/sources/QtiPolicySource.d.ts.map +1 -0
  50. package/dist/policy/sources/QtiPolicySource.js +270 -0
  51. package/dist/policy/sources/QtiPolicySource.js.map +1 -0
  52. package/dist/runtime/SectionRuntimeEngine.d.ts +145 -0
  53. package/dist/runtime/SectionRuntimeEngine.d.ts.map +1 -1
  54. package/dist/runtime/SectionRuntimeEngine.js +164 -3
  55. package/dist/runtime/SectionRuntimeEngine.js.map +1 -1
  56. package/dist/runtime/adapter/SectionEngineAdapter.d.ts +144 -0
  57. package/dist/runtime/adapter/SectionEngineAdapter.d.ts.map +1 -0
  58. package/dist/runtime/adapter/SectionEngineAdapter.js +183 -0
  59. package/dist/runtime/adapter/SectionEngineAdapter.js.map +1 -0
  60. package/dist/runtime/adapter/coordinator-bridge.d.ts +93 -0
  61. package/dist/runtime/adapter/coordinator-bridge.d.ts.map +1 -0
  62. package/dist/runtime/adapter/coordinator-bridge.js +127 -0
  63. package/dist/runtime/adapter/coordinator-bridge.js.map +1 -0
  64. package/dist/runtime/adapter/dom-event-bridge.d.ts +63 -0
  65. package/dist/runtime/adapter/dom-event-bridge.d.ts.map +1 -0
  66. package/dist/runtime/adapter/dom-event-bridge.js +95 -0
  67. package/dist/runtime/adapter/dom-event-bridge.js.map +1 -0
  68. package/dist/runtime/adapter/framework-error-bridge.d.ts +37 -0
  69. package/dist/runtime/adapter/framework-error-bridge.d.ts.map +1 -0
  70. package/dist/runtime/adapter/framework-error-bridge.js +47 -0
  71. package/dist/runtime/adapter/framework-error-bridge.js.map +1 -0
  72. package/dist/runtime/adapter/instrumentation-bridge.d.ts +32 -0
  73. package/dist/runtime/adapter/instrumentation-bridge.d.ts.map +1 -0
  74. package/dist/runtime/adapter/instrumentation-bridge.js +39 -0
  75. package/dist/runtime/adapter/instrumentation-bridge.js.map +1 -0
  76. package/dist/runtime/adapter/subscriber-fanout.d.ts +35 -0
  77. package/dist/runtime/adapter/subscriber-fanout.d.ts.map +1 -0
  78. package/dist/runtime/adapter/subscriber-fanout.js +60 -0
  79. package/dist/runtime/adapter/subscriber-fanout.js.map +1 -0
  80. package/dist/runtime/core/SectionEngineCore.d.ts +52 -0
  81. package/dist/runtime/core/SectionEngineCore.d.ts.map +1 -0
  82. package/dist/runtime/core/SectionEngineCore.js +81 -0
  83. package/dist/runtime/core/SectionEngineCore.js.map +1 -0
  84. package/dist/runtime/core/cohort.d.ts +40 -0
  85. package/dist/runtime/core/cohort.d.ts.map +1 -0
  86. package/dist/runtime/core/cohort.js +49 -0
  87. package/dist/runtime/core/cohort.js.map +1 -0
  88. package/dist/runtime/core/engine-input.d.ts +114 -0
  89. package/dist/runtime/core/engine-input.d.ts.map +1 -0
  90. package/dist/runtime/core/engine-input.js +19 -0
  91. package/dist/runtime/core/engine-input.js.map +1 -0
  92. package/dist/runtime/core/engine-output.d.ts +62 -0
  93. package/dist/runtime/core/engine-output.d.ts.map +1 -0
  94. package/dist/runtime/core/engine-output.js +27 -0
  95. package/dist/runtime/core/engine-output.js.map +1 -0
  96. package/dist/runtime/core/engine-readiness.d.ts +49 -0
  97. package/dist/runtime/core/engine-readiness.d.ts.map +1 -0
  98. package/dist/runtime/core/engine-readiness.js +47 -0
  99. package/dist/runtime/core/engine-readiness.js.map +1 -0
  100. package/dist/runtime/core/engine-resolver.d.ts +172 -0
  101. package/dist/runtime/core/engine-resolver.d.ts.map +1 -0
  102. package/dist/runtime/core/engine-resolver.js +170 -0
  103. package/dist/runtime/core/engine-resolver.js.map +1 -0
  104. package/dist/runtime/core/engine-stage-derivation.d.ts +19 -0
  105. package/dist/runtime/core/engine-stage-derivation.d.ts.map +1 -0
  106. package/dist/runtime/core/engine-stage-derivation.js +30 -0
  107. package/dist/runtime/core/engine-stage-derivation.js.map +1 -0
  108. package/dist/runtime/core/engine-state.d.ts +83 -0
  109. package/dist/runtime/core/engine-state.d.ts.map +1 -0
  110. package/dist/runtime/core/engine-state.js +49 -0
  111. package/dist/runtime/core/engine-state.js.map +1 -0
  112. package/dist/runtime/core/engine-transition.d.ts +49 -0
  113. package/dist/runtime/core/engine-transition.d.ts.map +1 -0
  114. package/dist/runtime/core/engine-transition.js +261 -0
  115. package/dist/runtime/core/engine-transition.js.map +1 -0
  116. package/dist/runtime/engine-context.d.ts +40 -0
  117. package/dist/runtime/engine-context.d.ts.map +1 -0
  118. package/dist/runtime/engine-context.js +32 -0
  119. package/dist/runtime/engine-context.js.map +1 -0
  120. package/dist/runtime/engine.d.ts +24 -0
  121. package/dist/runtime/engine.d.ts.map +1 -0
  122. package/dist/runtime/engine.js +24 -0
  123. package/dist/runtime/engine.js.map +1 -0
  124. package/dist/runtime/internal.d.ts +44 -0
  125. package/dist/runtime/internal.d.ts.map +1 -0
  126. package/dist/runtime/internal.js +46 -0
  127. package/dist/runtime/internal.js.map +1 -0
  128. package/dist/runtime/runtime-id.d.ts +8 -0
  129. package/dist/runtime/runtime-id.d.ts.map +1 -0
  130. package/dist/runtime/runtime-id.js +10 -0
  131. package/dist/runtime/runtime-id.js.map +1 -0
  132. package/dist/runtime/section-runtime-engine-host-context.d.ts +74 -0
  133. package/dist/runtime/section-runtime-engine-host-context.d.ts.map +1 -0
  134. package/dist/runtime/section-runtime-engine-host-context.js +96 -0
  135. package/dist/runtime/section-runtime-engine-host-context.js.map +1 -0
  136. package/dist/runtime/stage-emit-gate.d.ts +93 -0
  137. package/dist/runtime/stage-emit-gate.d.ts.map +1 -0
  138. package/dist/runtime/stage-emit-gate.js +83 -0
  139. package/dist/runtime/stage-emit-gate.js.map +1 -0
  140. package/dist/services/ToolkitCoordinator.d.ts +280 -7
  141. package/dist/services/ToolkitCoordinator.d.ts.map +1 -1
  142. package/dist/services/ToolkitCoordinator.js +412 -32
  143. package/dist/services/ToolkitCoordinator.js.map +1 -1
  144. package/dist/services/framework-error-bus.d.ts +73 -0
  145. package/dist/services/framework-error-bus.d.ts.map +1 -0
  146. package/dist/services/framework-error-bus.js +68 -0
  147. package/dist/services/framework-error-bus.js.map +1 -0
  148. package/dist/services/framework-error.d.ts +36 -1
  149. package/dist/services/framework-error.d.ts.map +1 -1
  150. package/dist/services/framework-error.js +37 -0
  151. package/dist/services/framework-error.js.map +1 -1
  152. package/dist/services/interfaces.d.ts +78 -0
  153. package/dist/services/interfaces.d.ts.map +1 -1
  154. package/dist/services/interfaces.js.map +1 -1
  155. package/dist/services/section-controller-types.d.ts +20 -0
  156. package/dist/services/section-controller-types.d.ts.map +1 -1
  157. package/dist/services/tools-config-normalizer.d.ts +23 -0
  158. package/dist/services/tools-config-normalizer.d.ts.map +1 -1
  159. package/dist/services/tools-config-normalizer.js +13 -1
  160. package/dist/services/tools-config-normalizer.js.map +1 -1
  161. package/dist/tools/client.d.ts +0 -1
  162. package/dist/tools/client.d.ts.map +1 -1
  163. package/dist/tools/client.js +0 -2
  164. package/dist/tools/client.js.map +1 -1
  165. package/dist/tools/types.d.ts +0 -26
  166. package/dist/tools/types.d.ts.map +1 -1
  167. package/package.json +23 -7
  168. package/dist/runtime/runtime-event-guards.d.ts +0 -2
  169. package/dist/runtime/runtime-event-guards.d.ts.map +0 -1
  170. package/dist/runtime/runtime-event-guards.js +0 -4
  171. package/dist/runtime/runtime-event-guards.js.map +0 -1
  172. package/dist/tools/tool-coordinator.d.ts +0 -72
  173. package/dist/tools/tool-coordinator.d.ts.map +0 -1
  174. package/dist/tools/tool-coordinator.js +0 -194
  175. package/dist/tools/tool-coordinator.js.map +0 -1
  176. package/dist/types/events.d.ts +0 -157
  177. package/dist/types/events.d.ts.map +0 -1
  178. package/dist/types/events.js +0 -11
  179. package/dist/types/events.js.map +0 -1
@@ -33,7 +33,7 @@ import { sanitizeSvgIcon } from '@pie-players/pie-players-shared/security';
33
33
  import { createFocusTrap } from '@pie-players/pie-players-shared';
34
34
  import { createPackagedToolRegistry } from '../services/createDefaultToolRegistry.js';
35
35
  import { DEFAULT_TOOL_MODULE_LOADERS } from '../tools/default-tool-module-loaders.js';
36
- import { normalizeToolsConfig, parseToolList, resolveToolsForLevel } from '../services/tools-config-normalizer.js';
36
+ import { parseToolList } from '../services/tools-config-normalizer.js';
37
37
  import { createScopedToolId, parseScopedToolId } from '../services/tool-instance-id.js';
38
38
 
39
39
  var root_4 = $.from_html(`<span class="item-toolbar__element-host svelte-rnx3ie"></span>`);
@@ -79,9 +79,6 @@ export default function ItemToolBar($$anchor, $$props) {
79
79
  position = $.prop($$props, 'position', 7, 'bottom'),
80
80
  scopeElement = $.prop($$props, 'scopeElement', 7, null),
81
81
  toolRegistry = $.prop($$props, 'toolRegistry', 7, null),
82
- pnpResolver = $.prop($$props, 'pnpResolver', 7, null),
83
- assessment = $.prop($$props, 'assessment', 7, null),
84
- itemRef = $.prop($$props, 'itemRef', 7, null),
85
82
  item = $.prop($$props, 'item', 7, null),
86
83
  hostButtons = $.prop($$props, 'hostButtons', 23, () => []),
87
84
  className = $.prop($$props, 'class', 7, ''),
@@ -93,6 +90,14 @@ export default function ItemToolBar($$anchor, $$props) {
93
90
  let shellContext = $.state(null);
94
91
  let moduleLoadVersion = $.state(0);
95
92
 
93
+ // Bumped from `coordinator.onPolicyChange(...)` so the engine-driven
94
+ // `policyDecision` / `policyInputs` derivations rerun on input
95
+ // changes (`updateAssessment`, `setQtiEnforcement`, custom source
96
+ // register / remove). The decision itself is computed by the
97
+ // coordinator's policy engine — this counter is just the reactive
98
+ // fanout that lets Svelte know the engine answer may have changed.
99
+ let policyChangeVersion = $.state(0);
100
+
96
101
  $.user_effect(() => {
97
102
  if (!$.get(toolbarRootElement)) return;
98
103
 
@@ -109,6 +114,25 @@ export default function ItemToolBar($$anchor, $$props) {
109
114
  });
110
115
  });
111
116
 
117
+ $.user_effect(() => {
118
+ const coord = $.get(runtimeContext)?.toolkitCoordinator;
119
+
120
+ if (!coord || typeof coord.onPolicyChange !== 'function') return;
121
+
122
+ const unsubscribe = coord.onPolicyChange(() => {
123
+ $.set(policyChangeVersion, $.get(policyChangeVersion) + 1);
124
+ });
125
+
126
+ return () => {
127
+ try {
128
+ unsubscribe?.();
129
+ } catch {
130
+ // detach errors are non-fatal: the coordinator may already
131
+ // be torn down, in which case the listener set is GC-eligible.
132
+ }
133
+ };
134
+ });
135
+
112
136
  const effectiveToolCoordinator = $.derived(() => $.get(runtimeContext)?.toolCoordinator);
113
137
  const effectiveTTSService = $.derived(() => $.get(runtimeContext)?.ttsService);
114
138
  const effectiveElementToolStateStore = $.derived(() => $.get(runtimeContext)?.elementToolStateStore);
@@ -142,12 +166,6 @@ export default function ItemToolBar($$anchor, $$props) {
142
166
  const explicitTools = $.derived(() => parseToolList(tools()));
143
167
  const normalizedExplicitTools = $.derived(() => $.get(effectiveToolRegistry).normalizeToolIds($.get(explicitTools)).filter(Boolean));
144
168
 
145
- const effectiveToolsConfig = $.derived(() => {
146
- const coordinatorConfig = $.get(runtimeContext)?.toolkitCoordinator?.config?.tools;
147
-
148
- return normalizeToolsConfig(coordinatorConfig || {});
149
- });
150
-
151
169
  const placementLevel = $.derived(() => {
152
170
  if ($.get(effectiveLevel) === 'section') return 'section';
153
171
  if ($.get(effectiveLevel) === 'passage' || $.get(effectiveContentKind) === 'rubric-block-stimulus') return 'passage';
@@ -155,48 +173,58 @@ export default function ItemToolBar($$anchor, $$props) {
155
173
  return 'item';
156
174
  });
157
175
 
158
- const placementAllowedToolIds = $.derived(() => {
159
- const resolved = resolveToolsForLevel($.get(effectiveToolsConfig), $.get(placementLevel));
160
-
161
- return $.get(effectiveToolRegistry).normalizeToolIds(resolved).filter(Boolean);
162
- });
163
-
164
- const providerConfig = $.derived(() => $.get(effectiveToolsConfig)?.providers || {});
165
-
166
- const normalizedProviderConfig = $.derived(() => {
167
- const normalized = {};
168
-
169
- for (const [toolId, config] of Object.entries($.get(providerConfig))) {
170
- const canonicalToolId = $.get(effectiveToolRegistry).normalizeToolId(toolId);
171
-
172
- if (!canonicalToolId) continue;
173
-
174
- normalized[canonicalToolId] = config;
176
+ // Pass 1 single source of truth via the ToolPolicyEngine
177
+ // (M8 PR 3). When the toolbar is mounted under
178
+ // `<pie-assessment-toolkit>` the coordinator owns the engine and
179
+ // applies `placement → policy.allowed → policy.blocked →
180
+ // providers → QTI` plus any custom `PolicySource`s the host has
181
+ // registered. The toolbar trusts that decision verbatim — the
182
+ // `tools=` prop is *not* applied as a downstream filter.
183
+ //
184
+ // The `tools=` attribute is only consulted in the standalone
185
+ // fallback path (no coordinator in scope, e.g. fixtures that
186
+ // mount `<pie-item-toolbar>` directly). Hosts that want to
187
+ // restrict the engine's visible set should configure
188
+ // `tools.policy` / `tools.providers` on the assessment-toolkit
189
+ // inputs, or register a custom `PolicySource`, rather than
190
+ // passing `tools=` here.
191
+ const policyDecision = $.derived(() => {
192
+ // Read `policyChangeVersion` so we re-derive whenever the
193
+ // coordinator emits a policy change (assessment binding,
194
+ // QTI enforcement override, custom source registration).
195
+ void $.get(policyChangeVersion);
196
+
197
+ const coord = $.get(runtimeContext)?.toolkitCoordinator;
198
+
199
+ if (!coord || typeof coord.decideToolPolicy !== 'function') {
200
+ return null;
175
201
  }
176
202
 
177
- return normalized;
203
+ return coord.decideToolPolicy({
204
+ level: $.get(placementLevel),
205
+ scope: {
206
+ level: $.get(effectiveLevel),
207
+ scopeId: $.get(effectiveScopeId),
208
+ assessmentId: $.get(runtimeContext)?.assessmentId,
209
+ sectionId: $.get(effectiveSectionId) || undefined,
210
+ itemId: $.get(effectiveItemId) || undefined,
211
+ canonicalItemId: $.get(effectiveCanonicalItemId) || undefined,
212
+ contentKind: $.get(effectiveContentKind)
213
+ }
214
+ });
178
215
  });
179
216
 
180
- const isProviderEnabled = (toolId) => $.get(normalizedProviderConfig)[toolId]?.enabled !== false;
181
-
182
- // Pass 1: determine allowed tools
183
217
  const allowedToolIds = $.derived(() => {
184
- const configuredTools = $.get(normalizedExplicitTools).length > 0
185
- ? $.get(normalizedExplicitTools)
186
- : $.get(placementAllowedToolIds);
187
-
188
- const providerEnabledConfiguredTools = configuredTools.filter((toolId) => isProviderEnabled(toolId));
189
218
  const dedupe = (toolIds) => Array.from(new Set(toolIds));
190
219
 
191
- if (pnpResolver() && assessment() && itemRef()) {
192
- const allowedByPnp = dedupe($.get(effectiveToolRegistry).normalizeToolIds(pnpResolver().getAllowedToolIds(assessment(), itemRef()))).filter(Boolean);
193
-
194
- if (providerEnabledConfiguredTools.length === 0) return allowedByPnp;
195
-
196
- return dedupe(allowedByPnp.filter((toolId) => providerEnabledConfiguredTools.includes(toolId)));
220
+ if ($.get(policyDecision)) {
221
+ return dedupe($.get(effectiveToolRegistry).normalizeToolIds($.get(policyDecision).visibleTools.map((entry) => entry.toolId)).filter(Boolean));
197
222
  }
198
223
 
199
- return dedupe(providerEnabledConfiguredTools);
224
+ // Standalone fallback: no coordinator in scope. Honour the
225
+ // explicit `tools=` prop verbatim so demo fixtures keep
226
+ // working without a `<pie-assessment-toolkit>` ancestor.
227
+ return dedupe($.get(normalizedExplicitTools));
200
228
  });
201
229
 
202
230
  const contentReady = $.derived(() => {
@@ -207,17 +235,41 @@ export default function ItemToolBar($$anchor, $$props) {
207
235
  return !!($.get(effectiveItem) && config && typeof config === 'object');
208
236
  });
209
237
 
238
+ // QTI inputs (`assessment`, `currentItemRef`) live on the
239
+ // coordinator after M8 PR 2; the toolbar reads them through
240
+ // `getPolicyInputs()` so it can build the correct Pass-2 context
241
+ // without re-binding props. Standalone (no-coordinator) usage
242
+ // falls back to the empty / canonical-id-derived contexts that
243
+ // satisfy the `ToolContext` shape for `isVisibleInContext` calls.
244
+ const policyInputs = $.derived(() => {
245
+ void $.get(policyChangeVersion);
246
+
247
+ const coord = $.get(runtimeContext)?.toolkitCoordinator;
248
+
249
+ if (!coord || typeof coord.getPolicyInputs !== 'function') {
250
+ return null;
251
+ }
252
+
253
+ return coord.getPolicyInputs();
254
+ });
255
+
256
+ const effectiveAssessment = $.derived(() => $.get(policyInputs)?.assessment ?? null);
257
+ const effectiveItemRef = $.derived(() => $.get(policyInputs)?.currentItemRef ?? null);
258
+
210
259
  const toolContext = $.derived(() => {
211
260
  if ($.get(effectiveLevel) === 'section') {
212
261
  return {
213
262
  level: 'section',
214
- assessment: assessment() || {},
263
+ assessment: $.get(effectiveAssessment) || {},
215
264
  section: {}
216
265
  };
217
266
  }
218
267
 
219
268
  if ($.get(effectiveLevel) === 'assessment') {
220
- return { level: 'assessment', assessment: assessment() || {} };
269
+ return {
270
+ level: 'assessment',
271
+ assessment: $.get(effectiveAssessment) || {}
272
+ };
221
273
  }
222
274
 
223
275
  if (!$.get(contentReady) || !$.get(effectiveItem)) {
@@ -227,16 +279,16 @@ export default function ItemToolBar($$anchor, $$props) {
227
279
  if ($.get(effectiveLevel) === 'passage') {
228
280
  return {
229
281
  level: 'passage',
230
- assessment: assessment() || {},
231
- itemRef: itemRef() || { id: $.get(effectiveCanonicalItemId) },
282
+ assessment: $.get(effectiveAssessment) || {},
283
+ itemRef: $.get(effectiveItemRef) || { id: $.get(effectiveCanonicalItemId) },
232
284
  passage: $.get(effectiveItem)
233
285
  };
234
286
  }
235
287
 
236
288
  return {
237
289
  level: 'item',
238
- assessment: assessment() || {},
239
- itemRef: itemRef() || { id: $.get(effectiveCanonicalItemId) },
290
+ assessment: $.get(effectiveAssessment) || {},
291
+ itemRef: $.get(effectiveItemRef) || { id: $.get(effectiveCanonicalItemId) },
240
292
  item: $.get(effectiveItem)
241
293
  };
242
294
  });
@@ -246,8 +298,8 @@ export default function ItemToolBar($$anchor, $$props) {
246
298
 
247
299
  return {
248
300
  level: 'item',
249
- assessment: assessment() || {},
250
- itemRef: itemRef() || { id: $.get(effectiveCanonicalItemId) },
301
+ assessment: $.get(effectiveAssessment) || {},
302
+ itemRef: $.get(effectiveItemRef) || { id: $.get(effectiveCanonicalItemId) },
251
303
  item: $.get(effectiveItem) || { id: $.get(effectiveCanonicalItemId), config: {} }
252
304
  };
253
305
  });
@@ -264,8 +316,8 @@ export default function ItemToolBar($$anchor, $$props) {
264
316
 
265
317
  return models.filter((model) => model && typeof model === 'object' && typeof model.id === 'string').map((model) => ({
266
318
  level: 'element',
267
- assessment: assessment() || {},
268
- itemRef: itemRef() || { id: $.get(effectiveCanonicalItemId) },
319
+ assessment: $.get(effectiveAssessment) || {},
320
+ itemRef: $.get(effectiveItemRef) || { id: $.get(effectiveCanonicalItemId) },
269
321
  item: $.get(effectiveItem),
270
322
  elementId: model.id
271
323
  }));
@@ -539,12 +591,32 @@ export default function ItemToolBar($$anchor, $$props) {
539
591
 
540
592
  for (const renderedTool of $.get(renderedTools)) {
541
593
  if (renderedTool.subscribeActive) {
594
+ let disposed = false;
595
+
542
596
  const unsubscribe = renderedTool.subscribeActive((active) => {
543
- $.set(activeToolState, { ...$.get(activeToolState), [renderedTool.toolId]: active }, true);
544
- renderedTool.sync?.();
597
+ // Tools may dispatch their active-change signal from inside
598
+ // a tracked Svelte $effect (e.g. tool-tts-inline does this
599
+ // on mount). When the toolbar synchronously creates / re-
600
+ // connects the tool element during its own template/
601
+ // derivation pass — for example when the section player
602
+ // collapses on resize — that dispatch lands back here while
603
+ // our parent derivation is still running, and Svelte
604
+ // rejects the `activeToolState` write with
605
+ // `state_unsafe_mutation`. Queue the update on a
606
+ // microtask so the mutation happens after the current
607
+ // reactive flush settles.
608
+ queueMicrotask(() => {
609
+ if (disposed) return;
610
+
611
+ $.set(activeToolState, { ...$.get(activeToolState), [renderedTool.toolId]: active }, true);
612
+ renderedTool.sync?.();
613
+ });
545
614
  });
546
615
 
547
- unsubs.push(unsubscribe);
616
+ unsubs.push(() => {
617
+ disposed = true;
618
+ unsubscribe();
619
+ });
548
620
  }
549
621
  }
550
622
 
@@ -556,9 +628,25 @@ export default function ItemToolBar($$anchor, $$props) {
556
628
  $.user_effect(() => {
557
629
  if (!$.get(toolbarContext).subscribeVisibility) return;
558
630
 
559
- return $.get(toolbarContext).subscribeVisibility(() => {
560
- syncRenderedToolsState();
631
+ let disposed = false;
632
+
633
+ const unsubscribe = $.get(toolbarContext).subscribeVisibility(() => {
634
+ // Defense-in-depth: defer the state write so a synchronous
635
+ // visibility broadcast from the coordinator never lands inside
636
+ // an active parent derivation/template flush. See the
637
+ // `subscribeActive` handler above for the failure mode this
638
+ // pattern prevents.
639
+ queueMicrotask(() => {
640
+ if (disposed) return;
641
+
642
+ syncRenderedToolsState();
643
+ });
561
644
  });
645
+
646
+ return () => {
647
+ disposed = true;
648
+ unsubscribe?.();
649
+ };
562
650
  });
563
651
 
564
652
  $.user_effect(() => {
@@ -566,13 +654,28 @@ export default function ItemToolBar($$anchor, $$props) {
566
654
 
567
655
  if (typeof toolkitCoordinator?.subscribeTelemetry !== 'function') return;
568
656
 
569
- return toolkitCoordinator.subscribeTelemetry(({ eventName, payload }) => {
570
- if (eventName !== 'tool-config-updated') return;
657
+ let disposed = false;
658
+
659
+ const unsubscribe = toolkitCoordinator.subscribeTelemetry(({ eventName, payload }) => {
660
+ if (eventName !== 'pie-toolkit-tool-config-updated') return;
571
661
  if (payload?.toolId && !$.get(toolbarVisibleToolIds).includes(payload.toolId)) return;
572
662
 
573
- $.set(moduleLoadVersion, $.get(moduleLoadVersion) + 1);
574
- syncRenderedToolsState();
663
+ // Defense-in-depth: telemetry can be emitted from anywhere in
664
+ // the toolkit lifecycle, including paths that flush
665
+ // synchronously while this component's deriveds are running.
666
+ // Defer the writes so the parent flush always settles first.
667
+ queueMicrotask(() => {
668
+ if (disposed) return;
669
+
670
+ $.set(moduleLoadVersion, $.get(moduleLoadVersion) + 1);
671
+ syncRenderedToolsState();
672
+ });
575
673
  });
674
+
675
+ return () => {
676
+ disposed = true;
677
+ unsubscribe?.();
678
+ };
576
679
  });
577
680
 
578
681
  function mountElement(node, element) {
@@ -1334,33 +1437,6 @@ export default function ItemToolBar($$anchor, $$props) {
1334
1437
  $.flush();
1335
1438
  },
1336
1439
 
1337
- get pnpResolver() {
1338
- return pnpResolver();
1339
- },
1340
-
1341
- set pnpResolver($$value) {
1342
- pnpResolver($$value);
1343
- $.flush();
1344
- },
1345
-
1346
- get assessment() {
1347
- return assessment();
1348
- },
1349
-
1350
- set assessment($$value) {
1351
- assessment($$value);
1352
- $.flush();
1353
- },
1354
-
1355
- get itemRef() {
1356
- return itemRef();
1357
- },
1358
-
1359
- set itemRef($$value) {
1360
- itemRef($$value);
1361
- $.flush();
1362
- },
1363
-
1364
1440
  get item() {
1365
1441
  return item();
1366
1442
  },
@@ -1796,9 +1872,6 @@ __pieDefineSafely('pie-item-toolbar', $.create_custom_element(
1796
1872
  language: { attribute: 'language', type: 'String' },
1797
1873
  scopeElement: { type: 'Object' },
1798
1874
  toolRegistry: { type: 'Object' },
1799
- pnpResolver: { type: 'Object' },
1800
- assessment: { type: 'Object' },
1801
- itemRef: { type: 'Object' },
1802
1875
  item: { type: 'Object' },
1803
1876
  hostButtons: { type: 'Object' },
1804
1877
  class: {}