@pie-players/pie-assessment-toolkit 0.2.9 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ItemToolBar.custom-element.js +425 -93
- package/dist/components/PieAssessmentToolkit.custom-element.js +200 -46
- package/dist/components/SectionToolBar.custom-element.js +162 -0
- package/dist/components/section-toolbar-element.d.ts +3 -0
- package/dist/components/section-toolbar-element.d.ts.map +1 -0
- package/dist/components/section-toolbar-element.js +2 -0
- package/dist/components/section-toolbar-element.js.map +1 -0
- package/dist/context/assessment-toolkit-context.d.ts +1 -1
- package/dist/context/assessment-toolkit-context.d.ts.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime/SectionRuntimeEngine.d.ts +57 -1
- package/dist/runtime/SectionRuntimeEngine.d.ts.map +1 -1
- package/dist/runtime/SectionRuntimeEngine.js +23 -0
- package/dist/runtime/SectionRuntimeEngine.js.map +1 -1
- package/dist/runtime/registration-events.d.ts +19 -0
- package/dist/runtime/registration-events.d.ts.map +1 -1
- package/dist/runtime/registration-events.js +3 -0
- package/dist/runtime/registration-events.js.map +1 -1
- package/dist/runtime/runtime-event-guards.d.ts +0 -1
- package/dist/runtime/runtime-event-guards.d.ts.map +1 -1
- package/dist/runtime/runtime-event-guards.js +0 -5
- package/dist/runtime/runtime-event-guards.js.map +1 -1
- package/dist/runtime/session-event-emitter-policy.d.ts +12 -0
- package/dist/runtime/session-event-emitter-policy.d.ts.map +1 -0
- package/dist/runtime/session-event-emitter-policy.js +57 -0
- package/dist/runtime/session-event-emitter-policy.js.map +1 -0
- package/dist/services/HighlightCoordinator.d.ts +9 -0
- package/dist/services/HighlightCoordinator.d.ts.map +1 -1
- package/dist/services/HighlightCoordinator.js +196 -55
- package/dist/services/HighlightCoordinator.js.map +1 -1
- package/dist/services/TTSService.d.ts +18 -0
- package/dist/services/TTSService.d.ts.map +1 -1
- package/dist/services/TTSService.js +343 -105
- package/dist/services/TTSService.js.map +1 -1
- package/dist/services/ToolRegistry.d.ts +40 -2
- package/dist/services/ToolRegistry.d.ts.map +1 -1
- package/dist/services/ToolRegistry.js +22 -1
- package/dist/services/ToolRegistry.js.map +1 -1
- package/dist/services/ToolkitCoordinator.d.ts +34 -1
- package/dist/services/ToolkitCoordinator.d.ts.map +1 -1
- package/dist/services/ToolkitCoordinator.js +203 -2
- package/dist/services/ToolkitCoordinator.js.map +1 -1
- package/dist/services/createDefaultToolRegistry.d.ts +5 -11
- package/dist/services/createDefaultToolRegistry.d.ts.map +1 -1
- package/dist/services/createDefaultToolRegistry.js +5 -48
- package/dist/services/createDefaultToolRegistry.js.map +1 -1
- package/dist/services/interfaces.d.ts +7 -2
- package/dist/services/interfaces.d.ts.map +1 -1
- package/dist/services/interfaces.js.map +1 -1
- package/dist/services/pnp-standard-features.d.ts +1 -1
- package/dist/services/section-controller-types.d.ts +5 -0
- package/dist/services/section-controller-types.d.ts.map +1 -1
- package/dist/services/tool-config-defaults.d.ts +14 -0
- package/dist/services/tool-config-defaults.d.ts.map +1 -0
- package/dist/services/tool-config-defaults.js +23 -0
- package/dist/services/tool-config-defaults.js.map +1 -0
- package/dist/services/tool-context.d.ts.map +1 -1
- package/dist/services/tool-context.js +50 -3
- package/dist/services/tool-context.js.map +1 -1
- package/dist/services/tool-instance-id.d.ts +1 -3
- package/dist/services/tool-instance-id.d.ts.map +1 -1
- package/dist/services/tool-instance-id.js +4 -10
- package/dist/services/tool-instance-id.js.map +1 -1
- package/dist/services/toolbar-items.d.ts +26 -0
- package/dist/services/toolbar-items.d.ts.map +1 -0
- package/dist/services/toolbar-items.js +10 -0
- package/dist/services/toolbar-items.js.map +1 -0
- package/dist/services/tools-config-normalizer.d.ts.map +1 -1
- package/dist/services/tools-config-normalizer.js +5 -6
- package/dist/services/tools-config-normalizer.js.map +1 -1
- package/dist/services/tts/browser-provider.d.ts.map +1 -1
- package/dist/services/tts/browser-provider.js +201 -34
- package/dist/services/tts/browser-provider.js.map +1 -1
- package/dist/services/tts/text-processing.d.ts +17 -0
- package/dist/services/tts/text-processing.d.ts.map +1 -0
- package/dist/services/tts/text-processing.js +144 -0
- package/dist/services/tts/text-processing.js.map +1 -0
- package/dist/tools/default-tool-module-loaders.d.ts.map +1 -1
- package/dist/tools/default-tool-module-loaders.js +1 -7
- package/dist/tools/default-tool-module-loaders.js.map +1 -1
- package/dist/tools/registrations/accessibility-tools.d.ts +7 -2
- package/dist/tools/registrations/accessibility-tools.d.ts.map +1 -1
- package/dist/tools/registrations/accessibility-tools.js +23 -15
- package/dist/tools/registrations/accessibility-tools.js.map +1 -1
- package/dist/tools/registrations/calculator.d.ts.map +1 -1
- package/dist/tools/registrations/calculator.js +18 -10
- package/dist/tools/registrations/calculator.js.map +1 -1
- package/dist/tools/registrations/interaction-tools.d.ts.map +1 -1
- package/dist/tools/registrations/interaction-tools.js +5 -4
- package/dist/tools/registrations/interaction-tools.js.map +1 -1
- package/dist/tools/registrations/measurement-tools.js +4 -4
- package/dist/tools/registrations/measurement-tools.js.map +1 -1
- package/dist/tools/registrations/subject-specific-tools.js +4 -4
- package/dist/tools/registrations/subject-specific-tools.js.map +1 -1
- package/dist/tools/registrations/tts.d.ts.map +1 -1
- package/dist/tools/registrations/tts.js +161 -17
- package/dist/tools/registrations/tts.js.map +1 -1
- package/dist/tools/tool-tag-map.d.ts.map +1 -1
- package/dist/tools/tool-tag-map.js +2 -1
- package/dist/tools/tool-tag-map.js.map +1 -1
- package/package.json +12 -8
- package/src/components/ItemToolBar.svelte +275 -47
- package/src/components/PieAssessmentToolkit.svelte +217 -53
- package/src/components/SectionToolBar.svelte +58 -0
- package/dist/services/tts/provider-interface.d.ts +0 -131
- package/dist/services/tts/provider-interface.d.ts.map +0 -1
- package/dist/services/tts/provider-interface.js +0 -10
- package/dist/services/tts/provider-interface.js.map +0 -1
- package/src/components/PNPProvenanceViewer.svelte +0 -270
|
@@ -38,18 +38,26 @@
|
|
|
38
38
|
import { connectAssessmentToolkitHostRuntimeContext } from "../context/runtime-context-consumer.js";
|
|
39
39
|
import { ToolkitCoordinator } from "../services/ToolkitCoordinator.js";
|
|
40
40
|
import {
|
|
41
|
-
|
|
41
|
+
PIE_INTERNAL_CONTENT_LOADED_EVENT,
|
|
42
|
+
PIE_INTERNAL_ITEM_SESSION_CHANGED_EVENT,
|
|
43
|
+
PIE_INTERNAL_ITEM_PLAYER_ERROR_EVENT,
|
|
42
44
|
PIE_REGISTER_EVENT,
|
|
43
45
|
PIE_UNREGISTER_EVENT,
|
|
44
|
-
type
|
|
46
|
+
type InternalContentLoadedDetail,
|
|
47
|
+
type InternalItemSessionChangedDetail,
|
|
48
|
+
type InternalItemPlayerErrorDetail,
|
|
45
49
|
type RuntimeRegistrationDetail,
|
|
46
50
|
} from "../runtime/registration-events.js";
|
|
47
51
|
import { dispatchCrossBoundaryEvent } from "../runtime/tool-host-contract.js";
|
|
48
52
|
import { SectionRuntimeEngine } from "../runtime/SectionRuntimeEngine.js";
|
|
49
53
|
import {
|
|
50
54
|
createRuntimeId,
|
|
51
|
-
shouldHandleBySourceRuntime,
|
|
52
55
|
} from "../runtime/runtime-event-guards.js";
|
|
56
|
+
import {
|
|
57
|
+
createSessionEmitPolicyState,
|
|
58
|
+
resetSessionEmitPolicyState,
|
|
59
|
+
shouldEmitCanonicalSessionEvent,
|
|
60
|
+
} from "../runtime/session-event-emitter-policy.js";
|
|
53
61
|
|
|
54
62
|
type SessionChangedLike = {
|
|
55
63
|
eventDetail?: unknown;
|
|
@@ -61,10 +69,14 @@
|
|
|
61
69
|
|
|
62
70
|
const runtimeId = createRuntimeId("toolkit");
|
|
63
71
|
const sectionEngine = new SectionRuntimeEngine();
|
|
72
|
+
const DEFAULT_ENV = {
|
|
73
|
+
mode: "gather",
|
|
74
|
+
role: "student",
|
|
75
|
+
} as const;
|
|
64
76
|
const DEFAULT_ITEM_PLAYER_BY_TYPE: Record<ItemPlayerType, string> = {
|
|
65
|
-
iife: "pie-
|
|
66
|
-
esm: "pie-
|
|
67
|
-
|
|
77
|
+
iife: "pie-item-player",
|
|
78
|
+
esm: "pie-item-player",
|
|
79
|
+
preloaded: "pie-item-player",
|
|
68
80
|
custom: "",
|
|
69
81
|
};
|
|
70
82
|
|
|
@@ -98,7 +110,11 @@
|
|
|
98
110
|
let compositionVersion = $state(0);
|
|
99
111
|
let compositionModel = $state<unknown>(null);
|
|
100
112
|
let runtimeError = $state<unknown>(null);
|
|
101
|
-
let
|
|
113
|
+
let lastCompositionRevisionKey = $state("");
|
|
114
|
+
let pendingCompositionModel: unknown = null;
|
|
115
|
+
let pendingCompositionEmit = false;
|
|
116
|
+
let compositionEmitRaf: number | null = null;
|
|
117
|
+
const sessionEmitPolicyState = createSessionEmitPolicyState();
|
|
102
118
|
|
|
103
119
|
function getHostElement(): HTMLElement | null {
|
|
104
120
|
if (!anchor) return null;
|
|
@@ -115,8 +131,59 @@
|
|
|
115
131
|
dispatchCrossBoundaryEvent(host, name, detail);
|
|
116
132
|
}
|
|
117
133
|
|
|
134
|
+
function hashString(input: string): string {
|
|
135
|
+
let hash = 5381;
|
|
136
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
137
|
+
hash = ((hash << 5) + hash) ^ input.charCodeAt(index);
|
|
138
|
+
}
|
|
139
|
+
return (hash >>> 0).toString(36);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function getCompositionRevisionKey(model: unknown): string {
|
|
143
|
+
const typed = (model || {}) as Record<string, unknown>;
|
|
144
|
+
const sectionId =
|
|
145
|
+
((typed.section as Record<string, unknown> | undefined)?.identifier as string) ||
|
|
146
|
+
(typed.sectionId as string) ||
|
|
147
|
+
"";
|
|
148
|
+
const currentItemIndex =
|
|
149
|
+
typeof typed.currentItemIndex === "number" ? typed.currentItemIndex : -1;
|
|
150
|
+
const renderables = Array.isArray(typed.renderables) ? typed.renderables : [];
|
|
151
|
+
const renderableSignature = renderables
|
|
152
|
+
.map((entry, index) => {
|
|
153
|
+
const row = (entry || {}) as Record<string, unknown>;
|
|
154
|
+
const entity = (row.entity || {}) as Record<string, unknown>;
|
|
155
|
+
const entityId =
|
|
156
|
+
(typeof entity.id === "string" && entity.id) || `renderable-${index}`;
|
|
157
|
+
const entityVersion =
|
|
158
|
+
(typeof entity.version === "string" && entity.version) ||
|
|
159
|
+
(typeof entity.version === "number" ? String(entity.version) : "") ||
|
|
160
|
+
(typeof (entity.config as Record<string, unknown> | undefined)?.version === "string"
|
|
161
|
+
? ((entity.config as Record<string, unknown>).version as string)
|
|
162
|
+
: "");
|
|
163
|
+
return `${entityId}:${entityVersion}`;
|
|
164
|
+
})
|
|
165
|
+
.join("|");
|
|
166
|
+
const sessionsByItem = (typed.itemSessionsByItemId || {}) as Record<string, unknown>;
|
|
167
|
+
const itemSessionSignature = Object.keys(sessionsByItem)
|
|
168
|
+
.sort((left, right) => left.localeCompare(right))
|
|
169
|
+
.map((itemId) => {
|
|
170
|
+
const session = sessionsByItem[itemId] as Record<string, unknown> | undefined;
|
|
171
|
+
const sessionId = (typeof session?.id === "string" && session.id) || "";
|
|
172
|
+
const dataPayload = Array.isArray(session?.data) ? session.data : [];
|
|
173
|
+
let payloadSignature = "";
|
|
174
|
+
try {
|
|
175
|
+
payloadSignature = hashString(JSON.stringify(dataPayload));
|
|
176
|
+
} catch {
|
|
177
|
+
payloadSignature = String(dataPayload.length);
|
|
178
|
+
}
|
|
179
|
+
return `${itemId}:${sessionId}:${payloadSignature}`;
|
|
180
|
+
})
|
|
181
|
+
.join("|");
|
|
182
|
+
return `${sectionId}|${currentItemIndex}|${renderableSignature}|${itemSessionSignature}`;
|
|
183
|
+
}
|
|
184
|
+
|
|
118
185
|
function isKnownPlayerType(value: unknown): value is ItemPlayerType {
|
|
119
|
-
return value === "iife" || value === "esm" || value === "
|
|
186
|
+
return value === "iife" || value === "esm" || value === "preloaded" || value === "custom";
|
|
120
187
|
}
|
|
121
188
|
|
|
122
189
|
function normalizeItemPlayerConfig(
|
|
@@ -150,6 +217,26 @@
|
|
|
150
217
|
};
|
|
151
218
|
}
|
|
152
219
|
|
|
220
|
+
function normalizeEnv(input: unknown): { mode: string; role: string } {
|
|
221
|
+
const envValue = ((input || {}) as Record<string, unknown>) || {};
|
|
222
|
+
const mode =
|
|
223
|
+
typeof envValue.mode === "string" && envValue.mode.trim()
|
|
224
|
+
? envValue.mode.trim()
|
|
225
|
+
: DEFAULT_ENV.mode;
|
|
226
|
+
const role =
|
|
227
|
+
typeof envValue.role === "string" && envValue.role.trim()
|
|
228
|
+
? envValue.role.trim()
|
|
229
|
+
: DEFAULT_ENV.role;
|
|
230
|
+
return { mode, role };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function resolveSectionViewFromEnv(input: unknown): string {
|
|
234
|
+
const resolvedEnv = normalizeEnv(input);
|
|
235
|
+
if (resolvedEnv.mode === "author") return "author";
|
|
236
|
+
if (resolvedEnv.role === "instructor") return "scorer";
|
|
237
|
+
return "candidate";
|
|
238
|
+
}
|
|
239
|
+
|
|
153
240
|
async function createDefaultSectionController() {
|
|
154
241
|
if (createSectionController) {
|
|
155
242
|
return createSectionController() as any;
|
|
@@ -203,6 +290,8 @@
|
|
|
203
290
|
const effectiveSectionId = $derived(
|
|
204
291
|
sectionId || (section as any)?.identifier || `section-${effectiveAssessmentId || "default"}`,
|
|
205
292
|
);
|
|
293
|
+
const effectiveEnv = $derived.by(() => normalizeEnv(env));
|
|
294
|
+
const effectiveSectionView = $derived.by(() => resolveSectionViewFromEnv(effectiveEnv));
|
|
206
295
|
const effectiveItemPlayer = $derived.by(() =>
|
|
207
296
|
normalizeItemPlayerConfig(player, playerType),
|
|
208
297
|
);
|
|
@@ -221,10 +310,10 @@
|
|
|
221
310
|
itemPlayer: effectiveItemPlayer,
|
|
222
311
|
reportSessionChanged: (itemId: string, detail: unknown) => {
|
|
223
312
|
const result = sectionEngine.handleItemSessionChanged(itemId, detail);
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
313
|
+
emitNormalizedSessionChanged({
|
|
314
|
+
itemId,
|
|
315
|
+
result,
|
|
316
|
+
fallbackSession: detail,
|
|
228
317
|
});
|
|
229
318
|
},
|
|
230
319
|
};
|
|
@@ -239,35 +328,12 @@
|
|
|
239
328
|
},
|
|
240
329
|
);
|
|
241
330
|
|
|
242
|
-
function
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
? typed.items.map((item: any) => item?.id || "")
|
|
246
|
-
: [];
|
|
247
|
-
const passageIds = Array.isArray(typed?.passages)
|
|
248
|
-
? typed.passages.map((item: any) => item?.id || "")
|
|
249
|
-
: [];
|
|
250
|
-
const sessionByItem = typed?.itemSessionsByItemId
|
|
251
|
-
? Object.entries(typed.itemSessionsByItemId)
|
|
252
|
-
.sort(([left], [right]) => left.localeCompare(right))
|
|
253
|
-
.map(([itemId, session]) => [itemId, JSON.stringify(session ?? null)])
|
|
254
|
-
: [];
|
|
255
|
-
return JSON.stringify({
|
|
256
|
-
sectionId: typed?.section?.identifier || typed?.sectionId || "",
|
|
257
|
-
currentItemIndex: typed?.currentItemIndex ?? -1,
|
|
258
|
-
itemIds,
|
|
259
|
-
passageIds,
|
|
260
|
-
sessionByItem,
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function emitCompositionChanged() {
|
|
265
|
-
const nextModel = sectionEngine.getCompositionModel();
|
|
266
|
-
const signature = getCompositionSignature(nextModel);
|
|
267
|
-
if (signature === lastCompositionSignature) {
|
|
331
|
+
function flushCompositionChanged(nextModel: unknown) {
|
|
332
|
+
const nextRevisionKey = getCompositionRevisionKey(nextModel);
|
|
333
|
+
if (nextRevisionKey === lastCompositionRevisionKey) {
|
|
268
334
|
return;
|
|
269
335
|
}
|
|
270
|
-
|
|
336
|
+
lastCompositionRevisionKey = nextRevisionKey;
|
|
271
337
|
compositionVersion += 1;
|
|
272
338
|
compositionModel = nextModel;
|
|
273
339
|
emit("composition-changed", {
|
|
@@ -276,6 +342,48 @@
|
|
|
276
342
|
});
|
|
277
343
|
}
|
|
278
344
|
|
|
345
|
+
function emitCompositionChanged(nextModel?: unknown) {
|
|
346
|
+
pendingCompositionModel = nextModel ?? sectionEngine.getCompositionModel();
|
|
347
|
+
if (pendingCompositionEmit) return;
|
|
348
|
+
pendingCompositionEmit = true;
|
|
349
|
+
const flush = () => {
|
|
350
|
+
pendingCompositionEmit = false;
|
|
351
|
+
compositionEmitRaf = null;
|
|
352
|
+
flushCompositionChanged(pendingCompositionModel);
|
|
353
|
+
};
|
|
354
|
+
if (typeof window !== "undefined" && typeof window.requestAnimationFrame === "function") {
|
|
355
|
+
compositionEmitRaf = window.requestAnimationFrame(flush);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
queueMicrotask(flush);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function emitNormalizedSessionChanged(args: {
|
|
362
|
+
itemId: string;
|
|
363
|
+
canonicalItemId?: string;
|
|
364
|
+
result: unknown;
|
|
365
|
+
fallbackSession: unknown;
|
|
366
|
+
}) {
|
|
367
|
+
const normalized =
|
|
368
|
+
(args.result as SessionChangedLike | null)?.eventDetail || args.fallbackSession;
|
|
369
|
+
const payload = {
|
|
370
|
+
...(normalized as Record<string, unknown>),
|
|
371
|
+
itemId: args.itemId,
|
|
372
|
+
canonicalItemId: args.canonicalItemId || args.itemId,
|
|
373
|
+
sourceRuntimeId: runtimeId,
|
|
374
|
+
} as Record<string, unknown>;
|
|
375
|
+
if (
|
|
376
|
+
!shouldEmitCanonicalSessionEvent({
|
|
377
|
+
state: sessionEmitPolicyState,
|
|
378
|
+
itemId: args.itemId,
|
|
379
|
+
payload,
|
|
380
|
+
})
|
|
381
|
+
) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
emit("session-changed", payload);
|
|
385
|
+
}
|
|
386
|
+
|
|
279
387
|
function isLocalToCurrentRuntime(eventTarget: EventTarget | null): boolean {
|
|
280
388
|
if (!(eventTarget instanceof HTMLElement)) return false;
|
|
281
389
|
const sourceRuntime = requestContext(
|
|
@@ -312,6 +420,14 @@
|
|
|
312
420
|
}
|
|
313
421
|
});
|
|
314
422
|
|
|
423
|
+
$effect(() => {
|
|
424
|
+
const currentSectionId = effectiveSectionId;
|
|
425
|
+
const currentAttemptId = attemptId || "";
|
|
426
|
+
void currentSectionId;
|
|
427
|
+
void currentAttemptId;
|
|
428
|
+
resetSessionEmitPolicyState(sessionEmitPolicyState);
|
|
429
|
+
});
|
|
430
|
+
|
|
315
431
|
$effect(() => {
|
|
316
432
|
if (!host || !hostRuntimeContextValue) return;
|
|
317
433
|
hostRuntimeProvider = new ContextProvider(host, {
|
|
@@ -339,8 +455,8 @@
|
|
|
339
455
|
if (!host) return;
|
|
340
456
|
host.setAttribute("data-item-player-type", effectiveItemPlayer.type);
|
|
341
457
|
host.setAttribute("data-item-player-tag", effectiveItemPlayer.tagName);
|
|
342
|
-
host.setAttribute("data-env-mode", String((
|
|
343
|
-
host.setAttribute("data-env-role", String((
|
|
458
|
+
host.setAttribute("data-env-mode", String((effectiveEnv as any)?.mode || ""));
|
|
459
|
+
host.setAttribute("data-env-role", String((effectiveEnv as any)?.role || ""));
|
|
344
460
|
});
|
|
345
461
|
|
|
346
462
|
$effect(() => {
|
|
@@ -378,12 +494,11 @@
|
|
|
378
494
|
sectionId: effectiveSectionId,
|
|
379
495
|
assessmentId: effectiveAssessmentId,
|
|
380
496
|
attemptId: attemptId || undefined,
|
|
381
|
-
view,
|
|
497
|
+
view: effectiveSectionView,
|
|
382
498
|
createDefaultController: createDefaultSectionController,
|
|
383
499
|
onCompositionChanged: (nextComposition) => {
|
|
384
500
|
if (cancelled) return;
|
|
385
|
-
|
|
386
|
-
emitCompositionChanged();
|
|
501
|
+
emitCompositionChanged(nextComposition);
|
|
387
502
|
},
|
|
388
503
|
})
|
|
389
504
|
.then(() => {
|
|
@@ -393,6 +508,7 @@
|
|
|
393
508
|
assessmentId: effectiveAssessmentId,
|
|
394
509
|
sectionId: effectiveSectionId,
|
|
395
510
|
itemPlayer: effectiveItemPlayer,
|
|
511
|
+
coordinator: effectiveCoordinator,
|
|
396
512
|
});
|
|
397
513
|
emit("section-ready", {
|
|
398
514
|
sectionId: effectiveSectionId,
|
|
@@ -400,6 +516,11 @@
|
|
|
400
516
|
})
|
|
401
517
|
.catch((error) => {
|
|
402
518
|
runtimeError = error;
|
|
519
|
+
sectionEngine.reportSectionError({
|
|
520
|
+
source: "section-runtime",
|
|
521
|
+
error,
|
|
522
|
+
timestamp: Date.now(),
|
|
523
|
+
});
|
|
403
524
|
emit("runtime-error", {
|
|
404
525
|
runtimeId,
|
|
405
526
|
error,
|
|
@@ -420,40 +541,78 @@
|
|
|
420
541
|
const detail = (event as CustomEvent<RuntimeRegistrationDetail>).detail;
|
|
421
542
|
if (!detail?.element || !detail?.itemId) return;
|
|
422
543
|
const changed = sectionEngine.register(detail);
|
|
544
|
+
sectionEngine.handleContentRegistered(detail);
|
|
423
545
|
if (changed) emitCompositionChanged();
|
|
424
546
|
};
|
|
425
547
|
|
|
426
548
|
const onUnregister = (event: Event) => {
|
|
427
549
|
if (!isLocalToCurrentRuntime(event.target)) return;
|
|
428
550
|
const detail = (event as CustomEvent<RuntimeRegistrationDetail>).detail;
|
|
551
|
+
if (!detail?.itemId) return;
|
|
429
552
|
const changed = detail?.element
|
|
430
553
|
? sectionEngine.unregister(detail.element)
|
|
431
554
|
: false;
|
|
555
|
+
sectionEngine.handleContentUnregistered(detail);
|
|
432
556
|
if (changed) emitCompositionChanged();
|
|
433
557
|
};
|
|
434
558
|
|
|
435
559
|
const onItemSessionChanged = (event: Event) => {
|
|
436
560
|
if (!isLocalToCurrentRuntime(event.target)) return;
|
|
437
|
-
const detail = (event as CustomEvent<
|
|
561
|
+
const detail = (event as CustomEvent<InternalItemSessionChangedDetail>).detail;
|
|
438
562
|
if (!detail?.itemId) return;
|
|
439
|
-
if (!shouldHandleBySourceRuntime(detail.sourceRuntimeId, runtimeId)) return;
|
|
440
563
|
const result = sectionEngine.handleItemSessionChanged(detail.itemId, detail.session);
|
|
441
|
-
|
|
442
|
-
emit("session-changed", {
|
|
443
|
-
...(normalized as Record<string, unknown>),
|
|
564
|
+
emitNormalizedSessionChanged({
|
|
444
565
|
itemId: detail.itemId,
|
|
445
|
-
canonicalItemId:
|
|
446
|
-
|
|
566
|
+
canonicalItemId: sectionEngine.getCanonicalItemId(detail.itemId),
|
|
567
|
+
result,
|
|
568
|
+
fallbackSession: detail.session,
|
|
569
|
+
});
|
|
570
|
+
};
|
|
571
|
+
const onContentLoaded = (event: Event) => {
|
|
572
|
+
if (!isLocalToCurrentRuntime(event.target)) return;
|
|
573
|
+
const detail = (event as CustomEvent<InternalContentLoadedDetail>).detail;
|
|
574
|
+
if (!detail?.itemId) return;
|
|
575
|
+
sectionEngine.handleContentLoaded({
|
|
576
|
+
itemId: detail.itemId,
|
|
577
|
+
canonicalItemId: detail.canonicalItemId,
|
|
578
|
+
contentKind: detail.contentKind,
|
|
579
|
+
detail: detail.detail,
|
|
580
|
+
timestamp: Date.now(),
|
|
581
|
+
});
|
|
582
|
+
};
|
|
583
|
+
const onItemPlayerError = (event: Event) => {
|
|
584
|
+
if (!isLocalToCurrentRuntime(event.target)) return;
|
|
585
|
+
const detail = (event as CustomEvent<InternalItemPlayerErrorDetail>).detail;
|
|
586
|
+
if (!detail?.itemId) return;
|
|
587
|
+
sectionEngine.handleItemPlayerError({
|
|
588
|
+
itemId: detail.itemId,
|
|
589
|
+
canonicalItemId: detail.canonicalItemId,
|
|
590
|
+
contentKind: detail.contentKind,
|
|
591
|
+
error: detail.error,
|
|
592
|
+
timestamp: Date.now(),
|
|
447
593
|
});
|
|
448
594
|
};
|
|
449
595
|
|
|
450
596
|
localHost.addEventListener(PIE_REGISTER_EVENT, onRegister);
|
|
451
597
|
localHost.addEventListener(PIE_UNREGISTER_EVENT, onUnregister);
|
|
452
|
-
localHost.addEventListener(
|
|
598
|
+
localHost.addEventListener(PIE_INTERNAL_ITEM_SESSION_CHANGED_EVENT, onItemSessionChanged);
|
|
599
|
+
localHost.addEventListener(PIE_INTERNAL_CONTENT_LOADED_EVENT, onContentLoaded);
|
|
600
|
+
localHost.addEventListener(PIE_INTERNAL_ITEM_PLAYER_ERROR_EVENT, onItemPlayerError);
|
|
453
601
|
return () => {
|
|
454
602
|
localHost.removeEventListener(PIE_REGISTER_EVENT, onRegister);
|
|
455
603
|
localHost.removeEventListener(PIE_UNREGISTER_EVENT, onUnregister);
|
|
456
|
-
localHost.removeEventListener(
|
|
604
|
+
localHost.removeEventListener(
|
|
605
|
+
PIE_INTERNAL_ITEM_SESSION_CHANGED_EVENT,
|
|
606
|
+
onItemSessionChanged,
|
|
607
|
+
);
|
|
608
|
+
localHost.removeEventListener(
|
|
609
|
+
PIE_INTERNAL_CONTENT_LOADED_EVENT,
|
|
610
|
+
onContentLoaded,
|
|
611
|
+
);
|
|
612
|
+
localHost.removeEventListener(
|
|
613
|
+
PIE_INTERNAL_ITEM_PLAYER_ERROR_EVENT,
|
|
614
|
+
onItemPlayerError,
|
|
615
|
+
);
|
|
457
616
|
};
|
|
458
617
|
});
|
|
459
618
|
|
|
@@ -500,6 +659,11 @@
|
|
|
500
659
|
|
|
501
660
|
$effect(() => {
|
|
502
661
|
return () => {
|
|
662
|
+
if (compositionEmitRaf !== null && typeof window !== "undefined") {
|
|
663
|
+
window.cancelAnimationFrame(compositionEmitRaf);
|
|
664
|
+
compositionEmitRaf = null;
|
|
665
|
+
}
|
|
666
|
+
pendingCompositionEmit = false;
|
|
503
667
|
void sectionEngine.dispose().catch((error) => {
|
|
504
668
|
runtimeError = error;
|
|
505
669
|
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<svelte:options
|
|
2
|
+
customElement={{
|
|
3
|
+
tag: "pie-section-toolbar",
|
|
4
|
+
shadow: "none",
|
|
5
|
+
props: {
|
|
6
|
+
enabledTools: { type: "String", attribute: "enabled-tools" },
|
|
7
|
+
position: { type: "String", attribute: "position" },
|
|
8
|
+
sectionId: { type: "String", attribute: "section-id" },
|
|
9
|
+
size: { type: "String", attribute: "size" },
|
|
10
|
+
language: { type: "String", attribute: "language" },
|
|
11
|
+
toolRegistry: { type: "Object", reflect: false },
|
|
12
|
+
pnpResolver: { type: "Object", reflect: false },
|
|
13
|
+
assessment: { type: "Object", reflect: false },
|
|
14
|
+
itemRef: { type: "Object", reflect: false },
|
|
15
|
+
item: { type: "Object", reflect: false },
|
|
16
|
+
hostButtons: { type: "Object", reflect: false },
|
|
17
|
+
},
|
|
18
|
+
}}
|
|
19
|
+
/>
|
|
20
|
+
|
|
21
|
+
<script lang="ts">
|
|
22
|
+
import "./item-toolbar-element.js";
|
|
23
|
+
import type { ToolRegistry } from "../services/ToolRegistry.js";
|
|
24
|
+
import type { PNPToolResolver } from "../services/PNPToolResolver.js";
|
|
25
|
+
import type { AssessmentEntity, AssessmentItemRef, ItemEntity } from "@pie-players/pie-players-shared/types";
|
|
26
|
+
import type { ToolbarItem } from "../services/toolbar-items.js";
|
|
27
|
+
|
|
28
|
+
let {
|
|
29
|
+
enabledTools = "",
|
|
30
|
+
position = "bottom",
|
|
31
|
+
sectionId = "",
|
|
32
|
+
size = "md" as "sm" | "md" | "lg",
|
|
33
|
+
language = "en-US",
|
|
34
|
+
toolRegistry = null as ToolRegistry | null,
|
|
35
|
+
pnpResolver = null as PNPToolResolver | null,
|
|
36
|
+
assessment = null as AssessmentEntity | null,
|
|
37
|
+
itemRef = null as AssessmentItemRef | null,
|
|
38
|
+
item = null as ItemEntity | null,
|
|
39
|
+
hostButtons = [] as ToolbarItem[],
|
|
40
|
+
} = $props();
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<pie-item-toolbar
|
|
44
|
+
level="section"
|
|
45
|
+
scope-id={sectionId}
|
|
46
|
+
section-id={sectionId}
|
|
47
|
+
tools={enabledTools}
|
|
48
|
+
content-kind="section"
|
|
49
|
+
position={position}
|
|
50
|
+
{size}
|
|
51
|
+
{language}
|
|
52
|
+
{toolRegistry}
|
|
53
|
+
{pnpResolver}
|
|
54
|
+
{assessment}
|
|
55
|
+
{itemRef}
|
|
56
|
+
{item}
|
|
57
|
+
{hostButtons}
|
|
58
|
+
></pie-item-toolbar>
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TTS Provider Interfaces
|
|
3
|
-
*
|
|
4
|
-
* Defines the contract for text-to-speech providers.
|
|
5
|
-
* Providers are stateless factories that create configured TTS implementations.
|
|
6
|
-
*
|
|
7
|
-
* Part of PIE Assessment Toolkit.
|
|
8
|
-
*/
|
|
9
|
-
import type { TTSConfig } from "../TTSService.js";
|
|
10
|
-
/**
|
|
11
|
-
* TTS Provider interface
|
|
12
|
-
*
|
|
13
|
-
* Providers are stateless factories that create TTS implementations.
|
|
14
|
-
* They describe capabilities and create configured instances.
|
|
15
|
-
*/
|
|
16
|
-
export interface ITTSProvider {
|
|
17
|
-
/**
|
|
18
|
-
* Unique identifier for this provider
|
|
19
|
-
*/
|
|
20
|
-
readonly providerId: string;
|
|
21
|
-
/**
|
|
22
|
-
* Human-readable provider name
|
|
23
|
-
*/
|
|
24
|
-
readonly providerName: string;
|
|
25
|
-
/**
|
|
26
|
-
* Provider version
|
|
27
|
-
*/
|
|
28
|
-
readonly version: string;
|
|
29
|
-
/**
|
|
30
|
-
* Initialize and create a configured TTS implementation
|
|
31
|
-
*/
|
|
32
|
-
initialize(config: TTSConfig): Promise<ITTSProviderImplementation>;
|
|
33
|
-
/**
|
|
34
|
-
* Check if a specific feature is supported
|
|
35
|
-
*/
|
|
36
|
-
supportsFeature(feature: TTSFeature): boolean;
|
|
37
|
-
/**
|
|
38
|
-
* Get provider capabilities
|
|
39
|
-
*/
|
|
40
|
-
getCapabilities(): TTSProviderCapabilities;
|
|
41
|
-
/**
|
|
42
|
-
* Clean up provider resources
|
|
43
|
-
*/
|
|
44
|
-
destroy(): void;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* TTS Provider Implementation interface
|
|
48
|
-
*
|
|
49
|
-
* The actual TTS implementation that handles playback.
|
|
50
|
-
* Created by ITTSProvider.initialize()
|
|
51
|
-
*/
|
|
52
|
-
export interface ITTSProviderImplementation {
|
|
53
|
-
/**
|
|
54
|
-
* Speak text
|
|
55
|
-
*/
|
|
56
|
-
speak(text: string): Promise<void>;
|
|
57
|
-
/**
|
|
58
|
-
* Pause playback
|
|
59
|
-
*/
|
|
60
|
-
pause(): void;
|
|
61
|
-
/**
|
|
62
|
-
* Resume playback
|
|
63
|
-
*/
|
|
64
|
-
resume(): void;
|
|
65
|
-
/**
|
|
66
|
-
* Stop playback
|
|
67
|
-
*/
|
|
68
|
-
stop(): void;
|
|
69
|
-
/**
|
|
70
|
-
* Check if currently playing
|
|
71
|
-
*/
|
|
72
|
-
isPlaying(): boolean;
|
|
73
|
-
/**
|
|
74
|
-
* Check if paused
|
|
75
|
-
*/
|
|
76
|
-
isPaused(): boolean;
|
|
77
|
-
/**
|
|
78
|
-
* Update settings dynamically (optional)
|
|
79
|
-
* Allows changing rate, pitch, and voice without full reinitialization.
|
|
80
|
-
* Changes take effect on the next speak() call.
|
|
81
|
-
*/
|
|
82
|
-
updateSettings?(settings: Partial<TTSConfig>): void | Promise<void>;
|
|
83
|
-
/**
|
|
84
|
-
* Word boundary callback (optional)
|
|
85
|
-
* Called during speech for word highlighting
|
|
86
|
-
* @param word - The word being spoken (may be empty for server providers)
|
|
87
|
-
* @param position - Character position in the text
|
|
88
|
-
* @param length - Optional word length (for server providers with speech marks)
|
|
89
|
-
*/
|
|
90
|
-
onWordBoundary?: (word: string, position: number, length?: number) => void;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* TTS Provider capabilities
|
|
94
|
-
*
|
|
95
|
-
* Describes which features a provider supports
|
|
96
|
-
*/
|
|
97
|
-
export interface TTSProviderCapabilities {
|
|
98
|
-
/**
|
|
99
|
-
* Supports pause/resume
|
|
100
|
-
*/
|
|
101
|
-
supportsPause: boolean;
|
|
102
|
-
/**
|
|
103
|
-
* Supports resume after pause
|
|
104
|
-
*/
|
|
105
|
-
supportsResume: boolean;
|
|
106
|
-
/**
|
|
107
|
-
* Supports word boundary events for highlighting
|
|
108
|
-
*/
|
|
109
|
-
supportsWordBoundary: boolean;
|
|
110
|
-
/**
|
|
111
|
-
* Supports voice selection
|
|
112
|
-
*/
|
|
113
|
-
supportsVoiceSelection: boolean;
|
|
114
|
-
/**
|
|
115
|
-
* Supports rate control (speed)
|
|
116
|
-
*/
|
|
117
|
-
supportsRateControl: boolean;
|
|
118
|
-
/**
|
|
119
|
-
* Supports pitch control
|
|
120
|
-
*/
|
|
121
|
-
supportsPitchControl: boolean;
|
|
122
|
-
/**
|
|
123
|
-
* Maximum text length (if limited)
|
|
124
|
-
*/
|
|
125
|
-
maxTextLength?: number;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* TTS features for capability checking
|
|
129
|
-
*/
|
|
130
|
-
export type TTSFeature = "pause" | "resume" | "wordBoundary" | "voiceSelection" | "rateControl" | "pitchControl";
|
|
131
|
-
//# sourceMappingURL=provider-interface.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"provider-interface.d.ts","sourceRoot":"","sources":["../../../src/services/tts/provider-interface.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC5B;;OAEG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAE9B;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAEnE;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC;IAE9C;;OAEG;IACH,eAAe,IAAI,uBAAuB,CAAC;IAE3C;;OAEG;IACH,OAAO,IAAI,IAAI,CAAC;CAChB;AAED;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IAC1C;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC;;OAEG;IACH,KAAK,IAAI,IAAI,CAAC;IAEd;;OAEG;IACH,MAAM,IAAI,IAAI,CAAC;IAEf;;OAEG;IACH,IAAI,IAAI,IAAI,CAAC;IAEb;;OAEG;IACH,SAAS,IAAI,OAAO,CAAC;IAErB;;OAEG;IACH,QAAQ,IAAI,OAAO,CAAC;IAEpB;;;;OAIG;IACH,cAAc,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpE;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3E;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACvC;;OAEG;IACH,aAAa,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,cAAc,EAAE,OAAO,CAAC;IAExB;;OAEG;IACH,oBAAoB,EAAE,OAAO,CAAC;IAE9B;;OAEG;IACH,sBAAsB,EAAE,OAAO,CAAC;IAEhC;;OAEG;IACH,mBAAmB,EAAE,OAAO,CAAC;IAE7B;;OAEG;IACH,oBAAoB,EAAE,OAAO,CAAC;IAE9B;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,MAAM,UAAU,GACnB,OAAO,GACP,QAAQ,GACR,cAAc,GACd,gBAAgB,GAChB,aAAa,GACb,cAAc,CAAC"}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TTS Provider Interfaces
|
|
3
|
-
*
|
|
4
|
-
* Defines the contract for text-to-speech providers.
|
|
5
|
-
* Providers are stateless factories that create configured TTS implementations.
|
|
6
|
-
*
|
|
7
|
-
* Part of PIE Assessment Toolkit.
|
|
8
|
-
*/
|
|
9
|
-
export {};
|
|
10
|
-
//# sourceMappingURL=provider-interface.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"provider-interface.js","sourceRoot":"","sources":["../../../src/services/tts/provider-interface.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|