triiiceratops 0.9.13 → 0.10.1

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 (133) hide show
  1. package/dist/ArrowCounterClockwise-C6n_F9YZ.js +136 -0
  2. package/dist/X-BUzsFa3u.js +886 -0
  3. package/dist/annotation_tool_point-CCJi2I8J.js +290 -0
  4. package/dist/components/AnnotationOverlay.svelte +179 -86
  5. package/dist/components/LeftFab.svelte +21 -9
  6. package/dist/components/OSDViewer.svelte +20 -3
  7. package/dist/components/ThumbnailGallery.svelte +4 -4
  8. package/dist/components/TriiiceratopsViewer.svelte +167 -49
  9. package/dist/components/TriiiceratopsViewer.svelte.d.ts +5 -5
  10. package/dist/components/TriiiceratopsViewerElement.svelte +2 -2
  11. package/dist/components/TriiiceratopsViewerElement.svelte.d.ts +2 -2
  12. package/dist/custom-element.d.ts +10 -0
  13. package/dist/custom-element.js +13 -0
  14. package/dist/image_filters_reset-CWe7vTJU.js +108 -0
  15. package/dist/index.d.ts +1 -2
  16. package/dist/index.js +0 -1
  17. package/dist/paraglide/messages/_index.d.ts +30 -0
  18. package/dist/paraglide/messages/_index.js +31 -1
  19. package/dist/paraglide/messages/annotation_editor_add_content.d.ts +4 -0
  20. package/dist/paraglide/messages/annotation_editor_add_content.js +34 -0
  21. package/dist/paraglide/messages/annotation_editor_cancel.d.ts +4 -0
  22. package/dist/paraglide/messages/annotation_editor_cancel.js +34 -0
  23. package/dist/paraglide/messages/annotation_editor_create_mode.d.ts +4 -0
  24. package/dist/paraglide/messages/annotation_editor_create_mode.js +34 -0
  25. package/dist/paraglide/messages/annotation_editor_delete.d.ts +4 -0
  26. package/dist/paraglide/messages/annotation_editor_delete.js +34 -0
  27. package/dist/paraglide/messages/annotation_editor_delete_message.d.ts +4 -0
  28. package/dist/paraglide/messages/annotation_editor_delete_message.js +34 -0
  29. package/dist/paraglide/messages/annotation_editor_delete_title.d.ts +4 -0
  30. package/dist/paraglide/messages/annotation_editor_delete_title.js +34 -0
  31. package/dist/paraglide/messages/annotation_editor_delete_tooltip.d.ts +4 -0
  32. package/dist/paraglide/messages/annotation_editor_delete_tooltip.js +34 -0
  33. package/dist/paraglide/messages/annotation_editor_edit_mode.d.ts +4 -0
  34. package/dist/paraglide/messages/annotation_editor_edit_mode.js +34 -0
  35. package/dist/paraglide/messages/annotation_editor_edit_section.d.ts +4 -0
  36. package/dist/paraglide/messages/annotation_editor_edit_section.js +34 -0
  37. package/dist/paraglide/messages/annotation_editor_instruction_create.d.ts +4 -0
  38. package/dist/paraglide/messages/annotation_editor_instruction_create.js +34 -0
  39. package/dist/paraglide/messages/annotation_editor_instruction_edit.d.ts +4 -0
  40. package/dist/paraglide/messages/annotation_editor_instruction_edit.js +34 -0
  41. package/dist/paraglide/messages/annotation_editor_link_placeholder.d.ts +4 -0
  42. package/dist/paraglide/messages/annotation_editor_link_placeholder.js +34 -0
  43. package/dist/paraglide/messages/annotation_editor_redo.d.ts +4 -0
  44. package/dist/paraglide/messages/annotation_editor_redo.js +34 -0
  45. package/dist/paraglide/messages/annotation_editor_save.d.ts +4 -0
  46. package/dist/paraglide/messages/annotation_editor_save.js +34 -0
  47. package/dist/paraglide/messages/annotation_editor_tag_placeholder.d.ts +4 -0
  48. package/dist/paraglide/messages/annotation_editor_tag_placeholder.js +34 -0
  49. package/dist/paraglide/messages/annotation_editor_text_placeholder.d.ts +4 -0
  50. package/dist/paraglide/messages/annotation_editor_text_placeholder.js +34 -0
  51. package/dist/paraglide/messages/annotation_editor_title.d.ts +4 -0
  52. package/dist/paraglide/messages/annotation_editor_title.js +34 -0
  53. package/dist/paraglide/messages/annotation_editor_tool_label.d.ts +4 -0
  54. package/dist/paraglide/messages/annotation_editor_tool_label.js +34 -0
  55. package/dist/paraglide/messages/annotation_editor_undo.d.ts +4 -0
  56. package/dist/paraglide/messages/annotation_editor_undo.js +34 -0
  57. package/dist/paraglide/messages/annotation_tool_point.d.ts +4 -0
  58. package/dist/paraglide/messages/annotation_tool_point.js +34 -0
  59. package/dist/paraglide/messages/annotation_tool_polygon.d.ts +4 -0
  60. package/dist/paraglide/messages/annotation_tool_polygon.js +34 -0
  61. package/dist/paraglide/messages/annotation_tool_rectangle.d.ts +4 -0
  62. package/dist/paraglide/messages/annotation_tool_rectangle.js +34 -0
  63. package/dist/paraglide/messages/image_adjustments_title.d.ts +4 -0
  64. package/dist/paraglide/messages/image_adjustments_title.js +34 -0
  65. package/dist/paraglide/messages/image_filters_brightness.d.ts +4 -0
  66. package/dist/paraglide/messages/image_filters_brightness.js +34 -0
  67. package/dist/paraglide/messages/image_filters_contrast.d.ts +4 -0
  68. package/dist/paraglide/messages/image_filters_contrast.js +34 -0
  69. package/dist/paraglide/messages/image_filters_effects.d.ts +4 -0
  70. package/dist/paraglide/messages/image_filters_effects.js +34 -0
  71. package/dist/paraglide/messages/image_filters_grayscale.d.ts +4 -0
  72. package/dist/paraglide/messages/image_filters_grayscale.js +34 -0
  73. package/dist/paraglide/messages/image_filters_invert.d.ts +4 -0
  74. package/dist/paraglide/messages/image_filters_invert.js +34 -0
  75. package/dist/paraglide/messages/image_filters_reset.d.ts +4 -0
  76. package/dist/paraglide/messages/image_filters_reset.js +34 -0
  77. package/dist/paraglide/messages/image_filters_saturation.d.ts +4 -0
  78. package/dist/paraglide/messages/image_filters_saturation.js +34 -0
  79. package/dist/paraglide/messages/plugins_tooltip.js +1 -1
  80. package/dist/plugins/annotation-editor/AnnotationEditorController.svelte +166 -0
  81. package/dist/plugins/annotation-editor/AnnotationEditorController.svelte.d.ts +9 -0
  82. package/dist/plugins/annotation-editor/AnnotationEditorPanel.svelte +315 -0
  83. package/dist/plugins/annotation-editor/AnnotationEditorPanel.svelte.d.ts +24 -0
  84. package/dist/plugins/annotation-editor/AnnotationManager.svelte.d.ts +39 -0
  85. package/dist/plugins/annotation-editor/AnnotationManager.svelte.js +433 -0
  86. package/dist/plugins/annotation-editor/adapters/LocalStorageAdapter.d.ts +20 -0
  87. package/dist/plugins/annotation-editor/adapters/LocalStorageAdapter.js +67 -0
  88. package/dist/plugins/annotation-editor/adapters/index.d.ts +2 -0
  89. package/dist/plugins/annotation-editor/adapters/index.js +1 -0
  90. package/dist/plugins/annotation-editor/adapters/types.d.ts +23 -0
  91. package/dist/plugins/annotation-editor/adapters/types.js +1 -0
  92. package/dist/plugins/annotation-editor/iife-entry.d.ts +15 -0
  93. package/dist/plugins/annotation-editor/iife-entry.js +35 -0
  94. package/dist/plugins/annotation-editor/index.d.ts +41 -0
  95. package/dist/plugins/annotation-editor/index.js +57 -0
  96. package/dist/plugins/annotation-editor/loader.svelte.d.ts +7 -0
  97. package/dist/plugins/annotation-editor/loader.svelte.js +32 -0
  98. package/dist/plugins/annotation-editor/types.d.ts +41 -0
  99. package/dist/plugins/annotation-editor/types.js +13 -0
  100. package/dist/plugins/annotation-editor.js +32824 -0
  101. package/dist/plugins/image-manipulation/ImageManipulationController.svelte +54 -0
  102. package/dist/plugins/image-manipulation/ImageManipulationController.svelte.d.ts +6 -0
  103. package/dist/plugins/image-manipulation/ImageManipulationPanel.svelte +19 -9
  104. package/dist/plugins/image-manipulation/ImageManipulationPanel.svelte.d.ts +1 -0
  105. package/dist/plugins/image-manipulation/iife-entry.d.ts +13 -0
  106. package/dist/plugins/image-manipulation/iife-entry.js +17 -0
  107. package/dist/plugins/image-manipulation/index.d.ts +15 -1
  108. package/dist/plugins/image-manipulation/index.js +21 -1
  109. package/dist/plugins/image-manipulation.js +265 -0
  110. package/dist/state/i18n.svelte.js +4 -2
  111. package/dist/state/manifests.svelte.d.ts +7 -2
  112. package/dist/state/manifests.svelte.js +45 -13
  113. package/dist/state/viewer.svelte.d.ts +16 -13
  114. package/dist/state/viewer.svelte.js +75 -84
  115. package/dist/triiiceratops-bundle.js +3260 -3155
  116. package/dist/triiiceratops-element.css +1 -0
  117. package/dist/triiiceratops-element.iife.js +99 -0
  118. package/dist/triiiceratops.css +1 -1
  119. package/dist/types/plugin.d.ts +21 -62
  120. package/dist/types/plugin.js +1 -23
  121. package/dist/utils/annotationAdapter.d.ts +12 -1
  122. package/dist/utils/annotationAdapter.js +98 -39
  123. package/package.json +13 -6
  124. package/dist/chunks/TriiiceratopsViewer-EViTQO_n.js +0 -10437
  125. package/dist/chunks/openseadragon-CHvATAD9.js +0 -12427
  126. package/dist/components/TriiiceratopsViewerElementImage.svelte +0 -143
  127. package/dist/components/TriiiceratopsViewerElementImage.svelte.d.ts +0 -27
  128. package/dist/custom-element-image.d.ts +0 -1
  129. package/dist/custom-element-image.js +0 -2
  130. package/dist/plugins/image-manipulation/ImageManipulationPlugin.svelte.d.ts +0 -19
  131. package/dist/plugins/image-manipulation/ImageManipulationPlugin.svelte.js +0 -87
  132. package/dist/triiiceratops-element-image.js +0 -555
  133. package/dist/triiiceratops-element.js +0 -114
