@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.
Files changed (62) hide show
  1. package/README.md +28 -568
  2. package/dist/component-definitions.d.ts +0 -3
  3. package/dist/component-definitions.d.ts.map +1 -1
  4. package/dist/components/section-player-vertical-element.d.ts +2 -0
  5. package/dist/components/section-player-vertical-element.d.ts.map +1 -0
  6. package/dist/components/shared/composition.d.ts +9 -0
  7. package/dist/components/shared/composition.d.ts.map +1 -0
  8. package/dist/components/shared/player-action.d.ts +18 -0
  9. package/dist/components/shared/player-action.d.ts.map +1 -0
  10. package/dist/components/shared/player-preload.d.ts +37 -0
  11. package/dist/components/shared/player-preload.d.ts.map +1 -0
  12. package/dist/components/shared/section-player-runtime.d.ts +104 -0
  13. package/dist/components/shared/section-player-runtime.d.ts.map +1 -0
  14. package/dist/components/shared/section-player-view-state.d.ts +24 -0
  15. package/dist/components/shared/section-player-view-state.d.ts.map +1 -0
  16. package/dist/controllers/SectionContentService.d.ts.map +1 -1
  17. package/dist/controllers/SectionController.d.ts +5 -1
  18. package/dist/controllers/SectionController.d.ts.map +1 -1
  19. package/dist/controllers/SectionSessionService.d.ts +0 -1
  20. package/dist/controllers/SectionSessionService.d.ts.map +1 -1
  21. package/dist/controllers/toolkit-section-contracts.d.ts +2 -28
  22. package/dist/controllers/toolkit-section-contracts.d.ts.map +1 -1
  23. package/dist/controllers/types.d.ts +28 -1
  24. package/dist/controllers/types.d.ts.map +1 -1
  25. package/dist/pie-item-player-B1iGN63e.js +6189 -0
  26. package/dist/pie-section-player.d.ts +0 -8
  27. package/dist/pie-section-player.d.ts.map +1 -1
  28. package/dist/pie-section-player.js +56558 -11
  29. package/dist/player-preload-CQVG0Bih.js +705 -0
  30. package/dist/utils/player-preload.d.ts +2 -0
  31. package/dist/utils/player-preload.d.ts.map +1 -0
  32. package/dist/utils/player-preload.js +8 -0
  33. package/package.json +23 -32
  34. package/src/components/ItemShellElement.svelte +10 -1
  35. package/src/components/PieSectionPlayerBaseElement.svelte +21 -78
  36. package/src/components/PieSectionPlayerSplitPaneElement.svelte +236 -295
  37. package/src/components/PieSectionPlayerVerticalElement.svelte +424 -0
  38. package/src/components/shared/SectionItemCard.svelte +92 -0
  39. package/src/components/shared/SectionPassageCard.svelte +88 -0
  40. package/dist/ItemRenderer-MsjF_Beu.js +0 -467
  41. package/dist/PieItemModeLayoutElement-D7oTzA9T.js +0 -316
  42. package/dist/PieSplitPanelLayoutElement-GUtJ_NlF.js +0 -246
  43. package/dist/PieVerticalLayoutElement-BoA3FO5g.js +0 -194
  44. package/dist/controllers/SectionToolkitService.d.ts +0 -24
  45. package/dist/controllers/SectionToolkitService.d.ts.map +0 -1
  46. package/dist/controllers/SessionPersistenceStrategy.d.ts +0 -15
  47. package/dist/controllers/SessionPersistenceStrategy.d.ts.map +0 -1
  48. package/dist/index.d.ts +0 -2
  49. package/dist/pie-section-player-DJ5NcwdT.js +0 -17078
  50. package/dist/runtime/runtime-event-guards.d.ts +0 -4
  51. package/dist/runtime/runtime-event-guards.d.ts.map +0 -1
  52. package/src/PieSectionPlayer.svelte +0 -826
  53. package/src/components/ItemModeLayout.svelte +0 -172
  54. package/src/components/ItemNavigation.svelte +0 -96
  55. package/src/components/ItemPlayerBridge.svelte +0 -110
  56. package/src/components/ItemRenderer.svelte +0 -248
  57. package/src/components/ItemShell.svelte +0 -86
  58. package/src/components/layout-elements/PieItemModeLayoutElement.svelte +0 -47
  59. package/src/components/layout-elements/PieSplitPanelLayoutElement.svelte +0 -62
  60. package/src/components/layout-elements/PieVerticalLayoutElement.svelte +0 -41
  61. package/src/components/layouts/SplitPanelLayout.svelte +0 -385
  62. package/src/components/layouts/VerticalLayout.svelte +0 -193
