@pie-players/pie-section-player 0.2.12 → 0.2.14
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 +28 -568
- package/dist/component-definitions.d.ts +0 -3
- package/dist/component-definitions.d.ts.map +1 -1
- package/dist/components/section-player-vertical-element.d.ts +2 -0
- package/dist/components/section-player-vertical-element.d.ts.map +1 -0
- package/dist/components/shared/composition.d.ts +9 -0
- package/dist/components/shared/composition.d.ts.map +1 -0
- package/dist/components/shared/player-action.d.ts +18 -0
- package/dist/components/shared/player-action.d.ts.map +1 -0
- package/dist/components/shared/player-preload.d.ts +37 -0
- package/dist/components/shared/player-preload.d.ts.map +1 -0
- package/dist/components/shared/section-player-runtime.d.ts +104 -0
- package/dist/components/shared/section-player-runtime.d.ts.map +1 -0
- package/dist/components/shared/section-player-view-state.d.ts +24 -0
- package/dist/components/shared/section-player-view-state.d.ts.map +1 -0
- package/dist/controllers/SectionContentService.d.ts +3 -0
- package/dist/controllers/SectionContentService.d.ts.map +1 -1
- package/dist/controllers/SectionController.d.ts +55 -1
- package/dist/controllers/SectionController.d.ts.map +1 -1
- package/dist/controllers/SectionSessionService.d.ts +0 -1
- package/dist/controllers/SectionSessionService.d.ts.map +1 -1
- package/dist/controllers/toolkit-section-contracts.d.ts +2 -28
- package/dist/controllers/toolkit-section-contracts.d.ts.map +1 -1
- package/dist/controllers/types.d.ts +97 -6
- package/dist/controllers/types.d.ts.map +1 -1
- package/dist/pie-item-player-q4jcP2lZ.js +6196 -0
- package/dist/pie-section-player.d.ts +0 -8
- package/dist/pie-section-player.d.ts.map +1 -1
- package/dist/pie-section-player.js +61631 -11
- package/dist/player-preload-CQVG0Bih.js +705 -0
- package/dist/utils/player-preload.d.ts +2 -0
- package/dist/utils/player-preload.d.ts.map +1 -0
- package/dist/utils/player-preload.js +8 -0
- package/package.json +24 -32
- package/src/components/ItemShellElement.svelte +119 -3
- package/src/components/PassageShellElement.svelte +49 -0
- package/src/components/PieSectionPlayerBaseElement.svelte +65 -78
- package/src/components/PieSectionPlayerSplitPaneElement.svelte +337 -296
- package/src/components/PieSectionPlayerVerticalElement.svelte +446 -0
- package/src/components/shared/SectionItemCard.svelte +92 -0
- package/src/components/shared/SectionPassageCard.svelte +88 -0
- package/dist/ItemRenderer-MsjF_Beu.js +0 -467
- package/dist/PieItemModeLayoutElement-D7oTzA9T.js +0 -316
- package/dist/PieSplitPanelLayoutElement-GUtJ_NlF.js +0 -246
- package/dist/PieVerticalLayoutElement-BoA3FO5g.js +0 -194
- package/dist/controllers/SectionToolkitService.d.ts +0 -24
- package/dist/controllers/SectionToolkitService.d.ts.map +0 -1
- package/dist/controllers/SessionPersistenceStrategy.d.ts +0 -15
- package/dist/controllers/SessionPersistenceStrategy.d.ts.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/pie-section-player-DJ5NcwdT.js +0 -17078
- package/dist/runtime/runtime-event-guards.d.ts +0 -4
- package/dist/runtime/runtime-event-guards.d.ts.map +0 -1
- package/src/PieSectionPlayer.svelte +0 -826
- package/src/components/ItemModeLayout.svelte +0 -172
- package/src/components/ItemNavigation.svelte +0 -96
- package/src/components/ItemPlayerBridge.svelte +0 -110
- package/src/components/ItemRenderer.svelte +0 -248
- package/src/components/ItemShell.svelte +0 -86
- package/src/components/layout-elements/PieItemModeLayoutElement.svelte +0 -47
- package/src/components/layout-elements/PieSplitPanelLayoutElement.svelte +0 -62
- package/src/components/layout-elements/PieVerticalLayoutElement.svelte +0 -41
- package/src/components/layouts/SplitPanelLayout.svelte +0 -385
- package/src/components/layouts/VerticalLayout.svelte +0 -193
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"player-preload.d.ts","sourceRoot":"","sources":["../../src/utils/player-preload.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,qBAAqB,EACrB,aAAa,EACb,uBAAuB,EACvB,+BAA+B,EAC/B,qBAAqB,EACrB,KAAK,kBAAkB,GACvB,MAAM,wCAAwC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pie-players/pie-section-player",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Web component for rendering QTI 3.0 assessment sections with passages and items",
|
|
6
6
|
"license": "MIT",
|
|
@@ -15,35 +15,23 @@
|
|
|
15
15
|
"unpkg": "./dist/pie-section-player.js",
|
|
16
16
|
"jsdelivr": "./dist/pie-section-player.js",
|
|
17
17
|
"main": "./dist/pie-section-player.js",
|
|
18
|
-
"types": "./dist/
|
|
18
|
+
"types": "./dist/pie-section-player.d.ts",
|
|
19
19
|
"exports": {
|
|
20
20
|
".": {
|
|
21
|
-
"types": "./dist/index.d.ts",
|
|
22
|
-
"import": "./dist/pie-section-player.js"
|
|
23
|
-
},
|
|
24
|
-
"./components/ItemShellElement.svelte": {
|
|
25
|
-
"types": "./dist/pie-section-player.d.ts",
|
|
26
|
-
"import": "./dist/pie-section-player.js"
|
|
27
|
-
},
|
|
28
|
-
"./components/PassageShellElement.svelte": {
|
|
29
|
-
"types": "./dist/pie-section-player.d.ts",
|
|
30
|
-
"import": "./dist/pie-section-player.js"
|
|
31
|
-
},
|
|
32
|
-
"./components/section-player-base-element": {
|
|
33
21
|
"types": "./dist/pie-section-player.d.ts",
|
|
34
22
|
"import": "./dist/pie-section-player.js"
|
|
35
23
|
},
|
|
36
|
-
"./components/
|
|
24
|
+
"./components/section-player-splitpane-element": {
|
|
37
25
|
"types": "./dist/pie-section-player.d.ts",
|
|
38
26
|
"import": "./dist/pie-section-player.js"
|
|
39
27
|
},
|
|
40
|
-
"./components/
|
|
28
|
+
"./components/section-player-vertical-element": {
|
|
41
29
|
"types": "./dist/pie-section-player.d.ts",
|
|
42
30
|
"import": "./dist/pie-section-player.js"
|
|
43
31
|
},
|
|
44
|
-
"./
|
|
45
|
-
"types": "./dist/
|
|
46
|
-
"import": "./dist/
|
|
32
|
+
"./utils/player-preload": {
|
|
33
|
+
"types": "./dist/utils/player-preload.d.ts",
|
|
34
|
+
"import": "./dist/utils/player-preload.js"
|
|
47
35
|
}
|
|
48
36
|
},
|
|
49
37
|
"files": [
|
|
@@ -61,22 +49,26 @@
|
|
|
61
49
|
"svelte": "^5.51.0"
|
|
62
50
|
},
|
|
63
51
|
"dependencies": {
|
|
64
|
-
"@pie-players/pie-assessment-toolkit": "0.2.
|
|
65
|
-
"@pie-players/pie-
|
|
66
|
-
"@pie-players/pie-
|
|
67
|
-
"@pie-players/pie-
|
|
68
|
-
"@pie-players/pie-
|
|
69
|
-
"@pie-players/pie-
|
|
70
|
-
"@pie-players/pie-
|
|
71
|
-
"@pie-players/pie-
|
|
72
|
-
"@pie-players/pie-tool-
|
|
73
|
-
"@pie-players/pie-tool-
|
|
74
|
-
"@pie-players/pie-tool-
|
|
75
|
-
"@pie-players/pie-tool-
|
|
76
|
-
"@pie-players/
|
|
52
|
+
"@pie-players/pie-assessment-toolkit": "0.2.11",
|
|
53
|
+
"@pie-players/pie-item-player": "0.1.2",
|
|
54
|
+
"@pie-players/pie-context": "0.1.3",
|
|
55
|
+
"@pie-players/pie-players-shared": "0.2.7",
|
|
56
|
+
"@pie-players/pie-toolbars": "0.1.2",
|
|
57
|
+
"@pie-players/pie-tool-answer-eliminator": "0.2.11",
|
|
58
|
+
"@pie-players/pie-tool-annotation-toolbar": "0.1.11",
|
|
59
|
+
"@pie-players/pie-tool-calculator": "0.1.11",
|
|
60
|
+
"@pie-players/pie-tool-theme": "0.1.11",
|
|
61
|
+
"@pie-players/pie-tool-graph": "0.1.11",
|
|
62
|
+
"@pie-players/pie-tool-line-reader": "0.1.11",
|
|
63
|
+
"@pie-players/pie-tool-periodic-table": "0.1.11",
|
|
64
|
+
"@pie-players/pie-tool-protractor": "0.1.11",
|
|
65
|
+
"@pie-players/pie-tool-ruler": "0.1.11",
|
|
66
|
+
"@pie-players/pie-tool-text-to-speech": "0.1.11",
|
|
67
|
+
"@pie-players/tts-client-server": "0.2.6",
|
|
77
68
|
"daisyui": "^5.5.18"
|
|
78
69
|
},
|
|
79
70
|
"devDependencies": {
|
|
71
|
+
"@axe-core/playwright": "^4.11.1",
|
|
80
72
|
"@biomejs/biome": "^2.3.13",
|
|
81
73
|
"@playwright/test": "^1.58.2",
|
|
82
74
|
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
@@ -13,8 +13,16 @@
|
|
|
13
13
|
}}
|
|
14
14
|
/>
|
|
15
15
|
|
|
16
|
+
<script module lang="ts">
|
|
17
|
+
const crossShellSessionDedupe = new Map<
|
|
18
|
+
string,
|
|
19
|
+
{ fingerprint: string; timestamp: number }
|
|
20
|
+
>();
|
|
21
|
+
</script>
|
|
22
|
+
|
|
16
23
|
<script lang="ts">
|
|
17
24
|
import {
|
|
25
|
+
PIE_INTERNAL_ITEM_SESSION_CHANGED_EVENT,
|
|
18
26
|
PIE_ITEM_SESSION_CHANGED_EVENT,
|
|
19
27
|
PIE_REGISTER_EVENT,
|
|
20
28
|
PIE_UNREGISTER_EVENT,
|
|
@@ -23,11 +31,28 @@
|
|
|
23
31
|
dispatchCrossBoundaryEvent,
|
|
24
32
|
type AssessmentToolkitRegionScopeContext,
|
|
25
33
|
type AssessmentToolkitShellContext,
|
|
34
|
+
type InternalItemSessionChangedDetail,
|
|
26
35
|
type ItemSessionChangedDetail,
|
|
27
36
|
type RuntimeRegistrationDetail,
|
|
28
37
|
} from "@pie-players/pie-assessment-toolkit";
|
|
38
|
+
import { normalizeItemSessionChange } from "@pie-players/pie-players-shared";
|
|
29
39
|
import { ContextProvider, ContextRoot } from "@pie-players/pie-context";
|
|
30
40
|
|
|
41
|
+
const PIE_INTERNAL_CONTENT_LOADED_EVENT = "pie-content-loaded";
|
|
42
|
+
const PIE_INTERNAL_ITEM_PLAYER_ERROR_EVENT = "pie-item-player-error";
|
|
43
|
+
type InternalContentLoadedDetail = {
|
|
44
|
+
itemId: string;
|
|
45
|
+
canonicalItemId?: string;
|
|
46
|
+
contentKind?: string;
|
|
47
|
+
detail?: unknown;
|
|
48
|
+
};
|
|
49
|
+
type InternalItemPlayerErrorDetail = {
|
|
50
|
+
itemId: string;
|
|
51
|
+
canonicalItemId?: string;
|
|
52
|
+
contentKind?: string;
|
|
53
|
+
error: unknown;
|
|
54
|
+
};
|
|
55
|
+
|
|
31
56
|
let {
|
|
32
57
|
itemId = "",
|
|
33
58
|
canonicalItemId = "",
|
|
@@ -100,24 +125,115 @@
|
|
|
100
125
|
function normalizeAndDispatchSession(event: Event): void {
|
|
101
126
|
if (!host || !itemId) return;
|
|
102
127
|
const detail = (event as CustomEvent).detail;
|
|
128
|
+
const internalPayload: InternalItemSessionChangedDetail = {
|
|
129
|
+
itemId,
|
|
130
|
+
session: detail,
|
|
131
|
+
};
|
|
132
|
+
// Internal runtime session wiring must always continue to keep item UI/state synchronized.
|
|
133
|
+
dispatchCrossBoundaryEvent(host, PIE_INTERNAL_ITEM_SESSION_CHANGED_EVENT, internalPayload);
|
|
134
|
+
// Keep public item stream response-focused; metadata-only belongs on canonical session-changed.
|
|
135
|
+
const normalized = normalizeItemSessionChange({
|
|
136
|
+
itemId,
|
|
137
|
+
sessionDetail: detail,
|
|
138
|
+
});
|
|
139
|
+
if (normalized.intent === "metadata-only" || !normalized.session) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
103
142
|
const payload: ItemSessionChangedDetail = {
|
|
104
143
|
itemId,
|
|
105
144
|
canonicalItemId: canonicalItemId || itemId,
|
|
106
|
-
session:
|
|
145
|
+
session: normalized.session,
|
|
107
146
|
};
|
|
108
147
|
dispatchCrossBoundaryEvent(host, PIE_ITEM_SESSION_CHANGED_EVENT, payload);
|
|
109
|
-
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function dispatchLoaded(detail: unknown): void {
|
|
151
|
+
if (!host || !itemId) return;
|
|
152
|
+
const payload: InternalContentLoadedDetail = {
|
|
153
|
+
itemId,
|
|
154
|
+
canonicalItemId: canonicalItemId || itemId,
|
|
155
|
+
contentKind,
|
|
156
|
+
detail,
|
|
157
|
+
};
|
|
158
|
+
dispatchCrossBoundaryEvent(host, PIE_INTERNAL_CONTENT_LOADED_EVENT, payload);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function dispatchPlayerError(error: unknown): void {
|
|
162
|
+
if (!host || !itemId) return;
|
|
163
|
+
const payload: InternalItemPlayerErrorDetail = {
|
|
164
|
+
itemId,
|
|
165
|
+
canonicalItemId: canonicalItemId || itemId,
|
|
166
|
+
contentKind,
|
|
167
|
+
error,
|
|
168
|
+
};
|
|
169
|
+
dispatchCrossBoundaryEvent(host, PIE_INTERNAL_ITEM_PLAYER_ERROR_EVENT, payload);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function createSessionEventFingerprint(detail: unknown): string {
|
|
173
|
+
const semanticDetail =
|
|
174
|
+
detail && typeof detail === "object"
|
|
175
|
+
? { ...(detail as Record<string, unknown>) }
|
|
176
|
+
: detail;
|
|
177
|
+
if (semanticDetail && typeof semanticDetail === "object") {
|
|
178
|
+
delete (semanticDetail as Record<string, unknown>).timestamp;
|
|
179
|
+
delete (semanticDetail as Record<string, unknown>).sourceRuntimeId;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
return JSON.stringify(semanticDetail);
|
|
183
|
+
} catch {
|
|
184
|
+
return String(semanticDetail);
|
|
185
|
+
}
|
|
110
186
|
}
|
|
111
187
|
|
|
112
188
|
$effect(() => {
|
|
113
189
|
if (!host) return;
|
|
114
190
|
dispatchRegistration(PIE_REGISTER_EVENT);
|
|
115
191
|
|
|
116
|
-
const
|
|
192
|
+
const seenSessionEvents = new WeakSet<Event>();
|
|
193
|
+
let lastForwardedFingerprint = "";
|
|
194
|
+
const CROSS_SHELL_DEDUPE_WINDOW_MS = 500;
|
|
195
|
+
const onSessionChanged = (event: Event) => {
|
|
196
|
+
// Keep framework surface minimal: normalize these descendant events and do not
|
|
197
|
+
// leak raw item-player session events to external listeners.
|
|
198
|
+
event.stopPropagation();
|
|
199
|
+
// Some players emit `session-changed`, others emit `sessionchanged`.
|
|
200
|
+
// Guard against duplicate forwarding when both fire for the same payload.
|
|
201
|
+
if (seenSessionEvents.has(event)) return;
|
|
202
|
+
seenSessionEvents.add(event);
|
|
203
|
+
const fingerprint = createSessionEventFingerprint((event as CustomEvent).detail);
|
|
204
|
+
if (fingerprint === lastForwardedFingerprint) return;
|
|
205
|
+
const dedupeKey = itemId || canonicalItemId || "__unknown-item__";
|
|
206
|
+
const now = Date.now();
|
|
207
|
+
const lastCrossShell = crossShellSessionDedupe.get(dedupeKey);
|
|
208
|
+
if (
|
|
209
|
+
lastCrossShell &&
|
|
210
|
+
lastCrossShell.fingerprint === fingerprint &&
|
|
211
|
+
now - lastCrossShell.timestamp < CROSS_SHELL_DEDUPE_WINDOW_MS
|
|
212
|
+
) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
crossShellSessionDedupe.set(dedupeKey, { fingerprint, timestamp: now });
|
|
216
|
+
lastForwardedFingerprint = fingerprint;
|
|
217
|
+
normalizeAndDispatchSession(event);
|
|
218
|
+
};
|
|
117
219
|
host.addEventListener("sessionchanged", onSessionChanged);
|
|
220
|
+
host.addEventListener("session-changed", onSessionChanged);
|
|
221
|
+
const onLoadComplete = (event: Event) => {
|
|
222
|
+
event.stopPropagation();
|
|
223
|
+
dispatchLoaded((event as CustomEvent).detail);
|
|
224
|
+
};
|
|
225
|
+
const onPlayerError = (event: Event) => {
|
|
226
|
+
event.stopPropagation();
|
|
227
|
+
dispatchPlayerError((event as CustomEvent).detail);
|
|
228
|
+
};
|
|
229
|
+
host.addEventListener("load-complete", onLoadComplete);
|
|
230
|
+
host.addEventListener("player-error", onPlayerError);
|
|
118
231
|
|
|
119
232
|
return () => {
|
|
120
233
|
host?.removeEventListener("sessionchanged", onSessionChanged);
|
|
234
|
+
host?.removeEventListener("session-changed", onSessionChanged);
|
|
235
|
+
host?.removeEventListener("load-complete", onLoadComplete);
|
|
236
|
+
host?.removeEventListener("player-error", onPlayerError);
|
|
121
237
|
dispatchRegistration(PIE_UNREGISTER_EVENT);
|
|
122
238
|
};
|
|
123
239
|
});
|
|
@@ -26,6 +26,21 @@
|
|
|
26
26
|
} from "@pie-players/pie-assessment-toolkit";
|
|
27
27
|
import { ContextProvider, ContextRoot } from "@pie-players/pie-context";
|
|
28
28
|
|
|
29
|
+
const PIE_INTERNAL_CONTENT_LOADED_EVENT = "pie-content-loaded";
|
|
30
|
+
const PIE_INTERNAL_ITEM_PLAYER_ERROR_EVENT = "pie-item-player-error";
|
|
31
|
+
type InternalContentLoadedDetail = {
|
|
32
|
+
itemId: string;
|
|
33
|
+
canonicalItemId?: string;
|
|
34
|
+
contentKind?: string;
|
|
35
|
+
detail?: unknown;
|
|
36
|
+
};
|
|
37
|
+
type InternalItemPlayerErrorDetail = {
|
|
38
|
+
itemId: string;
|
|
39
|
+
canonicalItemId?: string;
|
|
40
|
+
contentKind?: string;
|
|
41
|
+
error: unknown;
|
|
42
|
+
};
|
|
43
|
+
|
|
29
44
|
let {
|
|
30
45
|
itemId = "",
|
|
31
46
|
canonicalItemId = "",
|
|
@@ -95,11 +110,45 @@
|
|
|
95
110
|
dispatchCrossBoundaryEvent(host, eventName, detail);
|
|
96
111
|
}
|
|
97
112
|
|
|
113
|
+
function dispatchLoaded(detail: unknown): void {
|
|
114
|
+
if (!host || !itemId) return;
|
|
115
|
+
const payload: InternalContentLoadedDetail = {
|
|
116
|
+
itemId,
|
|
117
|
+
canonicalItemId: canonicalItemId || itemId,
|
|
118
|
+
contentKind,
|
|
119
|
+
detail,
|
|
120
|
+
};
|
|
121
|
+
dispatchCrossBoundaryEvent(host, PIE_INTERNAL_CONTENT_LOADED_EVENT, payload);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function dispatchPlayerError(error: unknown): void {
|
|
125
|
+
if (!host || !itemId) return;
|
|
126
|
+
const payload: InternalItemPlayerErrorDetail = {
|
|
127
|
+
itemId,
|
|
128
|
+
canonicalItemId: canonicalItemId || itemId,
|
|
129
|
+
contentKind,
|
|
130
|
+
error,
|
|
131
|
+
};
|
|
132
|
+
dispatchCrossBoundaryEvent(host, PIE_INTERNAL_ITEM_PLAYER_ERROR_EVENT, payload);
|
|
133
|
+
}
|
|
134
|
+
|
|
98
135
|
$effect(() => {
|
|
99
136
|
if (!host) return;
|
|
100
137
|
dispatchRegistration(PIE_REGISTER_EVENT);
|
|
138
|
+
const onLoadComplete = (event: Event) => {
|
|
139
|
+
event.stopPropagation();
|
|
140
|
+
dispatchLoaded((event as CustomEvent).detail);
|
|
141
|
+
};
|
|
142
|
+
const onPlayerError = (event: Event) => {
|
|
143
|
+
event.stopPropagation();
|
|
144
|
+
dispatchPlayerError((event as CustomEvent).detail);
|
|
145
|
+
};
|
|
146
|
+
host.addEventListener("load-complete", onLoadComplete);
|
|
147
|
+
host.addEventListener("player-error", onPlayerError);
|
|
101
148
|
|
|
102
149
|
return () => {
|
|
150
|
+
host?.removeEventListener("load-complete", onLoadComplete);
|
|
151
|
+
host?.removeEventListener("player-error", onPlayerError);
|
|
103
152
|
dispatchRegistration(PIE_UNREGISTER_EVENT);
|
|
104
153
|
};
|
|
105
154
|
});
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
section: { type: "Object", reflect: false },
|
|
9
9
|
sectionId: { attribute: "section-id", type: "String" },
|
|
10
10
|
attemptId: { attribute: "attempt-id", type: "String" },
|
|
11
|
-
view: { type: "String" },
|
|
12
11
|
playerType: { attribute: "player-type", type: "String" },
|
|
13
12
|
player: { type: "Object", reflect: false },
|
|
14
13
|
lazyInit: { attribute: "lazy-init", type: "Boolean" },
|
|
@@ -24,52 +23,33 @@
|
|
|
24
23
|
|
|
25
24
|
<script lang="ts">
|
|
26
25
|
import "@pie-players/pie-assessment-toolkit/components/pie-assessment-toolkit-element";
|
|
26
|
+
import "@pie-players/pie-tool-annotation-toolbar";
|
|
27
27
|
import {
|
|
28
28
|
createDefaultPersonalNeedsProfile,
|
|
29
29
|
} from "@pie-players/pie-assessment-toolkit";
|
|
30
|
+
import {
|
|
31
|
+
normalizeToolsConfig,
|
|
32
|
+
resolveToolsForLevel,
|
|
33
|
+
} from "@pie-players/pie-assessment-toolkit";
|
|
30
34
|
import { createEventDispatcher } from "svelte";
|
|
31
35
|
import { SectionController } from "../controllers/SectionController.js";
|
|
32
36
|
import type { SectionCompositionModel } from "../controllers/types.js";
|
|
33
37
|
import type { AssessmentSection } from "@pie-players/pie-players-shared/types";
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
currentItem: null,
|
|
44
|
-
isPageMode: false,
|
|
45
|
-
itemSessionsByItemId: {},
|
|
46
|
-
testAttemptSession: null,
|
|
47
|
-
};
|
|
48
|
-
const DEFAULT_ASSESSMENT_ID = "section-demo-direct";
|
|
49
|
-
const DEFAULT_PLAYER_TYPE = "iife";
|
|
50
|
-
const DEFAULT_LAZY_INIT = true;
|
|
51
|
-
const DEFAULT_ISOLATION = "inherit";
|
|
52
|
-
const LEGACY_RUNTIME_WARNING_KEY = "pie-section-player-base:legacy-runtime-props";
|
|
53
|
-
const warnedKeys = new Set<string>();
|
|
54
|
-
type RuntimeConfig = {
|
|
55
|
-
assessmentId?: string;
|
|
56
|
-
playerType?: string;
|
|
57
|
-
player?: Record<string, unknown> | null;
|
|
58
|
-
lazyInit?: boolean;
|
|
59
|
-
tools?: Record<string, unknown> | null;
|
|
60
|
-
accessibility?: Record<string, unknown> | null;
|
|
61
|
-
coordinator?: unknown;
|
|
62
|
-
createSectionController?: unknown;
|
|
63
|
-
isolation?: string;
|
|
64
|
-
env?: Record<string, unknown>;
|
|
65
|
-
};
|
|
38
|
+
import { EMPTY_COMPOSITION } from "./shared/composition.js";
|
|
39
|
+
import {
|
|
40
|
+
DEFAULT_ASSESSMENT_ID,
|
|
41
|
+
DEFAULT_ENV,
|
|
42
|
+
DEFAULT_ISOLATION,
|
|
43
|
+
DEFAULT_LAZY_INIT,
|
|
44
|
+
DEFAULT_PLAYER_TYPE,
|
|
45
|
+
type RuntimeConfig,
|
|
46
|
+
} from "./shared/section-player-runtime.js";
|
|
66
47
|
let {
|
|
67
48
|
assessmentId = DEFAULT_ASSESSMENT_ID,
|
|
68
49
|
runtime = null as RuntimeConfig | null,
|
|
69
50
|
section = null as AssessmentSection | null,
|
|
70
51
|
sectionId = "",
|
|
71
52
|
attemptId = "",
|
|
72
|
-
view = "candidate",
|
|
73
53
|
playerType = DEFAULT_PLAYER_TYPE,
|
|
74
54
|
player = null as Record<string, unknown> | null,
|
|
75
55
|
lazyInit = DEFAULT_LAZY_INIT,
|
|
@@ -82,7 +62,8 @@
|
|
|
82
62
|
} = $props();
|
|
83
63
|
|
|
84
64
|
let toolkitElement = $state<any>(null);
|
|
85
|
-
let
|
|
65
|
+
let activeToolkitCoordinator = $state<any>(null);
|
|
66
|
+
let lastCompositionVersion = $state(-1);
|
|
86
67
|
type BaseSectionPlayerEvents = {
|
|
87
68
|
"composition-changed": { composition: SectionCompositionModel };
|
|
88
69
|
"toolkit-ready": Record<string, unknown>;
|
|
@@ -106,7 +87,7 @@
|
|
|
106
87
|
() => runtime?.createSectionController ?? createSectionController,
|
|
107
88
|
);
|
|
108
89
|
const effectiveIsolation = $derived.by(() => runtime?.isolation ?? isolation);
|
|
109
|
-
const effectiveEnv = $derived.by(() => runtime?.env ?? env ??
|
|
90
|
+
const effectiveEnv = $derived.by(() => runtime?.env ?? env ?? DEFAULT_ENV);
|
|
110
91
|
let resolvedSection = $derived.by(() => {
|
|
111
92
|
if (!section) return null;
|
|
112
93
|
const sectionAny = section as any;
|
|
@@ -127,27 +108,18 @@
|
|
|
127
108
|
dispatch(name, detail);
|
|
128
109
|
}
|
|
129
110
|
|
|
130
|
-
function getCompositionSignature(
|
|
131
|
-
model: SectionCompositionModel | null | undefined,
|
|
132
|
-
): string {
|
|
133
|
-
if (!model) return "";
|
|
134
|
-
return JSON.stringify({
|
|
135
|
-
sectionId: model.section?.identifier || "",
|
|
136
|
-
currentItemIndex: model.currentItemIndex ?? -1,
|
|
137
|
-
itemIds: (model.items || []).map((item) => item?.id || ""),
|
|
138
|
-
passageIds: (model.passages || []).map((passage) => passage?.id || ""),
|
|
139
|
-
sessionByItem: Object.entries(model.itemSessionsByItemId || {})
|
|
140
|
-
.sort(([left], [right]) => left.localeCompare(right))
|
|
141
|
-
.map(([itemId, session]) => [itemId, JSON.stringify(session ?? null)]),
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
111
|
function handleCompositionChanged(event: Event): void {
|
|
146
|
-
const detail = (event as CustomEvent<{
|
|
112
|
+
const detail = (event as CustomEvent<{
|
|
113
|
+
composition?: SectionCompositionModel;
|
|
114
|
+
version?: number;
|
|
115
|
+
}>).detail;
|
|
147
116
|
const nextComposition = detail?.composition || EMPTY_COMPOSITION;
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
117
|
+
const nextVersion =
|
|
118
|
+
typeof detail?.version === "number"
|
|
119
|
+
? detail.version
|
|
120
|
+
: lastCompositionVersion + 1;
|
|
121
|
+
if (nextVersion === lastCompositionVersion) return;
|
|
122
|
+
lastCompositionVersion = nextVersion;
|
|
151
123
|
emit("composition-changed", {
|
|
152
124
|
composition: nextComposition,
|
|
153
125
|
});
|
|
@@ -158,37 +130,46 @@
|
|
|
158
130
|
eventName: Exclude<keyof BaseSectionPlayerEvents, "composition-changed">,
|
|
159
131
|
): void {
|
|
160
132
|
const detail = (event as CustomEvent).detail as Record<string, unknown>;
|
|
133
|
+
if (eventName === "toolkit-ready" && detail?.coordinator) {
|
|
134
|
+
activeToolkitCoordinator = detail.coordinator;
|
|
135
|
+
}
|
|
161
136
|
emit(eventName, detail || ({} as Record<string, unknown>));
|
|
162
137
|
}
|
|
163
138
|
|
|
139
|
+
const normalizedToolsConfig = $derived.by(() =>
|
|
140
|
+
normalizeToolsConfig((effectiveTools || {}) as any),
|
|
141
|
+
);
|
|
142
|
+
const annotationToolbarPlacementEnabled = $derived.by(() => {
|
|
143
|
+
const levels: Array<"section" | "item" | "passage"> = [
|
|
144
|
+
"section",
|
|
145
|
+
"item",
|
|
146
|
+
"passage",
|
|
147
|
+
];
|
|
148
|
+
return levels.some((level) =>
|
|
149
|
+
resolveToolsForLevel(normalizedToolsConfig as any, level).includes(
|
|
150
|
+
"annotationToolbar",
|
|
151
|
+
),
|
|
152
|
+
);
|
|
153
|
+
});
|
|
154
|
+
const annotationToolbarProviderEnabled = $derived.by(() =>
|
|
155
|
+
activeToolkitCoordinator?.isToolEnabled?.("annotationToolbar") ??
|
|
156
|
+
((normalizedToolsConfig as any)?.providers?.annotationToolbar?.enabled !==
|
|
157
|
+
false),
|
|
158
|
+
);
|
|
159
|
+
const shouldRenderAnnotationToolbar = $derived(
|
|
160
|
+
Boolean(
|
|
161
|
+
activeToolkitCoordinator &&
|
|
162
|
+
annotationToolbarPlacementEnabled &&
|
|
163
|
+
annotationToolbarProviderEnabled,
|
|
164
|
+
),
|
|
165
|
+
);
|
|
166
|
+
|
|
164
167
|
$effect(() => {
|
|
165
168
|
if (!toolkitElement) return;
|
|
166
169
|
toolkitElement.createSectionController =
|
|
167
170
|
effectiveCreateSectionController || (() => new SectionController());
|
|
168
171
|
});
|
|
169
172
|
|
|
170
|
-
$effect(() => {
|
|
171
|
-
if (typeof window === "undefined" || runtime) return;
|
|
172
|
-
const usedLegacyProps: string[] = [];
|
|
173
|
-
if (assessmentId !== DEFAULT_ASSESSMENT_ID) usedLegacyProps.push("assessmentId");
|
|
174
|
-
if (playerType !== DEFAULT_PLAYER_TYPE) usedLegacyProps.push("playerType");
|
|
175
|
-
if (player !== null) usedLegacyProps.push("player");
|
|
176
|
-
if (lazyInit !== DEFAULT_LAZY_INIT) usedLegacyProps.push("lazyInit");
|
|
177
|
-
if (tools !== null) usedLegacyProps.push("tools");
|
|
178
|
-
if (accessibility !== null) usedLegacyProps.push("accessibility");
|
|
179
|
-
if (coordinator !== null) usedLegacyProps.push("coordinator");
|
|
180
|
-
if (createSectionController !== null) usedLegacyProps.push("createSectionController");
|
|
181
|
-
if (isolation !== DEFAULT_ISOLATION) usedLegacyProps.push("isolation");
|
|
182
|
-
if (env !== null) usedLegacyProps.push("env");
|
|
183
|
-
if (usedLegacyProps.length === 0) return;
|
|
184
|
-
const key = `${LEGACY_RUNTIME_WARNING_KEY}:${usedLegacyProps.sort().join(",")}`;
|
|
185
|
-
if (warnedKeys.has(key)) return;
|
|
186
|
-
warnedKeys.add(key);
|
|
187
|
-
console.warn(
|
|
188
|
-
`[pie-section-player-base] Runtime props (${usedLegacyProps.join(", ")}) are deprecated. Prefer the \`runtime\` object prop.`,
|
|
189
|
-
);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
173
|
</script>
|
|
193
174
|
|
|
194
175
|
<pie-assessment-toolkit
|
|
@@ -199,7 +180,6 @@
|
|
|
199
180
|
attempt-id={attemptId}
|
|
200
181
|
player-type={effectivePlayerType}
|
|
201
182
|
player={effectivePlayer}
|
|
202
|
-
{view}
|
|
203
183
|
env={effectiveEnv}
|
|
204
184
|
lazy-init={effectiveLazyInit}
|
|
205
185
|
tools={effectiveTools}
|
|
@@ -214,6 +194,13 @@
|
|
|
214
194
|
onruntime-owned={(event: Event) => handleToolkitEvent(event, "runtime-owned")}
|
|
215
195
|
onruntime-inherited={(event: Event) => handleToolkitEvent(event, "runtime-inherited")}
|
|
216
196
|
>
|
|
197
|
+
{#if shouldRenderAnnotationToolbar}
|
|
198
|
+
<pie-tool-annotation-toolbar
|
|
199
|
+
enabled={true}
|
|
200
|
+
ttsService={activeToolkitCoordinator.ttsService}
|
|
201
|
+
highlightCoordinator={activeToolkitCoordinator.highlightCoordinator}
|
|
202
|
+
></pie-tool-annotation-toolbar>
|
|
203
|
+
{/if}
|
|
217
204
|
<slot></slot>
|
|
218
205
|
</pie-assessment-toolkit>
|
|
219
206
|
|