@pie-players/pie-tool-annotation-toolbar 0.3.73 → 0.3.75

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Annotation Toolbar
2
2
 
3
- A text selection toolbar for highlighting and annotating text in the PIEoneer assessment player. Uses modern CSS Custom Highlight API for zero DOM mutation and optimal performance.
3
+ A text selection toolbar for highlighting and annotating text in PIE assessment players. Uses modern CSS Custom Highlight API for zero DOM mutation and optimal performance.
4
4
 
5
5
  ## Features
6
6
 
@@ -39,16 +39,14 @@ A text selection toolbar for highlighting and annotating text in the PIEoneer as
39
39
 
40
40
  ## Props
41
41
 
42
- The strip shows itself when text is selected and hides when the selection is cleared or Escape is pressed. What it needs from its host is the services it annotates and reads with, and the actions it should offer on a selection.
42
+ The strip shows itself when text is selected and hides when the selection is cleared or Escape is pressed. It records annotations through the toolkit runtime context's `highlightCoordinator` and offers read-aloud when that context carries a `ttsService`; without a highlight coordinator the highlight controls no-op.
43
43
 
44
- | Name | Attribute | Type | Notes |
45
- | --------------------- | --------- | ----------------------- | ------------------------------------------------------------------------- |
46
- | `enabled` | `enabled` | boolean | Defaults to `true`; `false` stops it reacting to selections. |
47
- | `highlightCoordinator`| — | object | Where annotations are recorded. Without one the highlight controls no-op. |
48
- | `ttsService` | — | object | Read-aloud is offered only when present. |
49
- | `selectionActions` | — | `ToolSelectionAction[]` | Host-supplied actions on the current selection. See below. |
44
+ | Name | Attribute | Type | Notes |
45
+ | ------------------ | --------- | ----------------------- | ------------------------------------------------------------ |
46
+ | `enabled` | `enabled` | boolean | Defaults to `true`; `false` stops it reacting to selections. |
47
+ | `selectionActions` | — | `ToolSelectionAction[]` | Host-supplied actions on the current selection. See below. |
50
48
 
51
- Under `<pie-assessment-toolkit>` all four are supplied by the capability's registration, so a host mounting the strip through the toolkit passes nothing.
49
+ Under `<pie-assessment-toolkit>` both are supplied by the capability's registration, so a host mounting the strip through the toolkit passes nothing.
52
50
 
53
51
  ### Selection actions
54
52
 
@@ -74,7 +72,7 @@ The toolbar takes its surface and text from the canonical `--pie-background` and
74
72
  | `--pie-annotation-underline` | `#4221d5` | Underline mark on a light page |
75
73
  | `--pie-annotation-underline-dark` | `#9c89ec` | Underline mark on a dark page |
76
74
 
77
- The underline tokens are applied by `HighlightCoordinator` in `@pie-players/pie-assessment-toolkit`, which owns the `::highlight()` rules. They exist as a pair because one value cannot serve both surfaces — `#4221d5` is 2.41:1 on black and `#9c89ec` is 2.85:1 on white — and as two separate tokens so overriding one never silently moves the other. Neither consults `--pie-primary`: a theme accent is chosen against one background and is illegible on the other.
75
+ The underline tokens are applied by `HighlightCoordinator` in `@pie-players/pie-assessment-toolkit`, which owns the `::highlight()` rules. They exist as a pair because one value cannot serve both surfaces — `#4221d5` is 2.41:1 on black and `#9c89ec` is 2.85:1 on white — and as two separate tokens so overriding one never silently moves the other. Under `data-theme="light"` and `data-theme="dark"` the underline reads only its own token, because a theme accent is chosen against one background and is illegible on the other. Under any other `data-theme` value, such as a host or DaisyUI palette, it reads `--pie-annotation-underline` and falls back to that palette's `--pie-primary`.
78
76
 
79
77
  Which of the pair applies is decided by `[data-theme]`, which reports what the **page** declares — not which color scheme is active. A host that declares itself light while running a dark scheme would otherwise pin the light value over a dark background, so the accessibility palettes hand both states their own accent (below).
80
78
 
@@ -92,7 +90,7 @@ Hosts may override any of the three, but must keep 3:1 against both the toolbar
92
90
 
