@pie-players/pie-tool-line-reader 0.1.8 → 0.1.10
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/tool-line-reader.js +2856 -2123
- package/package.json +15 -6
- package/tool-line-reader.svelte +60 -146
- package/dist/tool-line-reader.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pie-players/pie-tool-line-reader",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Reading guide overlay tool for PIE assessment player",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "git+https://github.com/pie-framework/pie-players.git"
|
|
8
|
+
"url": "git+https://github.com/pie-framework/pie-players.git",
|
|
9
|
+
"directory": "packages/tool-line-reader"
|
|
9
10
|
},
|
|
10
11
|
"publishConfig": {
|
|
11
12
|
"access": "public"
|
|
@@ -40,9 +41,9 @@
|
|
|
40
41
|
"unpkg": "./dist/tool-line-reader.js",
|
|
41
42
|
"jsdelivr": "./dist/tool-line-reader.js",
|
|
42
43
|
"dependencies": {
|
|
43
|
-
"@pie-players/pie-assessment-toolkit": "0.2.
|
|
44
|
-
"@pie-players/pie-
|
|
45
|
-
"@
|
|
44
|
+
"@pie-players/pie-assessment-toolkit": "0.2.10",
|
|
45
|
+
"@pie-players/pie-context": "0.1.2",
|
|
46
|
+
"@pie-players/pie-players-shared": "0.2.6",
|
|
46
47
|
"daisyui": "^5.5.18"
|
|
47
48
|
},
|
|
48
49
|
"types": "./dist/index.d.ts",
|
|
@@ -59,5 +60,13 @@
|
|
|
59
60
|
"typescript": "^5.7.0",
|
|
60
61
|
"vite": "^7.0.8",
|
|
61
62
|
"vite-plugin-dts": "^4.5.3"
|
|
62
|
-
}
|
|
63
|
+
},
|
|
64
|
+
"homepage": "https://github.com/pie-framework/pie-players/tree/master/packages/tool-line-reader#readme",
|
|
65
|
+
"bugs": {
|
|
66
|
+
"url": "https://github.com/pie-framework/pie-players/issues"
|
|
67
|
+
},
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": ">=18.0.0"
|
|
70
|
+
},
|
|
71
|
+
"sideEffects": true
|
|
63
72
|
}
|
package/tool-line-reader.svelte
CHANGED
|
@@ -1,32 +1,38 @@
|
|
|
1
1
|
<svelte:options
|
|
2
2
|
customElement={{
|
|
3
3
|
tag: 'pie-tool-line-reader',
|
|
4
|
-
shadow: '
|
|
4
|
+
shadow: 'open',
|
|
5
5
|
props: {
|
|
6
6
|
visible: { type: 'Boolean', attribute: 'visible' },
|
|
7
|
-
toolId: { type: 'String', attribute: 'tool-id' }
|
|
8
|
-
coordinator: { type: 'Object' }
|
|
7
|
+
toolId: { type: 'String', attribute: 'tool-id' }
|
|
9
8
|
}
|
|
10
9
|
}}
|
|
11
10
|
/>
|
|
12
11
|
|
|
13
12
|
<script lang="ts">
|
|
14
13
|
|
|
15
|
-
import
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
import {
|
|
15
|
+
connectToolRuntimeContext,
|
|
16
|
+
ZIndexLayer,
|
|
17
|
+
} from '@pie-players/pie-assessment-toolkit';
|
|
18
|
+
import type {
|
|
19
|
+
AssessmentToolkitRuntimeContext,
|
|
20
|
+
IToolCoordinator,
|
|
21
|
+
} from '@pie-players/pie-assessment-toolkit';
|
|
19
22
|
import { onMount } from 'svelte';
|
|
20
23
|
|
|
21
24
|
// Props
|
|
22
|
-
let { visible = false, toolId = 'lineReader'
|
|
25
|
+
let { visible = false, toolId = 'lineReader' }: { visible?: boolean; toolId?: string } = $props();
|
|
23
26
|
|
|
24
27
|
// Check if running in browser
|
|
25
28
|
const isBrowser = typeof window !== 'undefined';
|
|
26
29
|
|
|
27
30
|
// State
|
|
28
31
|
let containerEl = $state<HTMLDivElement | undefined>();
|
|
29
|
-
let
|
|
32
|
+
let runtimeContext = $state<AssessmentToolkitRuntimeContext | null>(null);
|
|
33
|
+
const coordinator = $derived(
|
|
34
|
+
runtimeContext?.toolCoordinator as IToolCoordinator | undefined,
|
|
35
|
+
);
|
|
30
36
|
let isDragging = $state(false);
|
|
31
37
|
let isResizing = $state(false);
|
|
32
38
|
let position = $state({
|
|
@@ -40,7 +46,6 @@ import { onMount } from 'svelte';
|
|
|
40
46
|
let currentColor = $state('#ffff00'); // Yellow
|
|
41
47
|
let currentOpacity = $state(0.3);
|
|
42
48
|
let maskingMode = $state<'highlight' | 'obscure'>('highlight');
|
|
43
|
-
let settingsOpen = $state(false);
|
|
44
49
|
|
|
45
50
|
// Track registration state
|
|
46
51
|
let registered = $state(false);
|
|
@@ -58,6 +63,13 @@ import { onMount } from 'svelte';
|
|
|
58
63
|
const MOVE_STEP = 10; // pixels
|
|
59
64
|
const RESIZE_STEP = 10; // pixels
|
|
60
65
|
|
|
66
|
+
$effect(() => {
|
|
67
|
+
if (!containerEl) return;
|
|
68
|
+
return connectToolRuntimeContext(containerEl, (value: AssessmentToolkitRuntimeContext) => {
|
|
69
|
+
runtimeContext = value;
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
61
73
|
function announce(message: string) {
|
|
62
74
|
announceText = message;
|
|
63
75
|
setTimeout(() => announceText = '', 1000);
|
|
@@ -80,20 +92,6 @@ import { onMount } from 'svelte';
|
|
|
80
92
|
announce(`Mode changed to ${maskingMode === 'highlight' ? 'highlight' : 'masking'}`);
|
|
81
93
|
}
|
|
82
94
|
|
|
83
|
-
function toggleSettings() {
|
|
84
|
-
settingsOpen = !settingsOpen;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function closeSettings() {
|
|
88
|
-
settingsOpen = false;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function setColor(color: string) {
|
|
92
|
-
currentColor = color;
|
|
93
|
-
const colorName = colors.find(c => c.value === color)?.name || 'Unknown';
|
|
94
|
-
announce(`Color changed to ${colorName}`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
95
|
// Pointer event handlers (better for web components)
|
|
98
96
|
function handlePointerDown(e: PointerEvent) {
|
|
99
97
|
const target = e.target as HTMLElement;
|
|
@@ -101,9 +99,6 @@ import { onMount } from 'svelte';
|
|
|
101
99
|
// Check if clicking the resize handle
|
|
102
100
|
if (target.closest('.resize-handle')) {
|
|
103
101
|
startResizing(e);
|
|
104
|
-
} else if (target.closest('.tool-settings-button') || target.closest('.tool-settings-panel')) {
|
|
105
|
-
// Don't start dragging when clicking settings
|
|
106
|
-
return;
|
|
107
102
|
} else {
|
|
108
103
|
startDragging(e);
|
|
109
104
|
}
|
|
@@ -276,7 +271,7 @@ import { onMount } from 'svelte';
|
|
|
276
271
|
|
|
277
272
|
{#if visible}
|
|
278
273
|
<!-- Screen reader announcements -->
|
|
279
|
-
<div class="sr-only" role="status" aria-live="polite" aria-atomic="true">
|
|
274
|
+
<div class="pie-sr-only" role="status" aria-live="polite" aria-atomic="true">
|
|
280
275
|
{announceText}
|
|
281
276
|
</div>
|
|
282
277
|
|
|
@@ -284,25 +279,25 @@ import { onMount } from 'svelte';
|
|
|
284
279
|
{#if maskingMode === 'obscure'}
|
|
285
280
|
<!-- Top mask - from top of viewport to top of line reader -->
|
|
286
281
|
<div
|
|
287
|
-
class="
|
|
282
|
+
class="pie-tool-line-reader__mask pie-tool-line-reader__mask--top"
|
|
288
283
|
style="height: {Math.max(0, position.y - size.height / 2)}px;"
|
|
289
284
|
aria-hidden="true"
|
|
290
285
|
></div>
|
|
291
286
|
<!-- Bottom mask - from bottom of line reader to bottom of viewport -->
|
|
292
287
|
<div
|
|
293
|
-
class="
|
|
288
|
+
class="pie-tool-line-reader__mask pie-tool-line-reader__mask--bottom"
|
|
294
289
|
style="top: {position.y + size.height / 2}px;"
|
|
295
290
|
aria-hidden="true"
|
|
296
291
|
></div>
|
|
297
292
|
<!-- Left mask - left side of line reader window -->
|
|
298
293
|
<div
|
|
299
|
-
class="
|
|
294
|
+
class="pie-tool-line-reader__mask pie-tool-line-reader__mask--left"
|
|
300
295
|
style="top: {position.y - size.height / 2}px; height: {size.height}px; width: {Math.max(0, position.x - size.width / 2)}px;"
|
|
301
296
|
aria-hidden="true"
|
|
302
297
|
></div>
|
|
303
298
|
<!-- Right mask - right side of line reader window -->
|
|
304
299
|
<div
|
|
305
|
-
class="
|
|
300
|
+
class="pie-tool-line-reader__mask pie-tool-line-reader__mask--right"
|
|
306
301
|
style="top: {position.y - size.height / 2}px; height: {size.height}px; left: {position.x + size.width / 2}px;"
|
|
307
302
|
aria-hidden="true"
|
|
308
303
|
></div>
|
|
@@ -312,8 +307,8 @@ import { onMount } from 'svelte';
|
|
|
312
307
|
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
313
308
|
<div
|
|
314
309
|
bind:this={containerEl}
|
|
315
|
-
class="line-reader
|
|
316
|
-
class:masking-mode={maskingMode === 'obscure'}
|
|
310
|
+
class="pie-tool-line-reader"
|
|
311
|
+
class:pie-tool-line-reader--masking-mode={maskingMode === 'obscure'}
|
|
317
312
|
style="left: {position.x}px; top: {position.y}px; width: {size.width}px; height: {size.height}px;"
|
|
318
313
|
onpointerdown={handlePointerDown}
|
|
319
314
|
onkeydown={handleKeyDown}
|
|
@@ -322,110 +317,27 @@ import { onMount } from 'svelte';
|
|
|
322
317
|
aria-label="Line Reader tool. Mode: {maskingMode === 'highlight' ? 'Highlight' : 'Masking'}. Use arrow keys to move, +/- to resize height, C to change color, [ and ] to adjust opacity, M to toggle mode. Current color: {colors.find(c => c.value === currentColor)?.name}, Opacity: {Math.round(currentOpacity * 100)}%"
|
|
323
318
|
aria-roledescription="Draggable and resizable reading guide overlay"
|
|
324
319
|
>
|
|
325
|
-
<div class="line-
|
|
326
|
-
<!-- Settings Button -->
|
|
327
|
-
<ToolSettingsButton
|
|
328
|
-
bind:buttonEl={settingsButtonEl}
|
|
329
|
-
onClick={toggleSettings}
|
|
330
|
-
ariaLabel="Line reader settings"
|
|
331
|
-
active={settingsOpen}
|
|
332
|
-
/>
|
|
320
|
+
<div class="pie-tool-line-reader__container" style="background-color: {backgroundColor};">
|
|
333
321
|
</div>
|
|
334
322
|
|
|
335
323
|
<!-- Resize handle -->
|
|
336
324
|
<div
|
|
337
|
-
class="
|
|
325
|
+
class="pie-tool-line-reader__resize-handle pie-tool-line-reader__resize-handle--bottom"
|
|
338
326
|
title="Drag to resize height"
|
|
339
327
|
role="button"
|
|
340
328
|
tabindex="-1"
|
|
341
329
|
aria-label="Resize handle - drag to adjust height"
|
|
342
330
|
>
|
|
343
331
|
<svg width="20" height="8" viewBox="0 0 20 8" aria-hidden="true">
|
|
344
|
-
<rect x="8" y="3" width="4" height="2" fill="#4CAF50" rx="1"/>
|
|
332
|
+
<rect x="8" y="3" width="4" height="2" fill="var(--pie-primary, #4CAF50)" rx="1"/>
|
|
345
333
|
</svg>
|
|
346
334
|
</div>
|
|
347
335
|
</div>
|
|
348
336
|
|
|
349
|
-
<!-- Settings Panel - Rendered outside line-reader-frame to avoid height constraints -->
|
|
350
|
-
<ToolSettingsPanel
|
|
351
|
-
open={settingsOpen}
|
|
352
|
-
title="Line Reader Settings"
|
|
353
|
-
onClose={closeSettings}
|
|
354
|
-
anchorEl={settingsButtonEl}
|
|
355
|
-
>
|
|
356
|
-
<!-- Mode Selection - First, as it determines what other settings are relevant -->
|
|
357
|
-
<fieldset class="setting-group">
|
|
358
|
-
<legend>Mode</legend>
|
|
359
|
-
<label>
|
|
360
|
-
<input
|
|
361
|
-
type="radio"
|
|
362
|
-
name="mode"
|
|
363
|
-
value="highlight"
|
|
364
|
-
checked={maskingMode === 'highlight'}
|
|
365
|
-
onchange={() => { maskingMode = 'highlight'; announce('Mode changed to highlight'); }}
|
|
366
|
-
/>
|
|
367
|
-
<span>Highlight</span>
|
|
368
|
-
</label>
|
|
369
|
-
<label>
|
|
370
|
-
<input
|
|
371
|
-
type="radio"
|
|
372
|
-
name="mode"
|
|
373
|
-
value="obscure"
|
|
374
|
-
checked={maskingMode === 'obscure'}
|
|
375
|
-
onchange={() => { maskingMode = 'obscure'; announce('Mode changed to masking'); }}
|
|
376
|
-
/>
|
|
377
|
-
<span>Masking</span>
|
|
378
|
-
</label>
|
|
379
|
-
</fieldset>
|
|
380
|
-
|
|
381
|
-
<!-- Color Selection - Only shown in Highlight mode -->
|
|
382
|
-
{#if maskingMode === 'highlight'}
|
|
383
|
-
<fieldset class="setting-group">
|
|
384
|
-
<legend>Color</legend>
|
|
385
|
-
{#each colors as color}
|
|
386
|
-
<label>
|
|
387
|
-
<input
|
|
388
|
-
type="radio"
|
|
389
|
-
name="color"
|
|
390
|
-
value={color.value}
|
|
391
|
-
checked={currentColor === color.value}
|
|
392
|
-
onchange={() => setColor(color.value)}
|
|
393
|
-
/>
|
|
394
|
-
<div class="color-swatch" style="background-color: {color.value};"></div>
|
|
395
|
-
<span>{color.name}</span>
|
|
396
|
-
</label>
|
|
397
|
-
{/each}
|
|
398
|
-
</fieldset>
|
|
399
|
-
|
|
400
|
-
<!-- Opacity Slider - Only shown in Highlight mode -->
|
|
401
|
-
<div class="setting-group">
|
|
402
|
-
<div class="setting-label">
|
|
403
|
-
<span>Opacity</span>
|
|
404
|
-
<span class="setting-value" aria-live="polite">{Math.round(currentOpacity * 100)}%</span>
|
|
405
|
-
</div>
|
|
406
|
-
<input
|
|
407
|
-
type="range"
|
|
408
|
-
min="10"
|
|
409
|
-
max="90"
|
|
410
|
-
step="5"
|
|
411
|
-
value={currentOpacity * 100}
|
|
412
|
-
oninput={(e) => {
|
|
413
|
-
currentOpacity = Number(e.currentTarget.value) / 100;
|
|
414
|
-
announce(`Opacity ${Math.round(currentOpacity * 100)}%`);
|
|
415
|
-
}}
|
|
416
|
-
aria-label="Opacity"
|
|
417
|
-
aria-valuemin="10"
|
|
418
|
-
aria-valuemax="90"
|
|
419
|
-
aria-valuenow={Math.round(currentOpacity * 100)}
|
|
420
|
-
aria-valuetext="{Math.round(currentOpacity * 100)} percent"
|
|
421
|
-
/>
|
|
422
|
-
</div>
|
|
423
|
-
{/if}
|
|
424
|
-
</ToolSettingsPanel>
|
|
425
337
|
{/if}
|
|
426
338
|
|
|
427
339
|
<style>
|
|
428
|
-
.sr-only {
|
|
340
|
+
.pie-sr-only {
|
|
429
341
|
position: absolute;
|
|
430
342
|
width: 1px;
|
|
431
343
|
height: 1px;
|
|
@@ -437,8 +349,8 @@ import { onMount } from 'svelte';
|
|
|
437
349
|
border-width: 0;
|
|
438
350
|
}
|
|
439
351
|
|
|
440
|
-
.line-reader
|
|
441
|
-
border: 2px solid
|
|
352
|
+
.pie-tool-line-reader {
|
|
353
|
+
border: 2px solid color-mix(in srgb, var(--pie-primary, #4caf50) 80%, transparent);
|
|
442
354
|
cursor: move;
|
|
443
355
|
overflow: visible;
|
|
444
356
|
position: absolute;
|
|
@@ -448,17 +360,17 @@ import { onMount } from 'svelte';
|
|
|
448
360
|
touch-action: none;
|
|
449
361
|
}
|
|
450
362
|
|
|
451
|
-
.line-reader
|
|
452
|
-
outline: 3px solid #4A90E2;
|
|
363
|
+
.pie-tool-line-reader:focus {
|
|
364
|
+
outline: 3px solid var(--pie-button-focus-outline, var(--pie-primary, #4A90E2));
|
|
453
365
|
outline-offset: 2px;
|
|
454
366
|
}
|
|
455
367
|
|
|
456
|
-
.line-reader
|
|
457
|
-
outline: 3px solid #4A90E2;
|
|
368
|
+
.pie-tool-line-reader:focus-visible {
|
|
369
|
+
outline: 3px solid var(--pie-button-focus-outline, var(--pie-primary, #4A90E2));
|
|
458
370
|
outline-offset: 2px;
|
|
459
371
|
}
|
|
460
372
|
|
|
461
|
-
.line-
|
|
373
|
+
.pie-tool-line-reader__container {
|
|
462
374
|
width: 100%;
|
|
463
375
|
height: 100%;
|
|
464
376
|
position: relative;
|
|
@@ -466,7 +378,7 @@ import { onMount } from 'svelte';
|
|
|
466
378
|
}
|
|
467
379
|
|
|
468
380
|
|
|
469
|
-
.
|
|
381
|
+
.pie-tool-line-reader__resize-handle {
|
|
470
382
|
position: absolute;
|
|
471
383
|
cursor: ns-resize;
|
|
472
384
|
z-index: 10;
|
|
@@ -475,69 +387,71 @@ import { onMount } from 'svelte';
|
|
|
475
387
|
justify-content: center;
|
|
476
388
|
}
|
|
477
389
|
|
|
478
|
-
.
|
|
390
|
+
.pie-tool-line-reader__resize-handle--bottom {
|
|
479
391
|
bottom: -10px;
|
|
480
392
|
left: 50%;
|
|
481
393
|
transform: translateX(-50%);
|
|
482
394
|
width: 40px;
|
|
483
395
|
height: 16px;
|
|
484
|
-
background-color:
|
|
396
|
+
background-color: color-mix(in srgb, var(--pie-background, #fff) 90%, transparent);
|
|
485
397
|
border-radius: 8px;
|
|
486
|
-
border: 2px solid #
|
|
398
|
+
border: 2px solid var(--pie-primary, #4caf50);
|
|
487
399
|
}
|
|
488
400
|
|
|
489
|
-
.
|
|
490
|
-
background-color:
|
|
401
|
+
.pie-tool-line-reader__resize-handle:hover {
|
|
402
|
+
background-color: color-mix(in srgb, var(--pie-primary, #4caf50) 20%, transparent);
|
|
491
403
|
}
|
|
492
404
|
|
|
493
|
-
.
|
|
405
|
+
.pie-tool-line-reader__resize-handle:active {
|
|
494
406
|
cursor: ns-resize;
|
|
495
407
|
}
|
|
496
408
|
|
|
497
|
-
.line-reader
|
|
409
|
+
.pie-tool-line-reader:active {
|
|
498
410
|
cursor: grabbing;
|
|
499
411
|
}
|
|
500
412
|
|
|
501
413
|
/* Masking overlays for obscure mode - 4 rectangles covering all areas except line reader window */
|
|
502
|
-
.line-
|
|
414
|
+
.pie-tool-line-reader__mask {
|
|
503
415
|
position: fixed;
|
|
504
|
-
background:
|
|
416
|
+
background: color-mix(in srgb, var(--pie-text, #000) 85%, transparent);
|
|
505
417
|
z-index: 999;
|
|
506
418
|
pointer-events: none;
|
|
507
419
|
}
|
|
508
420
|
|
|
509
|
-
.
|
|
421
|
+
.pie-tool-line-reader__mask--top {
|
|
510
422
|
top: 0;
|
|
511
423
|
left: 0;
|
|
512
424
|
right: 0;
|
|
513
425
|
/* Height set via inline style */
|
|
514
426
|
}
|
|
515
427
|
|
|
516
|
-
.
|
|
428
|
+
.pie-tool-line-reader__mask--bottom {
|
|
517
429
|
/* Top set via inline style */
|
|
518
430
|
left: 0;
|
|
519
431
|
right: 0;
|
|
520
432
|
bottom: 0;
|
|
521
433
|
}
|
|
522
434
|
|
|
523
|
-
.
|
|
435
|
+
.pie-tool-line-reader__mask--left {
|
|
524
436
|
/* Top, height, and width set via inline style */
|
|
525
437
|
left: 0;
|
|
526
438
|
}
|
|
527
439
|
|
|
528
|
-
.
|
|
440
|
+
.pie-tool-line-reader__mask--right {
|
|
529
441
|
/* Top, height, and left set via inline style */
|
|
530
442
|
right: 0;
|
|
531
443
|
}
|
|
532
444
|
|
|
533
445
|
/* In masking mode, change the window appearance */
|
|
534
|
-
.line-reader-
|
|
535
|
-
border-color:
|
|
536
|
-
box-shadow:
|
|
446
|
+
.pie-tool-line-reader.pie-tool-line-reader--masking-mode {
|
|
447
|
+
border-color: var(--pie-primary, #4caf50);
|
|
448
|
+
box-shadow:
|
|
449
|
+
0 0 0 3px color-mix(in srgb, var(--pie-primary, #4caf50) 80%, transparent),
|
|
450
|
+
0 0 20px color-mix(in srgb, var(--pie-primary, #4caf50) 40%, transparent);
|
|
537
451
|
}
|
|
538
452
|
|
|
539
453
|
/* In masking mode, the window should be transparent to show content underneath */
|
|
540
|
-
.line-reader-
|
|
454
|
+
.pie-tool-line-reader.pie-tool-line-reader--masking-mode .pie-tool-line-reader__container {
|
|
541
455
|
background-color: transparent !important;
|
|
542
456
|
}
|
|
543
457
|
|