@@ -0,0 +1,290 @@
1
+ import { a as t } from "./X-BUzsFa3u.js";
2
+ const a = (
3
+ /** @type {(inputs: {}) => LocalizedString} */
4
+ () => (
5
+ /** @type {LocalizedString} */
6
+ "Annotation Editor"
7
+ )
8
+ ), r = (
9
+ /** @type {(inputs: {}) => LocalizedString} */
10
+ () => (
11
+ /** @type {LocalizedString} */
12
+ "Anmerkungs-Editor"
13
+ )
14
+ ), Q = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? a() : r(), i = (
15
+ /** @type {(inputs: {}) => LocalizedString} */
16
+ () => (
17
+ /** @type {LocalizedString} */
18
+ "Edit"
19
+ )
20
+ ), l = (
21
+ /** @type {(inputs: {}) => LocalizedString} */
22
+ () => (
23
+ /** @type {LocalizedString} */
24
+ "Bearbeiten"
25
+ )
26
+ ), V = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? i() : l(), c = (
27
+ /** @type {(inputs: {}) => LocalizedString} */
28
+ () => (
29
+ /** @type {LocalizedString} */
30
+ "Create"
31
+ )
32
+ ), _ = (
33
+ /** @type {(inputs: {}) => LocalizedString} */
34
+ () => (
35
+ /** @type {LocalizedString} */
36
+ "Erstellen"
37
+ )
38
+ ), X = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? c() : _(), s = (
39
+ /** @type {(inputs: {}) => LocalizedString} */
40
+ () => (
41
+ /** @type {LocalizedString} */
42
+ "Click to start annotating, click again to set shape"
43
+ )
44
+ ), d = (
45
+ /** @type {(inputs: {}) => LocalizedString} */
46
+ () => (
47
+ /** @type {LocalizedString} */
48
+ "Klicken zum Annotieren, erneut klicken zum Abschließen"
49
+ )
50
+ ), Y = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? s() : d(), u = (
51
+ /** @type {(inputs: {}) => LocalizedString} */
52
+ () => (
53
+ /** @type {LocalizedString} */
54
+ "Click on an annotation to select and edit it"
55
+ )
56
+ ), p = (
57
+ /** @type {(inputs: {}) => LocalizedString} */
58
+ () => (
59
+ /** @type {LocalizedString} */
60
+ "Anmerkung klicken zum Bearbeiten"
61
+ )
62
+ ), $ = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? u() : p(), g = (
63
+ /** @type {(inputs: {}) => LocalizedString} */
64
+ () => (
65
+ /** @type {LocalizedString} */
66
+ "Drawing Tool"
67
+ )
68
+ ), h = (
69
+ /** @type {(inputs: {}) => LocalizedString} */
70
+ () => (
71
+ /** @type {LocalizedString} */
72
+ "Zeichenwerkzeug"
73
+ )
74
+ ), nn = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? g() : h(), f = (
75
+ /** @type {(inputs: {}) => LocalizedString} */
76
+ () => (
77
+ /** @type {LocalizedString} */
78
+ "Undo"
79
+ )
80
+ ), k = (
81
+ /** @type {(inputs: {}) => LocalizedString} */
82
+ () => (
83
+ /** @type {LocalizedString} */
84
+ "Rückgängig"
85
+ )
86
+ ), tn = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? f() : k(), m = (
87
+ /** @type {(inputs: {}) => LocalizedString} */
88
+ () => (
89
+ /** @type {LocalizedString} */
90
+ "Redo"
91
+ )
92
+ ), b = (
93
+ /** @type {(inputs: {}) => LocalizedString} */
94
+ () => (
95
+ /** @type {LocalizedString} */
96
+ "Wiederholen"
97
+ )
98
+ ), en = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? m() : b(), A = (
99
+ /** @type {(inputs: {}) => LocalizedString} */
100
+ () => (
101
+ /** @type {LocalizedString} */
102
+ "Edit Annotation"
103
+ )
104
+ ), y = (
105
+ /** @type {(inputs: {}) => LocalizedString} */
106
+ () => (
107
+ /** @type {LocalizedString} */
108
+ "Anmerkung bearbeiten"
109
+ )
110
+ ), on = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? A() : y(), v = (
111
+ /** @type {(inputs: {}) => LocalizedString} */
112
+ () => (
113
+ /** @type {LocalizedString} */
114
+ "Delete annotation"
115
+ )
116
+ ), x = (
117
+ /** @type {(inputs: {}) => LocalizedString} */
118
+ () => (
119
+ /** @type {LocalizedString} */
120
+ "Anmerkung löschen"
121
+ )
122
+ ), an = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? v() : x(), C = (
123
+ /** @type {(inputs: {}) => LocalizedString} */
124
+ () => (
125
+ /** @type {LocalizedString} */
126
+ "Tag value..."
127
+ )
128
+ ), E = (
129
+ /** @type {(inputs: {}) => LocalizedString} */
130
+ () => (
131
+ /** @type {LocalizedString} */
132
+ "Tag eingeben..."
133
+ )
134
+ ), rn = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? C() : E(), w = (
135
+ /** @type {(inputs: {}) => LocalizedString} */
136
+ () => (
137
+ /** @type {LocalizedString} */
138
+ "https://..."
139
+ )
140
+ ), z = (
141
+ /** @type {(inputs: {}) => LocalizedString} */
142
+ () => (
143
+ /** @type {LocalizedString} */
144
+ "https://..."
145
+ )
146
+ ), ln = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? w() : z(), D = (
147
+ /** @type {(inputs: {}) => LocalizedString} */
148
+ () => (
149
+ /** @type {LocalizedString} */
150
+ "Enter text..."
151
+ )
152
+ ), T = (
153
+ /** @type {(inputs: {}) => LocalizedString} */
154
+ () => (
155
+ /** @type {LocalizedString} */
156
+ "Text eingeben..."
157
+ )
158
+ ), cn = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? D() : T(), P = (
159
+ /** @type {(inputs: {}) => LocalizedString} */
160
+ () => (
161
+ /** @type {LocalizedString} */
162
+ "Add Content"
163
+ )
164
+ ), R = (
165
+ /** @type {(inputs: {}) => LocalizedString} */
166
+ () => (
167
+ /** @type {LocalizedString} */
168
+ "Inhalt hinzufügen"
169
+ )
170
+ ), _n = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? P() : R(), B = (
171
+ /** @type {(inputs: {}) => LocalizedString} */
172
+ () => (
173
+ /** @type {LocalizedString} */
174
+ "Save Changes"
175
+ )
176
+ ), L = (
177
+ /** @type {(inputs: {}) => LocalizedString} */
178
+ () => (
179
+ /** @type {LocalizedString} */
180
+ "Speichern"
181
+ )
182
+ ), sn = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? B() : L(), S = (
183
+ /** @type {(inputs: {}) => LocalizedString} */
184
+ () => (
185
+ /** @type {LocalizedString} */
186
+ "Delete Annotation?"
187
+ )
188
+ ), j = (
189
+ /** @type {(inputs: {}) => LocalizedString} */
190
+ () => (
191
+ /** @type {LocalizedString} */
192
+ "Anmerkung löschen?"
193
+ )
194
+ ), dn = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? S() : j(), q = (
195
+ /** @type {(inputs: {}) => LocalizedString} */
196
+ () => (
197
+ /** @type {LocalizedString} */
198
+ "Are you sure you want to delete this annotation? This action cannot be undone."
199
+ )
200
+ ), I = (
201
+ /** @type {(inputs: {}) => LocalizedString} */
202
+ () => (
203
+ /** @type {LocalizedString} */
204
+ "Anmerkung wirklich löschen? Dies kann nicht rückgängig gemacht werden."
205
+ )
206
+ ), un = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? q() : I(), K = (
207
+ /** @type {(inputs: {}) => LocalizedString} */
208
+ () => (
209
+ /** @type {LocalizedString} */
210
+ "Cancel"
211
+ )
212
+ ), U = (
213
+ /** @type {(inputs: {}) => LocalizedString} */
214
+ () => (
215
+ /** @type {LocalizedString} */
216
+ "Abbrechen"
217
+ )
218
+ ), pn = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? K() : U(), W = (
219
+ /** @type {(inputs: {}) => LocalizedString} */
220
+ () => (
221
+ /** @type {LocalizedString} */
222
+ "Delete"
223
+ )
224
+ ), Z = (
225
+ /** @type {(inputs: {}) => LocalizedString} */
226
+ () => (
227
+ /** @type {LocalizedString} */
228
+ "Löschen"
229
+ )
230
+ ), gn = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? W() : Z(), F = (
231
+ /** @type {(inputs: {}) => LocalizedString} */
232
+ () => (
233
+ /** @type {LocalizedString} */
234
+ "Rectangle"
235
+ )
236
+ ), G = (
237
+ /** @type {(inputs: {}) => LocalizedString} */
238
+ () => (
239
+ /** @type {LocalizedString} */
240
+ "Rechteck"
241
+ )
242
+ ), hn = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? F() : G(), H = (
243
+ /** @type {(inputs: {}) => LocalizedString} */
244
+ () => (
245
+ /** @type {LocalizedString} */
246
+ "Polygon"
247
+ )
248
+ ), J = (
249
+ /** @type {(inputs: {}) => LocalizedString} */
250
+ () => (
251
+ /** @type {LocalizedString} */
252
+ "Polygon"
253
+ )
254
+ ), fn = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? H() : J(), M = (
255
+ /** @type {(inputs: {}) => LocalizedString} */
256
+ () => (
257
+ /** @type {LocalizedString} */
258
+ "Point"
259
+ )
260
+ ), N = (
261
+ /** @type {(inputs: {}) => LocalizedString} */
262
+ () => (
263
+ /** @type {LocalizedString} */
264
+ "Punkt"
265
+ )
266
+ ), kn = /* @__NO_SIDE_EFFECTS__ */ (e = {}, n = {}) => (n.locale ?? t()) === "en" ? M() : N();
267
+ export {
268
+ _n as a,
269
+ pn as b,
270
+ X as c,
271
+ gn as d,
272
+ un as e,
273
+ dn as f,
274
+ an as g,
275
+ V as h,
276
+ on as i,
277
+ Y as j,
278
+ $ as k,
279
+ ln as l,
280
+ en as m,
281
+ sn as n,
282
+ rn as o,
283
+ cn as p,
284
+ Q as q,
285
+ nn as r,
286
+ tn as s,
287
+ kn as t,
288
+ fn as u,
289
+ hn as v
290
+ };
@@ -6,6 +6,7 @@
6
6
  import { manifestsState } from '../state/manifests.svelte';
