@pie-players/pie-section-player 0.2.12 → 0.2.13
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.map +1 -1
- package/dist/controllers/SectionController.d.ts +5 -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 +28 -1
- package/dist/controllers/types.d.ts.map +1 -1
- package/dist/pie-item-player-B1iGN63e.js +6189 -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 +56558 -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 +23 -32
- package/src/components/ItemShellElement.svelte +10 -1
- package/src/components/PieSectionPlayerBaseElement.svelte +21 -78
- package/src/components/PieSectionPlayerSplitPaneElement.svelte +236 -295
- package/src/components/PieSectionPlayerVerticalElement.svelte +424 -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
package/README.md
CHANGED
|
@@ -1,596 +1,56 @@
|
|
|
1
1
|
# @pie-players/pie-section-player
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Section rendering package focused on the splitpane custom element:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- `pie-section-player-splitpane`
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The package no longer exposes the legacy `pie-section-player` layout orchestration API.
|
|
8
8
|
|
|
9
|
-
##
|
|
10
|
-
|
|
11
|
-
### Option 1: CDN (No Build Step)
|
|
12
|
-
|
|
13
|
-
Load directly from a CDN in your browser - no npm install or build step required:
|
|
14
|
-
|
|
15
|
-
```html
|
|
16
|
-
<!DOCTYPE html>
|
|
17
|
-
<html>
|
|
18
|
-
<head>
|
|
19
|
-
<script type="module">
|
|
20
|
-
// Import from jsdelivr CDN
|
|
21
|
-
import 'https://cdn.jsdelivr.net/npm/@pie-players/pie-section-player/dist/pie-section-player.js';
|
|
22
|
-
</script>
|
|
23
|
-
</head>
|
|
24
|
-
<body>
|
|
25
|
-
<pie-section-player id="player"></pie-section-player>
|
|
26
|
-
|
|
27
|
-
<script type="module">
|
|
28
|
-
const player = document.getElementById('player');
|
|
29
|
-
player.section = { /* your section data */ };
|
|
30
|
-
player.mode = 'gather';
|
|
31
|
-
</script>
|
|
32
|
-
</body>
|
|
33
|
-
</html>
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
**Alternative CDNs:**
|
|
37
|
-
|
|
38
|
-
- **unpkg**: `https://unpkg.com/@pie-players/pie-section-player@latest/dist/pie-section-player.js`
|
|
39
|
-
- **esm.sh**: `https://esm.sh/@pie-players/pie-section-player` (auto-resolves dependencies)
|
|
40
|
-
|
|
41
|
-
### Option 2: NPM (For Build Tools)
|
|
42
|
-
|
|
43
|
-
Install via npm for use with build tools (Vite, Webpack, etc.):
|
|
9
|
+
## Install
|
|
44
10
|
|
|
45
11
|
```bash
|
|
46
12
|
npm install @pie-players/pie-section-player
|
|
47
|
-
# or
|
|
48
|
-
bun add @pie-players/pie-section-player
|
|
49
13
|
```
|
|
50
14
|
|
|
51
|
-
**Note:** The section player requires the following peer dependencies for tool integration:
|
|
52
|
-
|
|
53
|
-
- `@pie-players/pie-tool-answer-eliminator` - Answer eliminator tool for test-taking strategies
|
|
54
|
-
- `@pie-players/pie-tool-tts-inline` - Inline TTS tool for accessibility
|
|
55
|
-
|
|
56
|
-
These are automatically resolved when using the section player as a library dependency.
|
|
57
|
-
|
|
58
15
|
## Usage
|
|
59
16
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
```html
|
|
63
|
-
<!DOCTYPE html>
|
|
64
|
-
<html>
|
|
65
|
-
<head>
|
|
66
|
-
<script type="module">
|
|
67
|
-
import '@pie-players/pie-section-player';
|
|
68
|
-
</script>
|
|
69
|
-
</head>
|
|
70
|
-
<body>
|
|
71
|
-
<pie-section-player id="player"></pie-section-player>
|
|
72
|
-
|
|
73
|
-
<script type="module">
|
|
74
|
-
const player = document.getElementById('player');
|
|
75
|
-
|
|
76
|
-
// Set section data
|
|
77
|
-
player.section = {
|
|
78
|
-
identifier: 'section-1',
|
|
79
|
-
keepTogether: true, // Page mode: all items visible
|
|
80
|
-
rubricBlocks: [
|
|
81
|
-
{
|
|
82
|
-
view: 'candidate',
|
|
83
|
-
use: 'passage',
|
|
84
|
-
passage: {
|
|
85
|
-
id: 'passage-1',
|
|
86
|
-
name: 'Reading Passage',
|
|
87
|
-
config: {
|
|
88
|
-
markup: '<reading-passage id="p1"></reading-passage>',
|
|
89
|
-
elements: {
|
|
90
|
-
'reading-passage': '@pie-element/reading-passage@latest'
|
|
91
|
-
},
|
|
92
|
-
models: [{
|
|
93
|
-
id: 'p1',
|
|
94
|
-
element: 'reading-passage',
|
|
95
|
-
content: '<p>Your passage content...</p>'
|
|
96
|
-
}]
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
],
|
|
101
|
-
assessmentItemRefs: [
|
|
102
|
-
{
|
|
103
|
-
identifier: 'q1',
|
|
104
|
-
item: {
|
|
105
|
-
id: 'item-1',
|
|
106
|
-
name: 'Question 1',
|
|
107
|
-
config: {
|
|
108
|
-
markup: '<multiple-choice id="mc1"></multiple-choice>',
|
|
109
|
-
elements: {
|
|
110
|
-
'multiple-choice': '@pie-element/multiple-choice@latest'
|
|
111
|
-
},
|
|
112
|
-
models: [{
|
|
113
|
-
id: 'mc1',
|
|
114
|
-
element: 'multiple-choice',
|
|
115
|
-
prompt: 'What is 2 + 2?',
|
|
116
|
-
choices: [/*...*/]
|
|
117
|
-
}]
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
]
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
// Set mode
|
|
125
|
-
player.mode = 'gather'; // or 'view', 'evaluate', 'author'
|
|
126
|
-
|
|
127
|
-
// Listen to events
|
|
128
|
-
player.addEventListener('section-loaded', (e) => {
|
|
129
|
-
console.log('Section loaded:', e.detail);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
player.addEventListener('item-changed', (e) => {
|
|
133
|
-
console.log('Item changed:', e.detail);
|
|
134
|
-
});
|
|
135
|
-
</script>
|
|
136
|
-
</body>
|
|
137
|
-
</html>
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### As Svelte Component
|
|
141
|
-
|
|
142
|
-
```svelte
|
|
143
|
-
<script>
|
|
144
|
-
import PieSectionPlayer from '@pie-players/pie-section-player';
|
|
145
|
-
|
|
146
|
-
let section = {
|
|
147
|
-
identifier: 'section-1',
|
|
148
|
-
keepTogether: false, // Item mode: one at a time
|
|
149
|
-
assessmentItemRefs: [/* items */]
|
|
150
|
-
};
|
|
151
|
-
</script>
|
|
152
|
-
|
|
153
|
-
<PieSectionPlayer
|
|
154
|
-
{section}
|
|
155
|
-
env={{ mode: 'gather', role: 'student' }}
|
|
156
|
-
view="candidate"
|
|
157
|
-
pageLayout="split-panel"
|
|
158
|
-
/>
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### In React
|
|
162
|
-
|
|
163
|
-
```jsx
|
|
164
|
-
import '@pie-players/pie-section-player';
|
|
165
|
-
|
|
166
|
-
function AssessmentSection({ section }) {
|
|
167
|
-
const playerRef = useRef(null);
|
|
168
|
-
|
|
169
|
-
useEffect(() => {
|
|
170
|
-
if (playerRef.current) {
|
|
171
|
-
playerRef.current.section = section;
|
|
172
|
-
playerRef.current.mode = 'gather';
|
|
173
|
-
}
|
|
174
|
-
}, [section]);
|
|
17
|
+
Import the custom-element registration entrypoint in consumers:
|
|
175
18
|
|
|
176
|
-
|
|
177
|
-
|
|
19
|
+
```ts
|
|
20
|
+
import '@pie-players/pie-section-player/components/section-player-splitpane-element';
|
|
178
21
|
```
|
|
179
22
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
| Prop | Type | Default | Description |
|
|
183
|
-
|------|------|---------|-------------|
|
|
184
|
-
| `section` | `AssessmentSection` | `null` | Section data with passages and items |
|
|
185
|
-
| `env` | `{ mode, role }` | `{ mode: 'gather', role: 'student' }` | Runtime environment |
|
|
186
|
-
| `view` | `'candidate' \| 'scorer' \| 'author' \| ...` | `'candidate'` | Current view (filters rubricBlocks) |
|
|
187
|
-
| `page-layout` | `string` | `'split-panel'` | Selected page-mode layout definition key (`split-panel`, `split-panel-composed`, `vertical`, or custom) |
|
|
188
|
-
| `layoutDefinitions` | `Record<string, ComponentDefinition>` | built-ins | Host page-layout web-component definitions |
|
|
189
|
-
| `session-state` | `{ currentItemIndex?, visitedItemIdentifiers?, itemSessions } \| null` | `null` | Host-facing session state used for section initialization and updates |
|
|
190
|
-
| `toolkitCoordinator` | `ToolkitCoordinator \| null` | `null` | Runtime toolkit coordinator for context-provided tool services |
|
|
191
|
-
| `custom-class-name` | `string` | `''` | Custom CSS class |
|
|
192
|
-
| `debug` | `string \| boolean` | `''` | Debug mode |
|
|
193
|
-
|
|
194
|
-
The section player currently renders items with the IIFE player by default.
|
|
195
|
-
|
|
196
|
-
## Session Initialization Contract
|
|
197
|
-
|
|
198
|
-
Section player accepts one session initialization input:
|
|
199
|
-
|
|
200
|
-
1. `session-state` (`{ currentItemIndex?, visitedItemIdentifiers?, itemSessions }`)
|
|
201
|
-
2. if omitted, section player starts from empty canonical attempt state
|
|
202
|
-
|
|
203
|
-
Notes:
|
|
204
|
-
|
|
205
|
-
- Backend-specific payload translation belongs to the host/integrator.
|
|
206
|
-
- `session-changed` emits `sessionState` (plus `itemSessions`) for host persistence.
|
|
207
|
-
|
|
208
|
-
## Host Integration Recipes
|
|
209
|
-
|
|
210
|
-
### Initialize from session state
|
|
211
|
-
|
|
212
|
-
```javascript
|
|
213
|
-
const player = document.querySelector("pie-section-player");
|
|
214
|
-
|
|
215
|
-
player.section = section;
|
|
216
|
-
player.sessionState = {
|
|
217
|
-
currentItemIndex: 0,
|
|
218
|
-
visitedItemIdentifiers: [],
|
|
219
|
-
itemSessions: {},
|
|
220
|
-
};
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
## Events
|
|
224
|
-
|
|
225
|
-
### `section-loaded`
|
|
226
|
-
|
|
227
|
-
Fired when the section is loaded and ready.
|
|
228
|
-
|
|
229
|
-
```javascript
|
|
230
|
-
player.addEventListener('section-loaded', (e) => {
|
|
231
|
-
console.log(e.detail);
|
|
232
|
-
// {
|
|
233
|
-
// sectionId: 'section-1',
|
|
234
|
-
// itemCount: 3,
|
|
235
|
-
// passageCount: 2,
|
|
236
|
-
// isPageMode: true
|
|
237
|
-
// }
|
|
238
|
-
});
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### `item-changed`
|
|
242
|
-
|
|
243
|
-
Fired when the current item changes (item mode only).
|
|
244
|
-
|
|
245
|
-
```javascript
|
|
246
|
-
player.addEventListener('item-changed', (e) => {
|
|
247
|
-
console.log(e.detail);
|
|
248
|
-
// {
|
|
249
|
-
// previousItemId: 'item-1',
|
|
250
|
-
// currentItemId: 'item-2',
|
|
251
|
-
// itemIndex: 1,
|
|
252
|
-
// totalItems: 3,
|
|
253
|
-
// timestamp: 1234567890
|
|
254
|
-
// }
|
|
255
|
-
});
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
### `section-complete`
|
|
259
|
-
|
|
260
|
-
Fired when all items in the section are completed.
|
|
261
|
-
|
|
262
|
-
### `player-error`
|
|
263
|
-
|
|
264
|
-
Fired when an error occurs.
|
|
265
|
-
|
|
266
|
-
### `session-changed`
|
|
267
|
-
|
|
268
|
-
Fired when an item session changes. Includes host-facing `sessionState` plus `itemSessions` so hosts can persist only the section state they care about.
|
|
269
|
-
|
|
270
|
-
```javascript
|
|
271
|
-
player.addEventListener("session-changed", (e) => {
|
|
272
|
-
const { itemId, session, sessionState, itemSessions } = e.detail;
|
|
273
|
-
|
|
274
|
-
// Host decides persistence strategy to ../../kds/pie-api-aws
|
|
275
|
-
// (immediate, debounced, checkpoint, submit).
|
|
276
|
-
});
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
## Performance Optimization
|
|
280
|
-
|
|
281
|
-
The section player automatically optimizes element loading for sections with multiple items using the same PIE elements.
|
|
282
|
-
|
|
283
|
-
### Element Aggregation
|
|
284
|
-
|
|
285
|
-
**Problem**: When rendering multiple items that use the same PIE elements (e.g., multiple multiple-choice questions), the old approach would load the same element bundle multiple times.
|
|
286
|
-
|
|
287
|
-
**Solution**: The section player now:
|
|
288
|
-
1. Aggregates all unique elements from all items before rendering
|
|
289
|
-
2. Loads each element bundle once
|
|
290
|
-
3. Items initialize from the pre-loaded registry
|
|
291
|
-
|
|
292
|
-
**Benefits:**
|
|
293
|
-
- **50%+ faster loading** for sections with repeated elements
|
|
294
|
-
- **Fewer network requests** - one bundle request per unique element (not per item)
|
|
295
|
-
- **Automatic** - no configuration needed, works out of the box
|
|
296
|
-
|
|
297
|
-
### Example Performance
|
|
298
|
-
|
|
299
|
-
Section with 5 items (3 multiple-choice, 2 hotspot):
|
|
300
|
-
|
|
301
|
-
**Before**: 5 loader calls → 2 unique + 3 cached = ~550ms
|
|
302
|
-
**After**: 1 loader call → 2 unique = ~250ms
|
|
303
|
-
|
|
304
|
-
**50% faster load time**
|
|
305
|
-
|
|
306
|
-
### Element Version Conflicts
|
|
307
|
-
|
|
308
|
-
If items require different versions of the same element, an error is thrown:
|
|
309
|
-
|
|
310
|
-
```
|
|
311
|
-
Element version conflict: pie-multiple-choice requires both
|
|
312
|
-
@pie-element/multiple-choice@10.0.0 and @pie-element/multiple-choice@11.0.1
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
**Solution**: Normalize element versions across items in your content authoring system.
|
|
316
|
-
|
|
317
|
-
### Technical Details
|
|
318
|
-
|
|
319
|
-
For implementation details and architecture, see:
|
|
320
|
-
- [ARCHITECTURE](../../docs/ARCHITECTURE.md)
|
|
321
|
-
- [TOOL_PROVIDER_SYSTEM](../../docs/TOOL_PROVIDER_SYSTEM.md)
|
|
322
|
-
|
|
323
|
-
## Rendering Modes
|
|
324
|
-
|
|
325
|
-
### Page Mode (`keepTogether: true`)
|
|
326
|
-
|
|
327
|
-
All items and passages are visible simultaneously. Ideal for:
|
|
328
|
-
- Paired passages
|
|
329
|
-
- Multi-item pages
|
|
330
|
-
- Print assessments
|
|
331
|
-
|
|
332
|
-
```javascript
|
|
333
|
-
section.keepTogether = true;
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
### Item Mode (`keepTogether: false`)
|
|
337
|
-
|
|
338
|
-
One item at a time with navigation controls. Ideal for:
|
|
339
|
-
- Linear assessments
|
|
340
|
-
- Adaptive tests
|
|
341
|
-
- Item-by-item delivery
|
|
342
|
-
|
|
343
|
-
```javascript
|
|
344
|
-
section.keepTogether = false;
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
## Methods
|
|
348
|
-
|
|
349
|
-
### `navigateNext()`
|
|
350
|
-
|
|
351
|
-
Navigate to the next item in the current section (item mode only).
|
|
352
|
-
|
|
353
|
-
```javascript
|
|
354
|
-
player.navigateNext();
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
### `navigatePrevious()`
|
|
358
|
-
|
|
359
|
-
Navigate to the previous item in the current section (item mode only).
|
|
360
|
-
|
|
361
|
-
```javascript
|
|
362
|
-
player.navigatePrevious();
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
### `getNavigationState()`
|
|
366
|
-
|
|
367
|
-
Get the current navigation state.
|
|
368
|
-
|
|
369
|
-
```javascript
|
|
370
|
-
const state = player.getNavigationState();
|
|
371
|
-
// {
|
|
372
|
-
// currentIndex: 0,
|
|
373
|
-
// totalItems: 3,
|
|
374
|
-
// canNext: true,
|
|
375
|
-
// canPrevious: false,
|
|
376
|
-
// isLoading: false
|
|
377
|
-
// }
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
## Passage Handling
|
|
381
|
-
|
|
382
|
-
The section player extracts passages from two sources:
|
|
383
|
-
|
|
384
|
-
1. **Section-level passages** (QTI 3.0 style) - `rubricBlocks` where `use="passage"`
|
|
385
|
-
2. **Item-linked passages** (legacy PIE) - `item.passage`
|
|
386
|
-
|
|
387
|
-
Passages are automatically deduplicated by ID.
|
|
388
|
-
|
|
389
|
-
## Assessment Toolkit Integration
|
|
390
|
-
|
|
391
|
-
The section player integrates with the [PIE Assessment Toolkit](../assessment-toolkit/) for centralized service management via **ToolkitCoordinator**.
|
|
392
|
-
|
|
393
|
-
The section player remains backend-agnostic by design. Hosts translate backend payloads into canonical `TestAttemptSession`, pass that into section player, then listen to `session-changed` and persist updates using host-owned API logic.
|
|
394
|
-
|
|
395
|
-
### Using ToolkitCoordinator (Recommended)
|
|
396
|
-
|
|
397
|
-
The **ToolkitCoordinator** provides a single entry point for all toolkit services, simplifying initialization:
|
|
398
|
-
|
|
399
|
-
```javascript
|
|
400
|
-
import { ToolkitCoordinator } from '@pie-players/pie-assessment-toolkit';
|
|
401
|
-
|
|
402
|
-
// Create coordinator with configuration
|
|
403
|
-
const coordinator = new ToolkitCoordinator({
|
|
404
|
-
assessmentId: 'my-assessment',
|
|
405
|
-
tools: {
|
|
406
|
-
tts: { enabled: true, defaultVoice: 'en-US' },
|
|
407
|
-
answerEliminator: { enabled: true }
|
|
408
|
-
},
|
|
409
|
-
accessibility: {
|
|
410
|
-
catalogs: assessment.accessibilityCatalogs || [],
|
|
411
|
-
language: 'en-US'
|
|
412
|
-
}
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
// Pass to section player as JavaScript property
|
|
416
|
-
const player = document.getElementById('player');
|
|
417
|
-
player.toolkitCoordinator = coordinator;
|
|
418
|
-
player.section = mySection;
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
**Benefits:**
|
|
422
|
-
- **Single initialization point**: One coordinator instead of 5+ services
|
|
423
|
-
- **Centralized configuration**: Tool settings in one place
|
|
424
|
-
- **Automatic service wiring**: Services work together automatically
|
|
425
|
-
- **Element-level tool state**: Answer eliminations, highlights tracked per element
|
|
426
|
-
- **State separation**: Tool state (ephemeral) separate from session data (persistent)
|
|
427
|
-
|
|
428
|
-
### What the Coordinator Provides
|
|
429
|
-
|
|
430
|
-
The coordinator owns and orchestrates all toolkit services:
|
|
431
|
-
|
|
432
|
-
```typescript
|
|
433
|
-
coordinator.ttsService // Text-to-speech service
|
|
434
|
-
coordinator.toolCoordinator // Tool visibility and z-index management
|
|
435
|
-
coordinator.highlightCoordinator // TTS and annotation highlights
|
|
436
|
-
coordinator.elementToolStateStore // Element-level tool state (answer eliminator, etc.)
|
|
437
|
-
coordinator.catalogResolver // QTI 3.0 accessibility catalogs for SSML
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
### Automatic Features
|
|
441
|
-
|
|
442
|
-
When using the coordinator, the section player automatically:
|
|
443
|
-
|
|
444
|
-
1. **Extracts services** from the coordinator
|
|
445
|
-
2. **Generates section ID** (from `section.identifier` or auto-generated)
|
|
446
|
-
3. **Provides a runtime context scope** for toolkit and tool components
|
|
447
|
-
4. **Extracts SSML** from embedded `<speak>` tags
|
|
448
|
-
5. **Manages catalog lifecycle** (add on item load, clear on navigation)
|
|
449
|
-
6. **Renders TTS tools** in passage/item headers
|
|
450
|
-
7. **Tracks element-level state** with global uniqueness
|
|
451
|
-
|
|
452
|
-
### Runtime Contract
|
|
453
|
-
|
|
454
|
-
Section player runtime dependencies are coordinated via
|
|
455
|
-
`toolkitCoordinator` and provided downstream through
|
|
456
|
-
`assessmentToolkitRuntimeContext`. Passing individual toolkit services to
|
|
457
|
-
child tools/components is no longer a supported integration pattern.
|
|
458
|
-
`toolkitCoordinator` may be provided by the host, or section player can create
|
|
459
|
-
one lazily when not supplied.
|
|
460
|
-
|
|
461
|
-
### SSML Extraction
|
|
462
|
-
|
|
463
|
-
✅ **Automatic SSML Extraction**: The section player automatically extracts embedded `<speak>` tags from item and passage content, converting them into QTI 3.0 accessibility catalogs at runtime.
|
|
464
|
-
|
|
465
|
-
**Benefits:**
|
|
466
|
-
- Authors embed SSML directly in content (no separate catalog files)
|
|
467
|
-
- Proper pronunciation of technical terms and math expressions
|
|
468
|
-
- Emphasis and pacing control via SSML
|
|
469
|
-
- Automatic catalog generation and registration
|
|
470
|
-
|
|
471
|
-
**Example:**
|
|
472
|
-
|
|
473
|
-
Authors create content with embedded SSML:
|
|
474
|
-
```typescript
|
|
475
|
-
{
|
|
476
|
-
config: {
|
|
477
|
-
models: [{
|
|
478
|
-
prompt: `<div>
|
|
479
|
-
<speak>Solve <prosody rate="slow">x squared, plus two x</prosody>.</speak>
|
|
480
|
-
<p>Solve x² + 2x = 0</p>
|
|
481
|
-
</div>`
|
|
482
|
-
}]
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
```
|
|
486
|
-
|
|
487
|
-
At runtime, the section player:
|
|
488
|
-
1. Extracts SSML and generates catalog entry
|
|
489
|
-
2. Cleans visual markup (removes SSML tags)
|
|
490
|
-
3. Adds `data-catalog-id` attribute for TTS lookup
|
|
491
|
-
4. Registers catalog with AccessibilityCatalogResolver
|
|
492
|
-
|
|
493
|
-
**Result:** TTS uses proper math pronunciation while visual display shows clean HTML.
|
|
494
|
-
|
|
495
|
-
See [TOOL_PROVIDER_SYSTEM](../../docs/TOOL_PROVIDER_SYSTEM.md) and [TOOL_HOST_CONTRACT](../../docs/TOOL_HOST_CONTRACT.md) for complete details.
|
|
496
|
-
|
|
497
|
-
### Element-Level Tool State
|
|
498
|
-
|
|
499
|
-
Tool state (answer eliminations, highlights, etc.) is tracked at the **element level** using globally unique composite keys:
|
|
500
|
-
|
|
501
|
-
**Format**: `${assessmentId}:${sectionId}:${itemId}:${elementId}`
|
|
502
|
-
|
|
503
|
-
**Example**: `"demo-assessment:section-1:question-1:mc1"`
|
|
504
|
-
|
|
505
|
-
**Benefits:**
|
|
506
|
-
- Each PIE element has independent tool state
|
|
507
|
-
- No cross-item contamination
|
|
508
|
-
- Persists across section navigation
|
|
509
|
-
- Separate from PIE session data (not sent to server)
|
|
510
|
-
|
|
511
|
-
### Runtime Context Flow
|
|
512
|
-
|
|
513
|
-
`pie-section-player` now provides orchestration/runtime dependencies through a
|
|
514
|
-
context scope rooted at the section-player container. Components keep explicit
|
|
515
|
-
props for direct contracts (`item`, `passage`, layout inputs), and consume
|
|
516
|
-
ambient runtime concerns from context (`toolCoordinator`, TTS services, IDs).
|
|
517
|
-
|
|
518
|
-
```
|
|
519
|
-
pie-section-player (provides runtime context)
|
|
520
|
-
↓
|
|
521
|
-
layouts / item shells (explicit composition + item contracts)
|
|
522
|
-
↓
|
|
523
|
-
pie-item-toolbar + tool components (consume context + explicit item scope)
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
This reduces prop-drilling through intermediate layout components while keeping
|
|
527
|
-
public component contracts explicit.
|
|
528
|
-
|
|
529
|
-
### Demos
|
|
530
|
-
|
|
531
|
-
See the [section-demos](../../apps/section-demos/) for complete examples:
|
|
532
|
-
|
|
533
|
-
- **Three Questions Demo**: Element-level answer eliminator with ToolkitCoordinator
|
|
534
|
-
- **TTS Integration Demo**: Coordinator with TTS service
|
|
535
|
-
- **Paired Passages Demo**: Multi-section with cross-section state persistence
|
|
536
|
-
|
|
537
|
-
## Styling
|
|
538
|
-
|
|
539
|
-
The web component uses Shadow DOM mode `'none'`, so you can style it with global CSS:
|
|
540
|
-
|
|
541
|
-
```css
|
|
542
|
-
.pie-section-player {
|
|
543
|
-
max-width: 1200px;
|
|
544
|
-
margin: 0 auto;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
.pie-section-player .pie-section-player__passages-section {
|
|
548
|
-
background: #f5f5f5;
|
|
549
|
-
padding: 1rem;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
.pie-section-player .pie-section-player__item-content {
|
|
553
|
-
border: 1px solid #ddd;
|
|
554
|
-
margin-bottom: 1rem;
|
|
555
|
-
}
|
|
556
|
-
```
|
|
557
|
-
|
|
558
|
-
## CDN Usage
|
|
23
|
+
Render in HTML/Svelte/JSX:
|
|
559
24
|
|
|
560
25
|
```html
|
|
561
|
-
<
|
|
562
|
-
import 'https://cdn.jsdelivr.net/npm/@pie-players/pie-section-player/dist/pie-section-player.js';
|
|
563
|
-
</script>
|
|
564
|
-
|
|
565
|
-
<pie-section-player id="player"></pie-section-player>
|
|
26
|
+
<pie-section-player-splitpane></pie-section-player-splitpane>
|
|
566
27
|
```
|
|
567
28
|
|
|
568
|
-
|
|
29
|
+
Set complex values (`runtime`, `section`, `env`) as JS properties.
|
|
569
30
|
|
|
570
|
-
|
|
31
|
+
## Runtime Inputs
|
|
571
32
|
|
|
572
|
-
|
|
573
|
-
import type { AssessmentSection } from '@pie-players/pie-players-shared/types';
|
|
574
|
-
import PieSectionPlayer from '@pie-players/pie-section-player';
|
|
33
|
+
`pie-section-player-splitpane` supports:
|
|
575
34
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
35
|
+
- `runtime` (object): coordinator/tools/player runtime bundle
|
|
36
|
+
- `section` (object): assessment section payload
|
|
37
|
+
- `env` (object): `{ mode, role }`
|
|
38
|
+
- `toolbar-position` (string): `top|right|bottom|left|none`
|
|
39
|
+
- `show-toolbar` (boolean)
|
|
581
40
|
|
|
582
|
-
|
|
583
|
-
player.section = section;
|
|
584
|
-
```
|
|
41
|
+
See `apps/section-demos/src/routes/demo/[[id]]/+page.svelte` for an end-to-end host integration.
|
|
585
42
|
|
|
586
|
-
##
|
|
43
|
+
## Exports
|
|
587
44
|
|
|
588
|
-
|
|
589
|
-
- Firefox 88+
|
|
590
|
-
- Safari 14+
|
|
45
|
+
Published exports are intentionally minimal:
|
|
591
46
|
|
|
592
|
-
|
|
47
|
+
- `@pie-players/pie-section-player`
|
|
48
|
+
- `@pie-players/pie-section-player/components/section-player-splitpane-element`
|
|
593
49
|
|
|
594
|
-
##
|
|
50
|
+
## Development
|
|
595
51
|
|
|
596
|
-
|
|
52
|
+
```bash
|
|
53
|
+
bun run --cwd packages/section-player dev
|
|
54
|
+
bun run --cwd packages/section-player check
|
|
55
|
+
bun run --cwd packages/section-player build
|
|
56
|
+
```
|
|
@@ -6,8 +6,5 @@ export interface ComponentDefinition {
|
|
|
6
6
|
props?: Record<string, unknown>;
|
|
7
7
|
}
|
|
8
8
|
export type PlayerDefinitionMap = Record<string, ComponentDefinition>;
|
|
9
|
-
export type LayoutDefinitionMap = Record<string, ComponentDefinition>;
|
|
10
9
|
export declare const DEFAULT_PLAYER_DEFINITIONS: PlayerDefinitionMap;
|
|
11
|
-
export declare const DEFAULT_LAYOUT_DEFINITIONS: LayoutDefinitionMap;
|
|
12
|
-
export declare function mergeComponentDefinitions<T extends Record<string, ComponentDefinition>>(defaults: T, overrides?: Partial<Record<string, ComponentDefinition>>): Record<string, ComponentDefinition>;
|
|
13
10
|
//# sourceMappingURL=component-definitions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-definitions.d.ts","sourceRoot":"","sources":["../src/component-definitions.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;AAE3D,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"component-definitions.d.ts","sourceRoot":"","sources":["../src/component-definitions.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;AAE3D,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AAEtE,eAAO,MAAM,0BAA0B,EAAE,mBAgCxC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section-player-vertical-element.d.ts","sourceRoot":"","sources":["../../src/components/section-player-vertical-element.ts"],"names":[],"mappings":"AAAA,OAAO,0CAA0C,CAAC;AAElD,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ItemEntity } from '../../../../players-shared/dist/types/index.d.ts';
|
|
2
|
+
import { SectionCompositionModel } from '../../controllers/types.js';
|
|
3
|
+
export declare const EMPTY_ITEM_SESSION: Record<string, unknown>;
|
|
4
|
+
export declare const EMPTY_COMPOSITION: SectionCompositionModel;
|
|
5
|
+
export declare function getEntityTitle(entity: unknown): string;
|
|
6
|
+
export declare function getCanonicalItemIdForItem(compositionModel: SectionCompositionModel, item: ItemEntity): string;
|
|
7
|
+
export declare function getSessionForItem(compositionModel: SectionCompositionModel, item: ItemEntity): unknown;
|
|
8
|
+
export declare function getSessionForItemOrEmpty(compositionModel: SectionCompositionModel, item: ItemEntity): Record<string, unknown>;
|
|
9
|
+
//# sourceMappingURL=composition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composition.d.ts","sourceRoot":"","sources":["../../../src/components/shared/composition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uCAAuC,CAAC;AACxE,OAAO,KAAK,EAEX,uBAAuB,EACvB,MAAM,4BAA4B,CAAC;AAEpC,eAAO,MAAM,kBAAkB,EAA2B,MAAM,CAC/D,MAAM,EACN,OAAO,CACP,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,uBAc/B,CAAC;AAEF,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAGtD;AAED,wBAAgB,yBAAyB,CACxC,gBAAgB,EAAE,uBAAuB,EACzC,IAAI,EAAE,UAAU,GACd,MAAM,CAGR;AAED,wBAAgB,iBAAiB,CAChC,gBAAgB,EAAE,uBAAuB,EACzC,IAAI,EAAE,UAAU,GACd,OAAO,CAGT;AAED,wBAAgB,wBAAwB,CACvC,gBAAgB,EAAE,uBAAuB,EACzC,IAAI,EAAE,UAAU,GACd,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAGzB"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type PlayerElementParams = {
|
|
2
|
+
config: Record<string, unknown>;
|
|
3
|
+
env: Record<string, unknown>;
|
|
4
|
+
session?: Record<string, unknown>;
|
|
5
|
+
attributes?: Record<string, string>;
|
|
6
|
+
props?: Record<string, unknown>;
|
|
7
|
+
skipElementLoading?: boolean;
|
|
8
|
+
};
|
|
9
|
+
type PlayerActionOptions = {
|
|
10
|
+
stateKey: string;
|
|
11
|
+
setSkipElementLoadingOnce?: boolean;
|
|
12
|
+
includeSessionRefInState?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare function createPlayerAction(options: PlayerActionOptions): (node: HTMLElement, params: PlayerElementParams) => {
|
|
15
|
+
update(nextParams: PlayerElementParams): void;
|
|
16
|
+
};
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=player-action.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"player-action.d.ts","sourceRoot":"","sources":["../../../src/components/shared/player-action.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAUF,KAAK,mBAAmB,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACnC,CAAC;AAaF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,IACtD,MAAM,WAAW,EAAE,QAAQ,mBAAmB;uBAGjC,mBAAmB;EAKxC"}
|