@@ -1,47 +0,0 @@
1
- <svelte:options
2
- customElement={{
3
- tag: "pie-item-mode-layout",
4
- shadow: "open",
5
- props: {
6
- composition: { type: "Object" },
7
- env: { type: "Object" },
8
- toolbarPosition: { type: "String", attribute: "toolbar-position" },
9
- showToolbar: { type: "Boolean", attribute: "show-toolbar" },
10
- onnext: { type: "Object", reflect: false },
11
- onprevious: { type: "Object", reflect: false },
12
- },
13
- }}
14
- />
15
-
16
- <script lang="ts">
17
- import type { SectionCompositionModel } from "../../controllers/types.js";
18
- import ItemModeLayout from "../ItemModeLayout.svelte";
19
-
20
- let {
21
- composition,
22
- env = { mode: "gather", role: "student" },
23
- toolbarPosition = "right",
24
- showToolbar = true,
25
- onnext = undefined as (() => void) | undefined,
26
- onprevious = undefined as (() => void) | undefined,
27
- }: {
28
- composition: SectionCompositionModel;
29
- env?: {
30
- mode: "gather" | "view" | "evaluate" | "author";
31
- role: "student" | "instructor";
32
- };
33
- toolbarPosition?: "top" | "right" | "bottom" | "left" | "none";
34
- showToolbar?: boolean;
35
- onnext?: () => void;
36
- onprevious?: () => void;
37
- } = $props();
38
- </script>
39
-
40
- <ItemModeLayout
41
- composition={composition as SectionCompositionModel}
42
- {env}
43
- {toolbarPosition}
44
- {showToolbar}
45
- {onnext}
46
- {onprevious}
47
- />
@@ -1,62 +0,0 @@
1
- <svelte:options
2
- customElement={{
3
- tag: "pie-split-panel-layout",
4
- shadow: "open",
5
- props: {
6
- composition: { type: "Object" },
7
- env: { type: "Object" },
8
- toolbarPosition: { type: "String", attribute: "toolbar-position" },
9
- showToolbar: { type: "Boolean", attribute: "show-toolbar" },
10
- },
11
- }}
12
- />
13
-
14
- <script lang="ts">
15
- import type { SectionCompositionModel } from "../../controllers/types.js";
16
- import SplitPanelLayout from "../layouts/SplitPanelLayout.svelte";
17
-
18
- let {
19
- composition,
20
- env = { mode: "gather", role: "student" },
21
- toolbarPosition = "right",
22
- showToolbar = true,
23
- }: {
24
- composition: SectionCompositionModel;
25
- env?: {
26
- mode: "gather" | "view" | "evaluate" | "author";
27
- role: "student" | "instructor";
28
- };
29
- toolbarPosition?: "top" | "right" | "bottom" | "left" | "none";
30
- showToolbar?: boolean;
31
- } = $props();
32
- </script>
33
-
34
- <SplitPanelLayout
35
- composition={
36
- composition as SectionCompositionModel
37
- }
38
- {env}
39
- {toolbarPosition}
40
- {showToolbar}
41
- />
42
-
43
- <style>
44
- /* Ensure the custom-element wrapper is a constrained block container. */
45
- :global(pie-split-panel-layout) {
46
- display: block;
47
- width: 100%;
48
- height: 100%;
49
- min-height: 0;
50
- max-height: 100%;
51
- overflow: hidden;
52
- }
53
-
54
- :host {
55
- display: block;
56
- width: 100%;
57
- height: 100%;
58
- min-height: 0;
59
- max-height: 100%;
60
- overflow: hidden;
61
- }
62
- </style>
@@ -1,41 +0,0 @@
1
- <svelte:options
2
- customElement={{
3
- tag: "pie-vertical-layout",
4
- shadow: "open",
5
- props: {
6
- composition: { type: "Object" },
7
- env: { type: "Object" },
8
- toolbarPosition: { type: "String", attribute: "toolbar-position" },
9
- showToolbar: { type: "Boolean", attribute: "show-toolbar" },
10
- },
11
- }}
12
- />
13
-
14
- <script lang="ts">
15
- import type { SectionCompositionModel } from "../../controllers/types.js";
16
- import VerticalLayout from "../layouts/VerticalLayout.svelte";
17
-
18
- let {
19
- composition,
20
- env = { mode: "gather", role: "student" },
21
- toolbarPosition = "right",
22
- showToolbar = true,
23
- }: {
24
- composition: SectionCompositionModel;
25
- env?: {
26
- mode: "gather" | "view" | "evaluate" | "author";
27
- role: "student" | "instructor";
28
- };
29
- toolbarPosition?: "top" | "right" | "bottom" | "left" | "none";
30
- showToolbar?: boolean;
31
- } = $props();
32
- </script>
33
-
34
- <VerticalLayout
35
- composition={
36
- composition as SectionCompositionModel
37
- }
38
- {env}
39
- {toolbarPosition}
40
- {showToolbar}
41
- />
@@ -1,385 +0,0 @@
1
- <!--
2
- SplitPanelLayout - Internal Component
3
-
4
- Renders split-panel layout - passages on left, items on right, both scrollable.
5
- Falls back to vertical layout on mobile/narrow screens.
6
- Not exposed as a web component - used internally in PieSectionPlayer.
7
- -->
8
- <script lang="ts">
9
- import type { SectionCompositionModel } from '../../controllers/types.js';
10
- import ItemRenderer from '../ItemRenderer.svelte';
11
-
12
- let {
13
- composition,
14
- env = { mode: 'gather', role: 'student' },
15
- toolbarPosition = 'right',
16
- showToolbar = true
17
- }: {
18
- composition: SectionCompositionModel;
19
- env?: { mode: 'gather' | 'view' | 'evaluate' | 'author'; role: 'student' | 'instructor' };
20
- toolbarPosition?: 'top' | 'right' | 'bottom' | 'left' | 'none';
21
- showToolbar?: boolean;
22
- } = $props();
23
-
24
- // Resizable panel state
25
- let leftPanelWidth = $state(50); // percentage
26
- let isDragging = $state(false);
27
- let containerElement: HTMLDivElement | null = $state(null);
28
- let passagesScrolling = $state(false);
29
- let itemsScrolling = $state(false);
30
- let passagesScrollTimer: ReturnType<typeof setTimeout> | null = null;
31
- let itemsScrollTimer: ReturnType<typeof setTimeout> | null = null;
32
-
33
- function handleMouseDown(event: MouseEvent) {
34
- event.preventDefault();
35
- isDragging = true;
36
- document.body.style.cursor = 'col-resize';
37
- document.body.style.userSelect = 'none';
38
- }
39
-
40
- function handleMouseMove(event: MouseEvent) {
41
- if (!isDragging || !containerElement) return;
42
-
43
- const containerRect = containerElement.getBoundingClientRect();
44
- const offsetX = event.clientX - containerRect.left;
45
- const newWidth = (offsetX / containerRect.width) * 100;
46
-
47
- // Constrain between 20% and 80%
48
- if (newWidth >= 20 && newWidth <= 80) {
49
- leftPanelWidth = newWidth;
50
- }
51
- }
52
-
53
- function handleMouseUp() {
54
- if (isDragging) {
55
- isDragging = false;
56
- document.body.style.cursor = '';
57
- document.body.style.userSelect = '';
58
- }
59
- }
60
-
61
- function handleKeyDown(event: KeyboardEvent) {
62
- const step = 5; // 5% change per key press
63
- if (event.key === 'ArrowLeft') {
64
- event.preventDefault();
65
- leftPanelWidth = Math.max(20, leftPanelWidth - step);
66
- } else if (event.key === 'ArrowRight') {
67
- event.preventDefault();
68
- leftPanelWidth = Math.min(80, leftPanelWidth + step);
69
- }
70
- }
71
-
72
- function markScrolling(target: 'passages' | 'items') {
73
- if (target === 'passages') {
74
- passagesScrolling = true;
75
- if (passagesScrollTimer) clearTimeout(passagesScrollTimer);
76
- passagesScrollTimer = setTimeout(() => {
77
- passagesScrolling = false;
78
- }, 700);
79
- } else {
80
- itemsScrolling = true;
81
- if (itemsScrollTimer) clearTimeout(itemsScrollTimer);
82
- itemsScrollTimer = setTimeout(() => {
83
- itemsScrolling = false;
84
- }, 700);
85
- }
86
- }
87
-
88
- // Attach global mouse listeners when dragging
89
- $effect(() => {
90
- if (isDragging) {
91
- window.addEventListener('mousemove', handleMouseMove);
92
- window.addEventListener('mouseup', handleMouseUp);
93
-
94
- return () => {
95
- window.removeEventListener('mousemove', handleMouseMove);
96
- window.removeEventListener('mouseup', handleMouseUp);
97
- };
98
- }
99
- return undefined;
100
- });
101
-
102
- // Check if we have passages to determine layout
103
- let passages = $derived(composition?.passages || []);
104
- let items = $derived(composition?.items || []);
105
- let itemSessionsByItemId = $derived(composition?.itemSessionsByItemId || {});
106
- let hasPassages = $derived(passages.length > 0);
107
- let shouldRenderToolbar = $derived(showToolbar && toolbarPosition !== 'none');
108
- let isToolbarBeforeContent = $derived(
109
- toolbarPosition === 'top' || toolbarPosition === 'left'
110
- );
111
- </script>
112
-
113
- <div class={`pie-section-player__layout-shell pie-section-player__layout-shell--${toolbarPosition}`}>
114
- {#if shouldRenderToolbar && isToolbarBeforeContent}
115
- <pie-section-tools-toolbar
116
- position={toolbarPosition}
117
- ></pie-section-tools-toolbar>
118
- {/if}
119
- <div
120
- class={`pie-section-player__split-panel-layout ${!hasPassages ? 'pie-section-player__split-panel-layout--no-passages' : ''}`}
121
- bind:this={containerElement}
122
- style={hasPassages ? `grid-template-columns: ${leftPanelWidth}% 0.5rem ${100 - leftPanelWidth - 0.5}%` : 'grid-template-columns: 1fr'}
123
- >
124
- {#if hasPassages}
125
- <!-- Left Panel: Passages -->
126
- <aside
127
- class={`pie-section-player__passages-panel ${passagesScrolling ? 'pie-section-player__panel--scrolling' : ''}`}
128
- aria-label="Reading passages"
129
- onscroll={() => markScrolling('passages')}
130
- >
131
- {#each passages as passage (passage.id)}
132
- <div class="pie-section-player__passage-wrapper">
133
- <ItemRenderer
134
- item={passage}
135
- contentKind="rubric-block-stimulus"
136
- env={{ mode: 'view', role: env.role }}
137
- customClassName="pie-section-player__passage-item"
138
- />
139
- </div>
140
- {/each}
141
- </aside>
142
-
143
- <!-- Draggable Divider: focusable resize handle, Arrow keys adjust panel width -->
144
- <button
145
- type="button"
146
- class={`pie-section-player__split-divider ${isDragging ? 'pie-section-player__split-divider--dragging' : ''}`}
147
- onmousedown={handleMouseDown}
148
- onkeydown={handleKeyDown}
149
- aria-label="Resize panels"
150
- >
151
- <span class="pie-section-player__split-divider-handle"></span>
152
- </button>
153
- {/if}
154
-
155
- <!-- Items Panel -->
156
- <main
157
- class={`pie-section-player__items-panel ${itemsScrolling ? 'pie-section-player__panel--scrolling' : ''}`}
158
- aria-label="Assessment items"
159
- onscroll={() => markScrolling('items')}
160
- >
161
- {#each items as item, index (item.id || index)}
162
- <div class="pie-section-player__item-wrapper" data-item-index={index}>
163
- <ItemRenderer
164
- {item}
165
- contentKind="assessment-item"
166
- {env}
167
- session={itemSessionsByItemId[item.id || '']}
168
- customClassName="pie-section-player__item-content"
169
- />
170
- </div>
171
- {/each}
172
- </main>
173
- </div>
174
- {#if shouldRenderToolbar && !isToolbarBeforeContent}
175
- <pie-section-tools-toolbar
176
- position={toolbarPosition}
177
- ></pie-section-tools-toolbar>
178
- {/if}
179
- </div>
180
-
181
- <style>
182
- .pie-section-player__layout-shell {
183
- display: flex;
184
- width: 100%;
185
- height: 100%;
186
- min-height: 0;
187
- overflow: hidden;
188
- }
189
-
190
- .pie-section-player__layout-shell--top,
191
- .pie-section-player__layout-shell--bottom,
192
- .pie-section-player__layout-shell--none {
193
- flex-direction: column;
194
- }
195
-
196
- .pie-section-player__layout-shell--left,
197
- .pie-section-player__layout-shell--right {
198
- flex-direction: row;
199
- }
200
-
201
- .pie-section-player__split-panel-layout {
202
- display: grid;
203
- grid-template-rows: minmax(0, 1fr);
204
- padding: 1rem;
205
- height: 100%;
206
- flex: 1;
207
- max-height: 100%;
208
- min-height: 0;
209
- overflow: hidden;
210
- gap: 0;
211
- }
212
-
213
- .pie-section-player__split-panel-layout--no-passages {
214
- padding: 1rem;
215
- }
216
-
217
- .pie-section-player__split-panel-layout--no-passages .pie-section-player__items-panel {
218
- padding: 0;
219
- }
220
-
221
- .pie-section-player__passages-panel,
222
- .pie-section-player__items-panel {
223
- height: 100%;
224
- max-height: 100%;
225
- overflow-y: auto;
226
- overflow-x: hidden;
227
- overscroll-behavior: contain;
228
- min-height: 0;
229
- min-width: 0;
230
- /* Firefox auto-hide scrollbar */
231
- scrollbar-width: auto;
232
- scrollbar-color: transparent transparent;
233
- }
234
-
235
- .pie-section-player__panel--scrolling {
236
- scrollbar-color: var(--pie-blue-grey-300, #c1c1c1) var(--pie-secondary-background, #f1f1f1);
237
- }
238
-
239
- .pie-section-player__split-divider {
240
- /* Reset button defaults so it looks like a divider strip */
241
- border: none;
242
- padding: 0;
243
- margin: 0;
244
- font: inherit;
245
- /* Fill grid cell height so the handle is vertically centered in the visible area */
246
- align-self: stretch;
247
- height: 100%;
248
- min-height: 0;
249
- position: relative;
250
- cursor: col-resize;
251
- background: var(--pie-secondary-background, #f3f4f6);
252
- display: flex;
253
- align-items: center;
254
- justify-content: center;
255
- user-select: none;
256
- touch-action: none;
257
- transition: background 0.2s ease;
258
- }
259
-
260
- .pie-section-player__split-divider:hover {
261
- background: var(--pie-border-light, #e5e7eb);
262
- }
263
-
264
- .pie-section-player__split-divider:focus {
265
- outline: 2px solid var(--pie-focus-checked-border, #1976d2);
266
- outline-offset: -2px;
267
- }
268
-
269
- .pie-section-player__split-divider-handle {
270
- /* Absolutely centered in the divider button (matches develop behavior) */
271
- position: absolute;
272
- inset: 0;
273
- margin: auto;
274
- width: 6px;
275
- height: 60px;
276
- background: var(--pie-blue-grey-600, #9ca3af);
277
- border-radius: 3px;
278
- transition: all 0.2s ease;
279
- pointer-events: none;
280
- }
281
-
282
- .pie-section-player__split-divider-handle::before {
283
- content: '';
284
- position: absolute;
285
- top: 50%;
286
- left: 50%;
287
- transform: translate(-50%, -50%);
288
- width: 2px;
289
- height: 20px;
290
- background: var(--pie-white, white);
291
- border-radius: 1px;
292
- opacity: 0.8;
293
- }
294
-
295
- .pie-section-player__split-divider:hover .pie-section-player__split-divider-handle,
296
- .pie-section-player__split-divider:focus .pie-section-player__split-divider-handle,
297
- .pie-section-player__split-divider--dragging .pie-section-player__split-divider-handle {
298
- background: var(--pie-primary, #1976d2);
299
- height: 80px;
300
- box-shadow: 0 2px 8px rgba(25, 118, 210, 0.3);
301
- }
302
-
303
- .pie-section-player__split-divider--dragging {
304
- background: var(--pie-primary-light, #dbeafe);
305
- }
306
-
307
- .pie-section-player__passages-panel {
308
- display: flex;
309
- flex-direction: column;
310
- gap: 1.5rem;
311
- }
312
-
313
- .pie-section-player__items-panel {
314
- display: flex;
315
- flex-direction: column;
316
- gap: 1.5rem;
317
- }
318
-
319
- .pie-section-player__item-wrapper {
320
- flex-shrink: 0;
321
- }
322
-
323
- .pie-section-player__passage-wrapper {
324
- flex-shrink: 0;
325
- }
326
-
327
- .pie-section-player__item-wrapper,
328
- .pie-section-player__passage-wrapper {
329
- padding: 0.25rem;
330
- background: var(--pie-white, white);
331
- border: 1px solid var(--pie-border-light, #e5e7eb);
332
- border-radius: 6px;
333
- }
334
-
335
- /* Mobile/Narrow: Fall back to vertical layout */
336
- @media (max-width: 768px) {
337
- .pie-section-player__split-panel-layout {
338
- grid-template-columns: 1fr !important;
339
- gap: 1rem;
340
- min-height: auto;
341
- padding: 0.5rem;
342
- }
343
-
344
- .pie-section-player__split-divider {
345
- display: none;
346
- }
347
-
348
- .pie-section-player__passages-panel,
349
- .pie-section-player__items-panel {
350
- height: auto;
351
- max-height: none;
352
- overflow-y: visible;
353
- }
354
-
355
- .pie-section-player__items-panel {
356
- gap: 1rem;
357
- }
358
- }
359
-
360
- /* Hide scrollbar by default - WebKit (Chrome, Safari, Edge) */
361
- .pie-section-player__passages-panel::-webkit-scrollbar,
362
- .pie-section-player__items-panel::-webkit-scrollbar {
363
- width: 0px;
364
- background: transparent;
365
- }
366
-
367
- /* Show scrollbar while scrolling */
368
- .pie-section-player__panel--scrolling::-webkit-scrollbar {
369
- width: 8px;
370
- }
371
-
372
- .pie-section-player__panel--scrolling::-webkit-scrollbar-track {
373
- background: var(--pie-secondary-background, #f1f1f1);
374
- border-radius: 4px;
375
- }
376
-
377
- .pie-section-player__panel--scrolling::-webkit-scrollbar-thumb {
378
- background: var(--pie-blue-grey-300, #c1c1c1);
379
- border-radius: 4px;
380
- }
381
-
382
- .pie-section-player__panel--scrolling::-webkit-scrollbar-thumb:hover {
383
- background: var(--pie-blue-grey-600, #a1a1a1);
384
- }
385
- </style>