7
7
  import { VIEWER_STATE_KEY, type ViewerState } from '../state/viewer.svelte';
8
8
  import { m } from '../state/i18n.svelte';
9
+ import { extractBody } from '../utils/annotationAdapter';
9
10
 
10
11
  const viewerState = getContext<ViewerState>(VIEWER_STATE_KEY);
11
12
 
@@ -92,83 +93,79 @@
92
93
  }
93
94
  }
94
95
 
95
- let renderedAnnotations = $derived.by(() => {
96
- if (!annotations.length) return [];
96
+ // State for hovered annotation to draw connecting line
97
+ let hoveredAnnotationId: string | null = $state(null);
98
+ let toolbarContainer: HTMLElement | undefined = $state();
97
99
 
98
- return annotations.map((anno: any) => {
99
- let content = '';
100
- let isHtml = false;
101
-
102
- // Extract content (support IIIF v2 and v3)
103
- if (typeof anno.getBody === 'function') {
104
- const body = anno.getBody();
105
- if (body && body.length) {
106
- const getValue = (b: any) => {
107
- const val = b.getValue ? b.getValue() : null;
108
- if (val) return val;
109
- return '';
110
- };
111
- content = body
112
- .map((b: any) => getValue(b))
113
- .filter(Boolean)
114
- .join(' ');
115
- isHtml = body.some((b: any) => {
116
- const fmt = b.getFormat ? b.getFormat() : '';
117
- return (
118
- fmt === 'text/html' || fmt === 'application/html'
119
- );
120
- });
121
- }
122
- if (!content && typeof anno.getLabel === 'function') {
123
- const label = anno.getLabel();
124
- if (label) content = label;
100
+ // Calculate coordinates for connecting line
101
+ let connectingLine = $derived.by(() => {
102
+ if (!hoveredAnnotationId) return null;
103
+ return null;
104
+ });
105
+
106
+ let lineCoords: { x1: number; y1: number; x2: number; y2: number } | null =
107
+ $state(null);
108
+
109
+ $effect(() => {
110
+ if (!hoveredAnnotationId) {
111
+ lineCoords = null;
112
+ return;
113
+ }
114
+
115
+ const updateCoords = () => {
116
+ let root: Document | ShadowRoot = document;
117
+ if (toolbarContainer) {
118
+ const node = toolbarContainer.getRootNode();
119
+ if (node instanceof Document || node instanceof ShadowRoot) {
120
+ root = node;
125
121
  }
122
+ }
123
+
124
+ const listItem = root.getElementById(
125
+ `annotation-list-item-${hoveredAnnotationId}`,
126
+ );
127
+ const visual = root.getElementById(
128
+ `annotation-visual-${hoveredAnnotationId}`,
129
+ );
130
+
131
+ if (listItem && visual) {
132
+ const listRect = listItem.getBoundingClientRect();
133
+ const visualRect = visual.getBoundingClientRect();
134
+
135
+ // Calculate connection points
136
+ // List item: Left center
137
+ const startX = listRect.left;
138
+ const startY = listRect.top + listRect.height / 2;
139
+
140
+ // Visual: Center of the annotation visual
141
+ const endX = visualRect.left + visualRect.width / 2;
142
+ const endY = visualRect.top + visualRect.height / 2;
143
+
144
+ lineCoords = { x1: startX, y1: startY, x2: endX, y2: endY };
126
145
  } else {
127
- // Raw JSON Parsing (Fallback)
128
- const getText = (r: any) => {
129
- if (!r) return '';
130
- return r.chars || r.value || r['cnt:chars'] || '';
131
- };
132
- const isHtmlFormat = (r: any) => {
133
- if (!r) return false;
134
- return r.format === 'text/html' || r.type === 'TextualBody';
135
- };
136
- if (anno.resource) {
137
- if (Array.isArray(anno.resource)) {
138
- content = anno.resource
139
- .map((r: any) => getText(r))
140
- .join(' ');
141
- if (anno.resource.some((r: any) => isHtmlFormat(r)))
142
- isHtml = true;
143
- } else {
144
- content = getText(anno.resource);
145
- if (isHtmlFormat(anno.resource)) isHtml = true;
146
- }
147
- } else if (anno.body) {
148
- if (Array.isArray(anno.body)) {
149
- content = anno.body
150
- .map((b: any) => getText(b))
151
- .join(' ');
152
- if (anno.body.some((b: any) => isHtmlFormat(b)))
153
- isHtml = true;
154
- } else {
155
- content = getText(anno.body);
156
- if (isHtmlFormat(anno.body)) isHtml = true;
157
- }
158
- }
159
- if (!content) {
160
- const fallback = anno.label || anno.name || anno.title;
161
- if (fallback)
162
- content = Array.isArray(fallback)
163
- ? fallback.join(' ')
164
- : fallback;
165
- }
146
+ lineCoords = null;
166
147
  }
148
+ };
149
+
150
+ // Run immediately
151
+ updateCoords();
152
+
153
+ // Optional: Could add resize listener or interval if things move,
154
+ // but for hover it might be enough to just set it once or on scroll
155
+ const interval = setInterval(updateCoords, 16); // ~60fps follow
156
+
157
+ return () => clearInterval(interval);
158
+ });
159
+
160
+ let renderedAnnotations = $derived.by(() => {
161
+ if (!annotations.length) return [];
162
+
163
+ return annotations.map((anno: any) => {
164
+ const bodies = extractBody(anno);
167
165
 
168
166
  return {
169
167
  id: getAnnotationId(anno),
170
- content,
171
- isHtml,
168
+ bodies,
172
169
  label:
173
170
  (typeof anno.getLabel === 'function'
174
171
  ? anno.getLabel()
@@ -178,9 +175,39 @@
178
175
  });
179
176
  </script>
180
177
 
178
+ {#if lineCoords}
179
+ <svg
180
+ class="fixed inset-0 z-50 pointer-events-none drop-shadow-md text-primary"
181
+ style="width: 100vw; height: 100vh;"
182
+ >
183
+ <line
184
+ x1={lineCoords.x1}
185
+ y1={lineCoords.y1}
186
+ x2={lineCoords.x2}
187
+ y2={lineCoords.y2}
188
+ stroke="currentColor"
189
+ stroke-width="2"
190
+ />
191
+ <!-- Optional: Dots at ends -->
192
+ <circle
193
+ cx={lineCoords.x1}
194
+ cy={lineCoords.y1}
195
+ r="3"
196
+ fill="currentColor"
197
+ />
198
+ <circle
199
+ cx={lineCoords.x2}
200
+ cy={lineCoords.y2}
201
+ r="3"
202
+ fill="currentColor"
203
+ />
204
+ </svg>
205
+ {/if}
206
+
181
207
  <!-- Unified Annotation Toolbar -->
182
208
  {#if viewerState.showAnnotations}
183
209
  <div
210
+ bind:this={toolbarContainer}
184
211
  class="absolute top-4 right-4 z-40 pointer-events-auto transition-all duration-300"
185
212
  >
186
213
  <!-- z-index increased for Leaflet (z-400 is map) -->
@@ -231,32 +258,52 @@
231
258
  {@const isVisible = viewerState.visibleAnnotationIds.has(
232
259
  anno.id,
233
260
  )}
234
- <!-- List Item Row: Click toggles visibility -->
235
- <button
236
- class="w-full text-left p-3 hover:bg-base-300 transition-colors cursor-pointer flex gap-3 group/item items-start focus:outline-none focus:bg-base-300"
261
+ <!-- List Item Row -->
262
+ <div
263
+ id="annotation-list-item-{anno.id}"
264
+ class="w-full text-left p-3 hover:bg-base-300 transition-colors cursor-pointer flex gap-3 group/item items-start focus:outline-none focus:bg-base-300 relative"
265
+ role="button"
266
+ tabindex="0"
267
+ onmouseenter={() => (hoveredAnnotationId = anno.id)}
268
+ onmouseleave={() => (hoveredAnnotationId = null)}
237
269
  onclick={(e) => {
238
270
  e.preventDefault();
239
271
  toggleAnnotation(anno.id);
240
272
  }}
273
+ onkeypress={(e) => {
274
+ if (e.key === 'Enter' || e.key === ' ') {
275
+ e.preventDefault();
276
+ toggleAnnotation(anno.id);
277
+ }
278
+ }}
241
279
  >
242
- <!-- Visual Toggle Indicator (formerly a button) -->
243
- <div
244
- class="btn btn-xs btn-circle btn-ghost mt-0.5 shrink-0 pointer-events-none"
280
+ <!-- Visual Toggle Indicator (eye icon button) -->
281
+ <!-- If user wants only the eye to toggle, we should stopPropagation on it,
282
+ and maybe remove toggle from the main click?
283
+ Wait, user updated task: "Changing the list item interaction so that clicking the item no longer key-toggles visibility. ... Disregard this."
284
+ So click anywhere ON the row still toggles.
285
+ -->
286
+ <button
287
+ class="btn btn-xs btn-circle btn-ghost mt-0.5 shrink-0"
288
+ onclick={(e) => {
289
+ e.stopPropagation();
290
+ toggleAnnotation(anno.id);
291
+ }}
245
292
  >
246
293
  {#if isVisible}
247
294
  <Eye size={16} weight="bold" />
248
295
  {:else}
249
296
  <EyeSlash size={16} weight="bold" />
250
297
  {/if}
251
- </div>
298
+ </button>
252
299
 
253
- <div class="flex-1 min-w-0">
300
+ <div class="flex-1 min-w-0 pointer-events-none">
254
301
  <div class="flex items-start justify-between">
255
302
  <span class="font-bold text-sm text-primary"
256
303
  >#{i + 1}</span
257
304
  >
258
305
  <!-- Only show label separately if it's different from the content being displayed -->
259
- {#if anno.label && anno.label !== anno.content}
306
+ {#if anno.label && !anno.bodies.some((b) => b.value === anno.label)}
260
307
  <span
261
308
  class="text-xs opacity-50 truncate max-w-[150px]"
262
309
  >{anno.label}</span
@@ -266,17 +313,63 @@
266
313
  <div
267
314
  class="text-sm prose prose-sm max-w-none prose-p:my-0 prose-a:text-blue-500 wrap-break-word text-left {isVisible
268
315
  ? ''
269
- : 'opacity-50'}"
316
+ : 'opacity-50'} space-y-2"
270
317
  >
271
- <!-- eslint-disable-next-line svelte/no-at-html-tags -->
272
- {#if anno.isHtml}
273
- {@html anno.content}
274
- {:else}
275
- {anno.content || '(No content)'}
318
+ {#each anno.bodies as body}
319
+ <div
320
+ class="flex flex-wrap gap-2 pointer-events-auto"
321
+ >
322
+ {#if body.purpose === 'tagging'}
323
+ <span
324
+ class="badge badge-primary badge-outline badge-sm"
325
+ >
326
+ {body.value}
327
+ </span>
328
+ {:else if body.purpose === 'linking'}
329
+ <a
330
+ href={body.value}
331
+ target="_blank"
332
+ rel="noopener noreferrer"
333
+ class="flex items-center gap-1 text-primary hover:underline hover:text-primary-focus p-1 rounded hover:bg-base-200 -ml-1 transition-colors"
334
+ onclick={(e) =>
335
+ e.stopPropagation()}
336
+ >
337
+ <!-- Link Icon -->
338
+ <svg
339
+ xmlns="http://www.w3.org/2000/svg"
340
+ width="12"
341
+ height="12"
342
+ fill="currentColor"
343
+ viewBox="0 0 256 256"
344
+ ><path
345
+ d="M136.37,187.53a12,12,0,0,1,0,17l-5.94,5.94a60,60,0,0,1-84.88-84.88l24.12-24.12A60,60,0,0,1,152.06,99,12,12,0,1,1,135,116a36,36,0,0,0-50.93,1.57L60,141.66a36,36,0,0,0,50.93,50.93l5.94-5.94A12,12,0,0,1,136.37,187.53Zm81.51-149.41a60,60,0,0,0-84.88,0l-5.94,5.94a12,12,0,0,0,17,17l5.94-5.94a36,36,0,0,1,50.93,50.93l-24.11,24.12A36,36,0,0,1,121,140a12,12,0,1,0-17.08,17,60,60,0,0,0,82.39,2.46l24.12-24.12A60,60,0,0,0,217.88,38.12Z"
346
+ ></path></svg
347
+ >
348
+ <span
349
+ class="truncate max-w-[200px]"
350
+ >{body.value}</span
351
+ >
352
+ </a>
353
+ {:else}
354
+ <!-- Commenting / Default -->
355
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
356
+ {#if body.isHtml}
357
+ {@html body.value}
358
+ {:else}
359
+ {body.value || '(No content)'}
360
+ {/if}
361
+ {/if}
362
+ </div>
363
+ {/each}
364
+
365
+ {#if anno.bodies.length === 0}
366
+ <span class="opacity-50 italic text-xs"
367
+ >(No content)</span
368
+ >
276
369
  {/if}
277
370
  </div>
278
371
  </div>
279
- </button>
372
+ </div>
280
373
  {:else}
281
374
  <div class="p-4 text-center opacity-50 text-sm">
282
375
  No annotations available.
@@ -3,18 +3,25 @@
3
3
  import PuzzlePiece from 'phosphor-svelte/lib/PuzzlePiece';
4
4
  import X from 'phosphor-svelte/lib/X';
5
5
  import { VIEWER_STATE_KEY, type ViewerState } from '../state/viewer.svelte';
6
- import { m } from '../state/i18n.svelte';
6
+ import { m, language } from '../state/i18n.svelte';
7
7
 
8
8
  const viewerState = getContext<ViewerState>(VIEWER_STATE_KEY);
9
9
 
10
- let sortedPluginButtons = $derived(
11
- [...viewerState.pluginMenuButtons].sort(
10
+ let sortedPluginButtons = $derived.by(() => {
11
+ // Read language to trigger re-evaluation
12
+ language.current;
13
+ return [...viewerState.pluginMenuButtons].sort(
12
14
  (a, b) => (a.order ?? 100) - (b.order ?? 100),
13
- ),
14
- );
15
+ );
16
+ });
15
17
 
16
18
  let isOpen = $state(false);
17
19
 
20
+ let pluginsTooltip = $derived.by(() => {
21
+ language.current;
22
+ return m.plugins_tooltip();
23
+ });
24
+
18
25
  function toggleOpen() {
19
26
  isOpen = !isOpen;
20
27
  }
@@ -32,7 +39,13 @@
32
39
  >
33
40
  {#each sortedPluginButtons as button (button.id)}
34
41
  {@const Icon = button.icon}
35
- <div class="tooltip tooltip-right" data-tip={button.tooltip}>
42
+ {@const tooltip =
43
+ // @ts-ignore - access message dynamically
44
+ typeof m[button.tooltip] === 'function'
45
+ ? // @ts-ignore
46
+ m[button.tooltip]()
47
+ : button.tooltip}
48
+ <div class="tooltip tooltip-right" data-tip={tooltip}>
36
49
  <button
37
50
  aria-label={button.tooltip}
38
51
  class="btn btn-lg btn-circle shadow-lg {button.isActive?.()
@@ -49,13 +62,12 @@
49
62
  {/each}
50
63
  </div>
51
64
 
52
- <!-- Main Toggle Button -->
53
- <div class="tooltip tooltip-right" data-tip={m.plugins_tooltip()}>
65
+ <div class="tooltip tooltip-right" data-tip={pluginsTooltip}>
54
66
  <button
55
67
  class="btn btn-lg btn-secondary btn-circle shadow-xl transition-transform duration-300 {isOpen
56
68
  ? 'rotate-90'
57
69
  : ''}"
58
- aria-label={m.plugins_tooltip()}
70
+ aria-label={pluginsTooltip}
59
71
  onclick={toggleOpen}
60
72
  >
61
73
  {#if isOpen}