@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/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@pie-players/pie-tool-line-reader",
3
- "version": "0.1.8",
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.7",
44
- "@pie-players/pie-players-shared": "0.2.4",
45
- "@sveltejs/kit": "^2.52.0",
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
  }
@@ -1,32 +1,38 @@
1
1
  <svelte:options
2
2
  customElement={{
3
3
  tag: 'pie-tool-line-reader',
4
- shadow: 'none',
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 type { IToolCoordinator } from '@pie-players/pie-assessment-toolkit';
16
- import { ZIndexLayer } from '@pie-players/pie-assessment-toolkit';
17
- import ToolSettingsButton from '@pie-players/pie-players-shared/components/ToolSettingsButton.svelte';
18
- import ToolSettingsPanel from '@pie-players/pie-players-shared/components/ToolSettingsPanel.svelte';
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', coordinator }: { visible?: boolean; toolId?: string; coordinator?: IToolCoordinator } = $props();
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 settingsButtonEl = $state<HTMLButtonElement | undefined>();
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="line-reader-mask line-reader-mask-top"
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="line-reader-mask line-reader-mask-bottom"
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="line-reader-mask line-reader-mask-left"
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="line-reader-mask line-reader-mask-right"
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-frame"
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-reader-container" style="background-color: {backgroundColor};">
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="resize-handle resize-handle-bottom"
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-frame {
441
- border: 2px solid rgba(76, 175, 80, 0.8);
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-frame:focus {
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-frame:focus-visible {
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-reader-container {
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
- .resize-handle {
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
- .resize-handle-bottom {
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: rgba(255, 255, 255, 0.9);
396
+ background-color: color-mix(in srgb, var(--pie-background, #fff) 90%, transparent);
485
397
  border-radius: 8px;
486
- border: 2px solid #4CAF50;
398
+ border: 2px solid var(--pie-primary, #4caf50);
487
399
  }
488
400
 
489
- .resize-handle:hover {
490
- background-color: rgba(76, 175, 80, 0.2);
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
- .resize-handle:active {
405
+ .pie-tool-line-reader__resize-handle:active {
494
406
  cursor: ns-resize;
495
407
  }
496
408
 
497
- .line-reader-frame:active {
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-reader-mask {
414
+ .pie-tool-line-reader__mask {
503
415
  position: fixed;
504
- background: rgba(0, 0, 0, 0.85);
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
- .line-reader-mask-top {
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
- .line-reader-mask-bottom {
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
- .line-reader-mask-left {
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
- .line-reader-mask-right {
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-frame.masking-mode {
535
- border-color: rgba(76, 175, 80, 1);
536
- box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.8), 0 0 20px rgba(76, 175, 80, 0.4);
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-frame.masking-mode .line-reader-container {
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