93
91
  The annotation toolbar integrates with PIE's shared highlight infrastructure:
94
92
 
95
- - **HighlightCoordinator**: Singleton managing both TTS and annotation highlights
93
+ - **HighlightCoordinator**: One per `ToolkitCoordinator`, shared by TTS and annotation highlights
96
94
  - **RangeSerializer**: Serializes/deserializes DOM ranges for persistence
97
95
  - **CSS Custom Highlight API**: Modern browser API for non-invasive highlighting
98
96
 
@@ -108,27 +106,31 @@ For unsupported browsers, the component gracefully degrades (no highlights shown
108
106
 
109
107
  ## Annotation Persistence
110
108
 
111
- Annotations are automatically saved to `sessionStorage` and restored on page load. The storage key includes the current URL path to scope annotations to specific content.
109
+ Annotations are saved to `sessionStorage` after each change and restored two seconds after the toolbar mounts. The key is `pie-annotations:<id>`, where `<id>` is the enclosing item's canonical item id, else its item id, else `global`, so each item keeps its own annotations across navigation within the browser session.
112
110
 
113
- Storage format:
111
+ Storage format, an array with one record per annotation:
114
112
 
115
113
  ```typescript
116
- {
117
- "annotation-highlight-yellow-1234567890": {
118
- startContainer: ["body", "div", "p", "#text"],
114
+ [
115
+ {
116
+ id: "annotation-0",
117
+ color: "yellow",
118
+ timestamp: 1234567890,
119
+ startContainer: "div:nth-of-type(2) > p",
119
120
  startOffset: 10,
120
- endContainer: ["body", "div", "p", "#text"],
121
+ endContainer: "div:nth-of-type(2) > p",
121
122
  endOffset: 20,
122
123
  text: "highlighted text"
123
124
  }
124
- }
125
+ ]
125
126
  ```
126
127
 
127
- Annotations are automatically cleared when:
128
+ `startContainer` and `endContainer` are selector paths relative to the scope root, with ` >>> ` marking a step into a shadow root.
129
+
130
+ Stored annotations are removed when:
128
131
 
129
- - User explicitly clicks "Clear" button
130
- - User navigates to different content
131
- - sessionStorage is cleared
132
+ - The user presses "Clear all", which removes that item's key
133
+ - `sessionStorage` is cleared, for example when the tab closes
132
134
 
133
135
  ## Text-to-Speech Integration
134
136
 
@@ -144,24 +146,25 @@ The annotation toolbar includes a "Read" button that uses the TTS service to rea
144
146
 
145
147
  ### Technical Implementation
146
148
 
147
- The toolbar uses `ttsService.speakRange()` instead of `ttsService.speak()` to ensure accurate word highlighting:
149
+ The toolbar passes the selected range to `ttsService.speak()`, which highlights
150
+ from the selection's offset within its content root:
148
151
 
149
152
  ```typescript
150
- // speakRange() calculates text offset for accurate highlighting
151
- await ttsService.speakRange(selectedRange, {
152
- rate: 1.0,
153
- highlightWords: true
154
- }, {
155
- onEnd: () => ttsSpeaking = false,
156
- onError: (err) => ttsSpeaking = false
157
- });
153
+ ttsSpeaking = true;
154
+ try {
155
+ // contentRoot: the item or passage scope element, else the document element.
156
+ // language: the toolkit's `content-language`; `speak` lets a `lang` in
157
+ // the content win over it.
158
+ // catalogContext: from the shell holding the selection.
159
+ await ttsService.speak(selectedRange, { contentRoot, language, catalogContext });
160
+ } finally {
161
+ ttsSpeaking = false;
162
+ }
158
163
  ```
159
164
 
160
- **Why this matters:**
161
-
162
- - User selects text in the middle of a paragraph
163
- - `speak(text)` would highlight from the beginning of the container (wrong)
164
- - `speakRange(range)` highlights the exact selected text (correct)
165
+ `speak` resolves when reading ends and rejects on a playback error. A
166
+ `data-catalog-idref` node the selection holds whole reads its spoken card, as
167
+ under tts-inline; part of one reads as the selected text.
165
168
 
166
169
  ### UX Details
167
170