triiiceratops 0.9.12 → 0.10.0

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 (136) hide show
  1. package/dist/ArrowCounterClockwise-aFffCOKw.js +136 -0
  2. package/dist/X-DZEgXrJ8.js +877 -0
  3. package/dist/annotation_tool_point-CZKsj4Nk.js +290 -0
  4. package/dist/components/AnnotationOverlay.svelte +179 -86
  5. package/dist/components/DemoHeader.svelte +12 -0
  6. package/dist/components/LeftFab.svelte +21 -9
  7. package/dist/components/OSDViewer.svelte +24 -4
  8. package/dist/components/SearchPanel.svelte +4 -1
  9. package/dist/components/TriiiceratopsViewer.svelte +114 -47
  10. package/dist/components/TriiiceratopsViewer.svelte.d.ts +5 -5
  11. package/dist/components/TriiiceratopsViewerElement.svelte +2 -2
  12. package/dist/components/TriiiceratopsViewerElement.svelte.d.ts +2 -2
  13. package/dist/custom-element.d.ts +10 -0
  14. package/dist/custom-element.js +13 -0
  15. package/dist/image_filters_reset-BEIf-_QA.js +108 -0
  16. package/dist/index.d.ts +1 -2
  17. package/dist/index.js +0 -1
  18. package/dist/paraglide/messages/_index.d.ts +31 -0
  19. package/dist/paraglide/messages/_index.js +32 -1
  20. package/dist/paraglide/messages/annotation_editor_add_content.d.ts +4 -0
  21. package/dist/paraglide/messages/annotation_editor_add_content.js +34 -0
  22. package/dist/paraglide/messages/annotation_editor_cancel.d.ts +4 -0
  23. package/dist/paraglide/messages/annotation_editor_cancel.js +34 -0
  24. package/dist/paraglide/messages/annotation_editor_create_mode.d.ts +4 -0
  25. package/dist/paraglide/messages/annotation_editor_create_mode.js +34 -0
  26. package/dist/paraglide/messages/annotation_editor_delete.d.ts +4 -0
  27. package/dist/paraglide/messages/annotation_editor_delete.js +34 -0
  28. package/dist/paraglide/messages/annotation_editor_delete_message.d.ts +4 -0
  29. package/dist/paraglide/messages/annotation_editor_delete_message.js +34 -0
  30. package/dist/paraglide/messages/annotation_editor_delete_title.d.ts +4 -0
  31. package/dist/paraglide/messages/annotation_editor_delete_title.js +34 -0
  32. package/dist/paraglide/messages/annotation_editor_delete_tooltip.d.ts +4 -0
  33. package/dist/paraglide/messages/annotation_editor_delete_tooltip.js +34 -0
  34. package/dist/paraglide/messages/annotation_editor_edit_mode.d.ts +4 -0
  35. package/dist/paraglide/messages/annotation_editor_edit_mode.js +34 -0
  36. package/dist/paraglide/messages/annotation_editor_edit_section.d.ts +4 -0
  37. package/dist/paraglide/messages/annotation_editor_edit_section.js +34 -0
  38. package/dist/paraglide/messages/annotation_editor_instruction_create.d.ts +4 -0
  39. package/dist/paraglide/messages/annotation_editor_instruction_create.js +34 -0
  40. package/dist/paraglide/messages/annotation_editor_instruction_edit.d.ts +4 -0
  41. package/dist/paraglide/messages/annotation_editor_instruction_edit.js +34 -0
  42. package/dist/paraglide/messages/annotation_editor_link_placeholder.d.ts +4 -0
  43. package/dist/paraglide/messages/annotation_editor_link_placeholder.js +34 -0
  44. package/dist/paraglide/messages/annotation_editor_redo.d.ts +4 -0
  45. package/dist/paraglide/messages/annotation_editor_redo.js +34 -0
  46. package/dist/paraglide/messages/annotation_editor_save.d.ts +4 -0
  47. package/dist/paraglide/messages/annotation_editor_save.js +34 -0
  48. package/dist/paraglide/messages/annotation_editor_tag_placeholder.d.ts +4 -0
  49. package/dist/paraglide/messages/annotation_editor_tag_placeholder.js +34 -0
  50. package/dist/paraglide/messages/annotation_editor_text_placeholder.d.ts +4 -0
  51. package/dist/paraglide/messages/annotation_editor_text_placeholder.js +34 -0
  52. package/dist/paraglide/messages/annotation_editor_title.d.ts +4 -0
  53. package/dist/paraglide/messages/annotation_editor_title.js +34 -0
  54. package/dist/paraglide/messages/annotation_editor_tool_label.d.ts +4 -0
  55. package/dist/paraglide/messages/annotation_editor_tool_label.js +34 -0
  56. package/dist/paraglide/messages/annotation_editor_undo.d.ts +4 -0
  57. package/dist/paraglide/messages/annotation_editor_undo.js +34 -0
  58. package/dist/paraglide/messages/annotation_tool_point.d.ts +4 -0
  59. package/dist/paraglide/messages/annotation_tool_point.js +34 -0
  60. package/dist/paraglide/messages/annotation_tool_polygon.d.ts +4 -0
  61. package/dist/paraglide/messages/annotation_tool_polygon.js +34 -0
  62. package/dist/paraglide/messages/annotation_tool_rectangle.d.ts +4 -0
  63. package/dist/paraglide/messages/annotation_tool_rectangle.js +34 -0
  64. package/dist/paraglide/messages/image_adjustments_title.d.ts +4 -0
  65. package/dist/paraglide/messages/image_adjustments_title.js +34 -0
  66. package/dist/paraglide/messages/image_filters_brightness.d.ts +4 -0
  67. package/dist/paraglide/messages/image_filters_brightness.js +34 -0
  68. package/dist/paraglide/messages/image_filters_contrast.d.ts +4 -0
  69. package/dist/paraglide/messages/image_filters_contrast.js +34 -0
  70. package/dist/paraglide/messages/image_filters_effects.d.ts +4 -0
  71. package/dist/paraglide/messages/image_filters_effects.js +34 -0
  72. package/dist/paraglide/messages/image_filters_grayscale.d.ts +4 -0
  73. package/dist/paraglide/messages/image_filters_grayscale.js +34 -0
  74. package/dist/paraglide/messages/image_filters_invert.d.ts +4 -0
  75. package/dist/paraglide/messages/image_filters_invert.js +34 -0
  76. package/dist/paraglide/messages/image_filters_reset.d.ts +4 -0
  77. package/dist/paraglide/messages/image_filters_reset.js +34 -0
  78. package/dist/paraglide/messages/image_filters_saturation.d.ts +4 -0
  79. package/dist/paraglide/messages/image_filters_saturation.js +34 -0
  80. package/dist/paraglide/messages/plugins_tooltip.js +1 -1
  81. package/dist/paraglide/messages/settings_transparent_background.d.ts +4 -0
  82. package/dist/paraglide/messages/settings_transparent_background.js +34 -0
  83. package/dist/plugins/annotation-editor/AnnotationEditorController.svelte +166 -0
  84. package/dist/plugins/annotation-editor/AnnotationEditorController.svelte.d.ts +9 -0
  85. package/dist/plugins/annotation-editor/AnnotationEditorPanel.svelte +315 -0
  86. package/dist/plugins/annotation-editor/AnnotationEditorPanel.svelte.d.ts +24 -0
  87. package/dist/plugins/annotation-editor/AnnotationManager.svelte.d.ts +39 -0
  88. package/dist/plugins/annotation-editor/AnnotationManager.svelte.js +433 -0
  89. package/dist/plugins/annotation-editor/adapters/LocalStorageAdapter.d.ts +20 -0
  90. package/dist/plugins/annotation-editor/adapters/LocalStorageAdapter.js +67 -0
  91. package/dist/plugins/annotation-editor/adapters/index.d.ts +2 -0
  92. package/dist/plugins/annotation-editor/adapters/index.js +1 -0
  93. package/dist/plugins/annotation-editor/adapters/types.d.ts +23 -0
  94. package/dist/plugins/annotation-editor/adapters/types.js +1 -0
  95. package/dist/plugins/annotation-editor/iife-entry.d.ts +15 -0
  96. package/dist/plugins/annotation-editor/iife-entry.js +35 -0
  97. package/dist/plugins/annotation-editor/index.d.ts +41 -0
  98. package/dist/plugins/annotation-editor/index.js +57 -0
  99. package/dist/plugins/annotation-editor/loader.svelte.d.ts +7 -0
  100. package/dist/plugins/annotation-editor/loader.svelte.js +32 -0
  101. package/dist/plugins/annotation-editor/types.d.ts +41 -0
  102. package/dist/plugins/annotation-editor/types.js +13 -0
  103. package/dist/plugins/annotation-editor.js +32824 -0
  104. package/dist/plugins/image-manipulation/ImageManipulationController.svelte +54 -0
  105. package/dist/plugins/image-manipulation/ImageManipulationController.svelte.d.ts +6 -0
  106. package/dist/plugins/image-manipulation/ImageManipulationPanel.svelte +19 -9
  107. package/dist/plugins/image-manipulation/ImageManipulationPanel.svelte.d.ts +1 -0
  108. package/dist/plugins/image-manipulation/iife-entry.d.ts +13 -0
  109. package/dist/plugins/image-manipulation/iife-entry.js +17 -0
  110. package/dist/plugins/image-manipulation/index.d.ts +15 -1
  111. package/dist/plugins/image-manipulation/index.js +21 -1
  112. package/dist/plugins/image-manipulation.js +265 -0
  113. package/dist/state/i18n.svelte.js +4 -2
  114. package/dist/state/manifests.svelte.d.ts +5 -0
  115. package/dist/state/manifests.svelte.js +42 -13
  116. package/dist/state/viewer.svelte.d.ts +14 -13
  117. package/dist/state/viewer.svelte.js +63 -74
  118. package/dist/triiiceratops-bundle.js +3208 -3124
  119. package/dist/triiiceratops-element.iife.js +99 -0
  120. package/dist/triiiceratops.css +1 -1
  121. package/dist/types/config.d.ts +5 -0
  122. package/dist/types/plugin.d.ts +21 -62
  123. package/dist/types/plugin.js +1 -23
  124. package/dist/utils/annotationAdapter.d.ts +12 -1
  125. package/dist/utils/annotationAdapter.js +98 -39
  126. package/package.json +13 -6
  127. package/dist/chunks/TriiiceratopsViewer-DpZQA17w.js +0 -10435
  128. package/dist/chunks/openseadragon-5MHeYuQz.js +0 -12427
  129. package/dist/components/TriiiceratopsViewerElementImage.svelte +0 -143
  130. package/dist/components/TriiiceratopsViewerElementImage.svelte.d.ts +0 -27
  131. package/dist/custom-element-image.d.ts +0 -1
  132. package/dist/custom-element-image.js +0 -2
  133. package/dist/plugins/image-manipulation/ImageManipulationPlugin.svelte.d.ts +0 -19
  134. package/dist/plugins/image-manipulation/ImageManipulationPlugin.svelte.js +0 -87
  135. package/dist/triiiceratops-element-image.js +0 -555
  136. package/dist/triiiceratops-element.js +0 -114
@@ -0,0 +1,433 @@
1
+ import { createOSDAnnotator, W3CImageFormat, } from '@annotorious/openseadragon';
2
+ import OpenSeadragon from 'openseadragon';
3
+ import { LocalStorageAdapter } from './adapters/LocalStorageAdapter';
4
+ /**
5
+ * Manages the Annotorious instance and annotation CRUD operations.
6
+ * Instantiated within the controller component.
7
+ */
8
+ export class AnnotationManager {
9
+ config;
10
+ adapter;
11
+ annotorious = null;
12
+ // Store reference to OSD viewer for mouse nav toggling
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ osdViewer = null;
15
+ // Current canvas tracking
16
+ currentManifestId = null;
17
+ currentCanvasId = null;
18
+ // Internal state tracking
19
+ isDrawingEnabled = false;
20
+ activeTool = 'rectangle';
21
+ // Callbacks for state updates (set by controller)
22
+ onSelectionChange;
23
+ onUndoRedoChange;
24
+ onAnnotationCreated;
25
+ constructor(config) {
26
+ this.config = config;
27
+ this.adapter = config.adapter ?? new LocalStorageAdapter();
28
+ this.activeTool = config.defaultTool ?? 'rectangle';
29
+ }
30
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
+ init(viewer, canvasId) {
32
+ if (!viewer) {
33
+ console.error('[AnnotationManager] Cannot init: Viewer is null');
34
+ return;
35
+ }
36
+ // Store viewer reference
37
+ this.osdViewer = viewer;
38
+ this.currentCanvasId = canvasId;
39
+ // If viewer is already open, init immediately
40
+ const worldCount = viewer.world?.getItemCount() ?? 0;
41
+ if (worldCount > 0) {
42
+ this.initAnnotorious(viewer, canvasId);
43
+ }
44
+ else {
45
+ // Wait for open event and give it a moment to settle layout
46
+ viewer.addHandler('open', () => {
47
+ setTimeout(() => {
48
+ this.initAnnotorious(viewer, canvasId);
49
+ }, 250);
50
+ });
51
+ }
52
+ // Attach global click handler for Point tool
53
+ // "canvas-click" is robust and handles drag vs click detection
54
+ // Attach global click handler for ALL tools when drawing is enabled
55
+ // This ensures we can block Zoom on click, while allowing Panning (Drag)
56
+ viewer.addHandler('canvas-click', (event) => {
57
+ if (this.isDrawingEnabled && event.quick) {
58
+ // Prevent default OSD navigation (zoom on click)
59
+ event.preventDefaultAction = true;
60
+ if (this.activeTool === 'point' && this.annotorious) {
61
+ this.handlePointClick(event);
62
+ }
63
+ }
64
+ });
65
+ }
66
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
67
+ initAnnotorious(viewer, canvasId) {
68
+ if (this.annotorious)
69
+ return;
70
+ try {
71
+ const sourceId = canvasId ?? 'unknown';
72
+ // Initial drawing enabled state only if tool is NOT point (Annotorious handles others)
73
+ const initialDrawingEnabled = this.isDrawingEnabled && this.activeTool !== 'point';
74
+ const config = {
75
+ adapter: W3CImageFormat(sourceId),
76
+ drawingEnabled: initialDrawingEnabled,
77
+ autoSave: false,
78
+ drawingMode: 'click',
79
+ style: this.config.drawingStyle ?? {
80
+ fill: '#1e90ff',
81
+ fillOpacity: 0.25,
82
+ stroke: '#1e90ff',
83
+ strokeWidth: 2,
84
+ },
85
+ formatter: (annotation) => {
86
+ // Check if this is a 'point' annotation
87
+ if (annotation.id && annotation.id.startsWith('point-')) {
88
+ return 'point-annotation';
89
+ }
90
+ return ''; // No class for normal annotations
91
+ },
92
+ };
93
+ const anno = createOSDAnnotator(viewer, config);
94
+ this.annotorious = anno;
95
+ if (this.config.user) {
96
+ anno.setUser(this.config.user);
97
+ }
98
+ // Set initial tool
99
+ if (this.activeTool !== 'point') {
100
+ anno.setDrawingTool(this.activeTool);
101
+ }
102
+ // Inject styles
103
+ this.injectStyles(viewer);
104
+ this.setupEvents();
105
+ // Apply pending state
106
+ this.updateDrawingMode(this.isDrawingEnabled);
107
+ this.annotorious.setVisible(true); // Always start visible
108
+ }
109
+ catch (error) {
110
+ console.error('[AnnotationManager] Failed to create annotator:', error);
111
+ }
112
+ }
113
+ // ... (injectStyles remains same) ...
114
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
115
+ handlePointClick(event) {
116
+ if (!this.osdViewer || !this.annotorious)
117
+ return;
118
+ // Helper to get the TiledImage (first item) for accurate conversion
119
+ const tiledImage = this.osdViewer.world.getItemAt(0);
120
+ if (!tiledImage)
121
+ return;
122
+ // 1. Resolve Click Position
123
+ // event.position is in "Web Coordinates" (Pixels relative to viewer)
124
+ // We MUST convert to Viewport coordinates first.
125
+ const viewportPoint = this.osdViewer.viewport.pointFromPixel(event.position);
126
+ // Then convert Viewport -> Image
127
+ const imagePoint = tiledImage.viewportToImageCoordinates(viewportPoint);
128
+ // Calculate width/height based on "Screen Pixels" to ensure visibility
129
+ // User requested a 2x2 pixel rectangle.
130
+ const targetScreenPixels = 2;
131
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
132
+ const OSD = OpenSeadragon;
133
+ const p1 = this.osdViewer.viewport.pointFromPixel(new OSD.Point(0, 0));
134
+ const p2 = this.osdViewer.viewport.pointFromPixel(new OSD.Point(targetScreenPixels, 0));
135
+ const imgP1 = tiledImage.viewportToImageCoordinates(p1);
136
+ const imgP2 = tiledImage.viewportToImageCoordinates(p2);
137
+ const imageDist = Math.abs(imgP2.x - imgP1.x);
138
+ // Ensure at least 1 image unit, but otherwise strictly follow the screen-pixel math
139
+ const size = Math.max(Math.round(imageDist), 1);
140
+ const width = size;
141
+ const height = size;
142
+ // Center the rectangle on the click
143
+ const x = Math.round(imagePoint.x - width / 2);
144
+ const y = Math.round(imagePoint.y - height / 2);
145
+ console.log('[PointTool] Sizing Debug:', {
146
+ p1,
147
+ p2,
148
+ imageDist,
149
+ size,
150
+ width,
151
+ height,
152
+ });
153
+ console.log('[PointTool] Creating Rect:', {
154
+ clickViewport: viewportPoint,
155
+ clickImage: imagePoint,
156
+ finalRect: { x, y, width, height },
157
+ imageBounds: this.osdViewer.world.getItemAt(0)?.getBounds(),
158
+ });
159
+ const id = `point-${crypto.randomUUID()}`;
160
+ const annotation = {
161
+ '@context': 'http://www.w3.org/ns/anno.jsonld',
162
+ id: id,
163
+ type: 'Annotation',
164
+ body: [],
165
+ target: {
166
+ source: this.currentCanvasId ?? 'unknown',
167
+ selector: {
168
+ type: 'FragmentSelector',
169
+ conformsTo: 'http://www.w3.org/TR/media-frags/',
170
+ value: `xywh=${x},${y},${width},${height}`,
171
+ },
172
+ },
173
+ };
174
+ // 5. Add to Annotorious
175
+ this.annotorious.addAnnotation(annotation);
176
+ // Wait for render cycle before selecting coverage
177
+ setTimeout(() => {
178
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
179
+ this.annotorious.setSelected(id);
180
+ }, 50);
181
+ }
182
+ // ... (injectStyles/styles/etc)
183
+ // === Public API ===
184
+ setEditing(enabled) {
185
+ this.isDrawingEnabled = enabled; // Keep track of "Drawing" state
186
+ if (this.annotorious) {
187
+ this.updateDrawingMode(enabled);
188
+ // Always keep visible so that "Drawing Mode: Off" acts as "Edit/Select Mode"
189
+ this.annotorious.setVisible(true);
190
+ }
191
+ }
192
+ updateDrawingMode(enabled) {
193
+ if (!this.annotorious || !this.osdViewer?.element)
194
+ return;
195
+ // If tool is 'point', we do NOT enable Annotorious native drawing
196
+ // because we handle it manually.
197
+ if (this.activeTool === 'point') {
198
+ this.annotorious.setDrawingEnabled(false);
199
+ }
200
+ else {
201
+ this.annotorious.setDrawingEnabled(enabled);
202
+ }
203
+ // Toggle class for CSS cursor control
204
+ if (enabled) {
205
+ this.osdViewer.element.classList.add('annotorious-drawing-mode');
206
+ }
207
+ else {
208
+ this.osdViewer.element.classList.remove('annotorious-drawing-mode');
209
+ }
210
+ // NOTE: We do NOT disable mouse nav (panning) here anymore.
211
+ // User workflow is "Click-Move-Click", so Dragging should Pan.
212
+ // We also do NOT isolate events, so that MouseDown/Up bubble to OSD for Panning.
213
+ }
214
+ setTool(tool) {
215
+ this.activeTool = tool;
216
+ // Re-evaluate drawing mode to enable/disable native drawing
217
+ this.updateDrawingMode(this.isDrawingEnabled);
218
+ if (tool !== 'point') {
219
+ this.annotorious?.setDrawingTool(tool);
220
+ }
221
+ }
222
+ // ... (rest is same)
223
+ get availableTools() {
224
+ return this.config.tools ?? ['rectangle', 'polygon', 'point'];
225
+ }
226
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
227
+ injectStyles(viewer) {
228
+ if (!viewer.element)
229
+ return;
230
+ const root = viewer.element.getRootNode();
231
+ const styleId = 'annotorious-fixes';
232
+ // Annotorious default CSS (minified) to ensure it works in Shadow DOM
233
+ const annotoriousCss = 'canvas.a9s-gl-canvas{height:100%;left:0;position:absolute;top:0;width:100%}canvas.a9s-gl-canvas.hidden{display:none}canvas.a9s-gl-canvas.hover{cursor:pointer!important}svg.svelte-g4ws1v.svelte-g4ws1v{pointer-events:none}svg.drawing.svelte-g4ws1v.svelte-g4ws1v,svg.editing.svelte-g4ws1v .svelte-g4ws1v{pointer-events:all}svg.hover.svelte-g4ws1v.svelte-g4ws1v{cursor:pointer}svg.svelte-g4ws1v .svelte-g4ws1v{pointer-events:all}text.svelte-1rehw2p{fill:#fff;font-family:Arial,Helvetica,sans-serif;font-weight:600}rect.svelte-1rehw2p{stroke-width:1.2;vector-effect:non-scaling-stroke}polygon.svelte-fgq4n0{stroke-width:1.2;vector-effect:non-scaling-stroke}rect.svelte-gze948{stroke-width:1.2;vector-effect:non-scaling-stroke}svg.svelte-1krwc4m{position:absolute;top:0;left:0;width:100%;height:100%;outline:none;pointer-events:none}svg.svelte-jwrce3{overflow:visible;position:absolute;top:0;left:0;width:100%;height:100%;outline:none;pointer-events:none}.a9s-osd-selectionlayer :is(rect,path,polygon,ellipse,line){fill:#3182ed40;stroke:#3182ed;stroke-width:1.5px;vector-effect:non-scaling-stroke}rect.a9s-union-fg.svelte-jwrce3{fill:#3182ed1f;stroke-width:1px}rect.a9s-union-bg.svelte-jwrce3{fill:transparent;stroke:#fff;stroke-width:2px}circle.a9s-handle-buffer.svelte-qtyc7s:focus{outline:none}circle.a9s-handle-buffer.svelte-qtyc7s:focus-visible{stroke:#fffc;stroke-width:3px}.a9s-polygon-midpoint.svelte-12ykj76{cursor:crosshair}.a9s-polygon-midpoint-buffer.svelte-12ykj76{fill:transparent}.a9s-polygon-midpoint-outer.svelte-12ykj76{display:none;fill:transparent;pointer-events:none;stroke:#00000059;stroke-width:1.5px;vector-effect:non-scaling-stroke}.a9s-polygon-midpoint-inner.svelte-12ykj76{fill:#00000040;pointer-events:none;stroke:#fff;stroke-width:1px;vector-effect:non-scaling-stroke}mask.a9s-polygon-editor-mask.svelte-1h2slbm>rect.svelte-1h2slbm{fill:#fff}mask.a9s-polygon-editor-mask.svelte-1h2slbm>circle.svelte-1h2slbm,mask.a9s-polygon-editor-mask.svelte-1h2slbm>polygon.svelte-1h2slbm{fill:#000}mask.a9s-rectangle-editor-mask.svelte-1njczvj>rect.rect-mask-bg.svelte-1njczvj{fill:#fff}mask.a9s-rectangle-editor-mask.svelte-1njczvj>rect.rect-mask-fg.svelte-1njczvj{fill:#000}mask.a9s-multipolygon-editor-mask.svelte-1vxo6dc>rect.svelte-1vxo6dc{fill:#fff}mask.a9s-multipolygon-editor-mask.svelte-1vxo6dc>circle.svelte-1vxo6dc,mask.a9s-multipolygon-editor-mask.svelte-1vxo6dc>path.svelte-1vxo6dc{fill:#000}mask.a9s-rubberband-rectangle-mask.svelte-1a76qe7>rect.rect-mask-bg.svelte-1a76qe7{fill:#fff}mask.a9s-rubberband-rectangle-mask.svelte-1a76qe7>rect.rect-mask-fg.svelte-1a76qe7{fill:#000}mask.a9s-rubberband-polygon-mask.svelte-18wrg3t>rect.svelte-18wrg3t{fill:#fff}mask.a9s-rubberband-polygon-mask.svelte-18wrg3t>polygon.svelte-18wrg3t{fill:#000}circle.a9s-handle.svelte-18wrg3t.svelte-18wrg3t{fill:#fff;pointer-events:none;stroke:#00000059;stroke-width:1px;vector-effect:non-scaling-stroke}path.open.svelte-1w0132l{fill:transparent!important}.a9s-annotationlayer{box-sizing:border-box;height:100%;left:0;outline:none;position:absolute;top:0;touch-action:none;width:100%;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.a9s-annotationlayer.hover{cursor:pointer}.a9s-annotationlayer.hidden{display:none}.a9s-annotationlayer ellipse,.a9s-annotationlayer line,.a9s-annotationlayer path,.a9s-annotationlayer polygon,.a9s-annotationlayer rect{fill:transparent;shape-rendering:geometricPrecision;vector-effect:non-scaling-stroke;-webkit-tap-highlight-color:transparent}.a9s-touch-halo{fill:transparent;pointer-events:none;stroke-width:0;transition:fill .15s}.a9s-touch-halo.touched{fill:#fff6}.a9s-handle-buffer{fill:transparent}.a9s-handle [role=button]{cursor:inherit!important}.a9s-handle-dot{fill:#fff;pointer-events:none;stroke:#00000059;stroke-width:1px;vector-effect:non-scaling-stroke}.a9s-handle-dot.selected{fill:#1a1a1a;stroke:none}.a9s-handle-selected{animation:dash-rotate .35s linear infinite reverse;fill:#ffffff40;stroke:#000000e6;stroke-dasharray:2 2;stroke-width:1px;pointer-events:none;vector-effect:non-scaling-stroke}@keyframes dash-rotate{0%{stroke-dashoffset:0}to{stroke-dashoffset:4}}.a9s-edge-handle{fill:transparent;stroke:transparent;stroke-width:6px}.a9s-shape-handle,.a9s-handle{cursor:move}.a9s-handle.a9s-corner-handle{cursor:crosshair}.a9s-edge-handle-top{cursor:n-resize}.a9s-edge-handle-right{cursor:e-resize}.a9s-edge-handle-bottom{cursor:s-resize}.a9s-edge-handle-left{cursor:w-resize}.a9s-handle.a9s-corner-handle-topleft{cursor:nw-resize}.a9s-handle.a9s-corner-handle-topright{cursor:ne-resize}.a9s-handle.a9s-corner-handle-bottomright{cursor:se-resize}.a9s-handle.a9s-corner-handle-bottomleft{cursor:sw-resize}.a9s-annotationlayer .a9s-outer,div[data-theme=dark] .a9s-annotationlayer .a9s-outer{display:none}.a9s-annotationlayer .a9s-inner,div[data-theme=dark] .a9s-annotationlayer .a9s-inner{fill:#0000001f;stroke:#000;stroke-width:1px}rect.a9s-handle,div[data-theme=dark] rect.a9s-handle{fill:#000;rx:2px}rect.a9s-close-polygon-handle,div[data-theme=dark] rect.a9s-close-polygon-handle{fill:#000;rx:1px}.a9s-annotationlayer .a9s-outer,div[data-theme=light] .a9s-annotationlayer .a9s-outer{display:block;stroke:#00000059;stroke-width:3px}.a9s-annotationlayer .a9s-inner,div[data-theme=light] .a9s-annotationlayer .a9s-inner{fill:#ffffff26;stroke:#fff;stroke-width:1.5px}rect.a9s-handle,div[data-theme=light] rect.a9s-handle{fill:#fff;rx:1px;stroke:#00000073;stroke-width:1px}rect.a9s-close-polygon-handle,div[data-theme=light] rect.a9s-close-polygon-handle{fill:#fff;rx:1px;stroke:#00000073;stroke-width:1px}';
234
+ // Define the critical CSS fixes
235
+ const cssContent = `
236
+ ${annotoriousCss}
237
+
238
+ .a9s-annotationlayer, .a9s-osd-drawinglayer {
239
+ width: 100% !important;
240
+ height: 100% !important;
241
+ position: absolute;
242
+ top: 0;
243
+ left: 0;
244
+ pointer-events: none; /* Let clicks pass through to children */
245
+ }
246
+ .a9s-annotationlayer svg, .a9s-osd-drawinglayer svg {
247
+ pointer-events: auto; /* Default: Only capture clicks on shapes */
248
+ width: 100%;
249
+ height: 100%;
250
+ }
251
+ /* When in drawing mode, ensure crosshair but DO NOT BLOCK events */
252
+ /* We want events to fall through to OSD for Panning */
253
+ .annotorious-drawing-mode .a9s-annotationlayer svg,
254
+ .annotorious-drawing-mode .a9s-osd-drawinglayer svg {
255
+ pointer-events: none !important;
256
+ cursor: crosshair;
257
+ }
258
+
259
+ /* Ensure custom styling for unselected annotations */
260
+ .a9s-annotationlayer :is(rect, polygon, path, ellipse, line) {
261
+ stroke: #1e90ff;
262
+ stroke-width: 2px;
263
+ fill: transparent;
264
+ vector-effect: non-scaling-stroke;
265
+ }
266
+
267
+ /* Ensure circles support events (usually stripped by default CSS) */
268
+ .a9s-annotationlayer circle {
269
+ stroke: #1e90ff;
270
+ stroke-width: 2px;
271
+ vector-effect: non-scaling-stroke;
272
+ pointer-events: visiblePainted;
273
+ }
274
+
275
+ /* HIDE resize handles for point annotations using robust container class */
276
+ .point-selected .a9s-handle,
277
+ .point-selected .a9s-edge-handle {
278
+ display: none !important;
279
+ pointer-events: none !important;
280
+ }
281
+ .a9s-osd-selectionlayer circle {
282
+ fill: transparent;
283
+ stroke: #3182ed;
284
+ stroke-width: 1.5px;
285
+ vector-effect: non-scaling-stroke;
286
+ pointer-events: none;
287
+ }
288
+ `;
289
+ if (root instanceof ShadowRoot) {
290
+ if (!root.getElementById(styleId)) {
291
+ const style = document.createElement('style');
292
+ style.id = styleId;
293
+ style.textContent = cssContent;
294
+ root.appendChild(style);
295
+ }
296
+ }
297
+ else {
298
+ // Document context
299
+ if (!document.getElementById(styleId)) {
300
+ const style = document.createElement('style');
301
+ style.id = styleId;
302
+ style.textContent = cssContent;
303
+ document.head.appendChild(style);
304
+ }
305
+ }
306
+ }
307
+ setupEvents() {
308
+ if (!this.annotorious)
309
+ return;
310
+ this.annotorious.on('createAnnotation', async (annotation) => {
311
+ this.onAnnotationCreated?.(annotation);
312
+ await this.saveAnnotation(annotation);
313
+ this.updateUndoRedoState();
314
+ });
315
+ this.annotorious.on('updateAnnotation', async (updated) => {
316
+ await this.saveAnnotation(updated);
317
+ this.updateUndoRedoState();
318
+ });
319
+ this.annotorious.on('selectionChanged', (annotations) => {
320
+ const selected = annotations.length > 0 ? annotations[0] : null;
321
+ // Robustly toggle a class on the viewer container
322
+ if (selected && selected.id && selected.id.startsWith('point-')) {
323
+ this.osdViewer.element.classList.add('point-selected');
324
+ }
325
+ else {
326
+ this.osdViewer.element.classList.remove('point-selected');
327
+ }
328
+ this.onSelectionChange?.(selected);
329
+ });
330
+ }
331
+ updateUndoRedoState() {
332
+ const canUndo = this.annotorious?.canUndo() ?? false;
333
+ const canRedo = this.annotorious?.canRedo() ?? false;
334
+ this.onUndoRedoChange?.(canUndo, canRedo);
335
+ }
336
+ async handleCanvasChange(manifestId, canvasId) {
337
+ if (manifestId === this.currentManifestId &&
338
+ canvasId === this.currentCanvasId) {
339
+ return;
340
+ }
341
+ this.currentManifestId = manifestId;
342
+ this.currentCanvasId = canvasId;
343
+ this.onSelectionChange?.(null);
344
+ await this.loadAnnotations();
345
+ }
346
+ async loadAnnotations() {
347
+ if (!this.annotorious ||
348
+ !this.currentManifestId ||
349
+ !this.currentCanvasId) {
350
+ return;
351
+ }
352
+ try {
353
+ this.annotorious.clearAnnotations();
354
+ const annotations = await this.adapter.load(this.currentManifestId, this.currentCanvasId);
355
+ if (annotations.length > 0) {
356
+ this.annotorious.setAnnotations(annotations, true);
357
+ }
358
+ this.updateUndoRedoState();
359
+ }
360
+ catch (error) {
361
+ console.error('[AnnotationEditor] Failed to load annotations:', error);
362
+ }
363
+ }
364
+ async saveAnnotation(annotation) {
365
+ if (!this.currentManifestId || !this.currentCanvasId)
366
+ return;
367
+ const w3cAnnotation = this.ensureTargetSource(annotation);
368
+ try {
369
+ const existing = await this.adapter.load(this.currentManifestId, this.currentCanvasId);
370
+ const exists = existing.some((a) => a.id === w3cAnnotation.id);
371
+ if (exists) {
372
+ await this.adapter.update(this.currentManifestId, this.currentCanvasId, w3cAnnotation);
373
+ }
374
+ else {
375
+ await this.adapter.create(this.currentManifestId, this.currentCanvasId, w3cAnnotation);
376
+ }
377
+ }
378
+ catch (error) {
379
+ console.error('[AnnotationEditor] Failed to save annotation:', error);
380
+ }
381
+ }
382
+ ensureTargetSource(annotation) {
383
+ const clone = JSON.parse(JSON.stringify(annotation));
384
+ if (clone.target && !clone.target.source) {
385
+ clone.target.source = this.currentCanvasId;
386
+ }
387
+ else if (typeof clone.target === 'string') {
388
+ clone.target = { source: this.currentCanvasId };
389
+ }
390
+ return clone;
391
+ }
392
+ async deleteAnnotation(annotationId) {
393
+ if (!this.currentManifestId || !this.currentCanvasId)
394
+ return;
395
+ try {
396
+ await this.adapter.delete(this.currentManifestId, this.currentCanvasId, annotationId);
397
+ this.annotorious?.removeAnnotation(annotationId);
398
+ this.updateUndoRedoState();
399
+ }
400
+ catch (error) {
401
+ console.error('[AnnotationEditor] Failed to delete annotation:', error);
402
+ }
403
+ }
404
+ async updateAnnotationBodies(annotationId, bodies) {
405
+ const annotations = this.annotorious?.getAnnotations() ?? [];
406
+ const annotation = annotations.find((a) => a.id === annotationId);
407
+ if (annotation) {
408
+ const updated = {
409
+ ...annotation,
410
+ body: bodies.length === 1 ? bodies[0] : bodies,
411
+ };
412
+ await this.saveAnnotation(updated);
413
+ // Cast to any to avoid type conflicts with Annotorious internals
414
+ this.annotorious?.updateAnnotation(updated);
415
+ }
416
+ }
417
+ cancelSelection() {
418
+ this.annotorious?.cancelSelected();
419
+ }
420
+ undo() {
421
+ this.annotorious?.undo();
422
+ this.updateUndoRedoState();
423
+ }
424
+ redo() {
425
+ this.annotorious?.redo();
426
+ this.updateUndoRedoState();
427
+ }
428
+ destroy() {
429
+ this.annotorious?.destroy();
430
+ this.annotorious = null;
431
+ this.adapter.destroy?.();
432
+ }
433
+ }
@@ -0,0 +1,20 @@
1
+ import type { W3CAnnotation, AnnotationStorageAdapter } from './types';
2
+ /**
3
+ * LocalStorage-based annotation adapter.
4
+ * Stores annotations in localStorage and injects them into manifestsState for display.
5
+ */
6
+ export declare class LocalStorageAdapter implements AnnotationStorageAdapter {
7
+ readonly id = "localStorage";
8
+ readonly name = "Local Storage";
9
+ private injectedCanvases;
10
+ private storageKey;
11
+ private canvasKey;
12
+ load(manifestId: string, canvasId: string): Promise<W3CAnnotation[]>;
13
+ create(manifestId: string, canvasId: string, annotation: W3CAnnotation): Promise<void>;
14
+ update(manifestId: string, canvasId: string, annotation: W3CAnnotation): Promise<void>;
15
+ delete(manifestId: string, canvasId: string, annotationId: string): Promise<void>;
16
+ destroy(): void;
17
+ private injectIntoManifestsState;
18
+ private loadFromStorage;
19
+ private saveToStorage;
20
+ }
@@ -0,0 +1,67 @@
1
+ import { manifestsState } from '../../../state/manifests.svelte';
2
+ /**
3
+ * LocalStorage-based annotation adapter.
4
+ * Stores annotations in localStorage and injects them into manifestsState for display.
5
+ */
6
+ export class LocalStorageAdapter {
7
+ id = 'localStorage';
8
+ name = 'Local Storage';
9
+ injectedCanvases = new Set();
10
+ storageKey(manifestId, canvasId) {
11
+ return `triiiceratops:annotations:${encodeURIComponent(manifestId)}:${encodeURIComponent(canvasId)}`;
12
+ }
13
+ canvasKey(manifestId, canvasId) {
14
+ return `${manifestId}::${canvasId}`;
15
+ }
16
+ async load(manifestId, canvasId) {
17
+ const key = this.storageKey(manifestId, canvasId);
18
+ const data = localStorage.getItem(key);
19
+ const annotations = data ? JSON.parse(data) : [];
20
+ // Inject into manifestsState for display
21
+ this.injectIntoManifestsState(manifestId, canvasId, annotations);
22
+ return annotations;
23
+ }
24
+ async create(manifestId, canvasId, annotation) {
25
+ const annotations = await this.loadFromStorage(manifestId, canvasId);
26
+ annotations.push(annotation);
27
+ this.saveToStorage(manifestId, canvasId, annotations);
28
+ this.injectIntoManifestsState(manifestId, canvasId, annotations);
29
+ }
30
+ async update(manifestId, canvasId, annotation) {
31
+ const annotations = await this.loadFromStorage(manifestId, canvasId);
32
+ const index = annotations.findIndex((a) => a.id === annotation.id);
33
+ if (index >= 0) {
34
+ annotations[index] = annotation;
35
+ this.saveToStorage(manifestId, canvasId, annotations);
36
+ this.injectIntoManifestsState(manifestId, canvasId, annotations);
37
+ }
38
+ }
39
+ async delete(manifestId, canvasId, annotationId) {
40
+ const annotations = await this.loadFromStorage(manifestId, canvasId);
41
+ const filtered = annotations.filter((a) => a.id !== annotationId);
42
+ this.saveToStorage(manifestId, canvasId, filtered);
43
+ this.injectIntoManifestsState(manifestId, canvasId, filtered);
44
+ }
45
+ destroy() {
46
+ // Clear injected annotations from manifestsState
47
+ for (const canvasKey of this.injectedCanvases) {
48
+ const [manifestId, canvasId] = canvasKey.split('::');
49
+ manifestsState.clearUserAnnotations(manifestId, canvasId);
50
+ }
51
+ this.injectedCanvases.clear();
52
+ }
53
+ injectIntoManifestsState(manifestId, canvasId, annotations) {
54
+ const key = this.canvasKey(manifestId, canvasId);
55
+ this.injectedCanvases.add(key);
56
+ manifestsState.setUserAnnotations(manifestId, canvasId, annotations);
57
+ }
58
+ async loadFromStorage(manifestId, canvasId) {
59
+ const key = this.storageKey(manifestId, canvasId);
60
+ const data = localStorage.getItem(key);
61
+ return data ? JSON.parse(data) : [];
62
+ }
63
+ saveToStorage(manifestId, canvasId, annotations) {
64
+ const key = this.storageKey(manifestId, canvasId);
65
+ localStorage.setItem(key, JSON.stringify(annotations));
66
+ }
67
+ }
@@ -0,0 +1,2 @@
1
+ export type { W3CAnnotation, AnnotationStorageAdapter } from './types';
2
+ export { LocalStorageAdapter } from './LocalStorageAdapter';
@@ -0,0 +1 @@
1
+ export { LocalStorageAdapter } from './LocalStorageAdapter';
@@ -0,0 +1,23 @@
1
+ import type { W3CAnnotationBody, AnnotationStorageAdapter } from '../types';
2
+ /** W3C Web Annotation structure (simplified for our use case) */
3
+ export interface W3CAnnotation {
4
+ '@context'?: string;
5
+ id: string;
6
+ type: 'Annotation';
7
+ body?: W3CAnnotationBody | W3CAnnotationBody[];
8
+ target: {
9
+ source: string;
10
+ selector?: {
11
+ type: string;
12
+ value?: string;
13
+ conformsTo?: string;
14
+ };
15
+ };
16
+ creator?: {
17
+ id?: string;
18
+ name?: string;
19
+ };
20
+ created?: string;
21
+ modified?: string;
22
+ }
23
+ export type { AnnotationStorageAdapter };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ /**
2
+ * IIFE entry point for the Annotation Editor plugin.
3
+ * This file is bundled separately for script-tag usage.
4
+ *
5
+ * Usage:
6
+ * <script src="triiiceratops-element.iife.js"></script>
7
+ * <script src="triiiceratops-plugin-annotation-editor.iife.js"></script>
8
+ * <script>
9
+ * const viewer = document.querySelector('triiiceratops-viewer');
10
+ * viewer.plugins = [window.TriiiceratopsPlugins.AnnotationEditor];
11
+ * </script>
12
+ */
13
+ import type { PluginDef } from '../../types/plugin';
14
+ declare const AnnotationEditorPlugin: PluginDef;
15
+ export { AnnotationEditorPlugin };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * IIFE entry point for the Annotation Editor plugin.
3
+ * This file is bundled separately for script-tag usage.
4
+ *
5
+ * Usage:
6
+ * <script src="triiiceratops-element.iife.js"></script>
7
+ * <script src="triiiceratops-plugin-annotation-editor.iife.js"></script>
8
+ * <script>
9
+ * const viewer = document.querySelector('triiiceratops-viewer');
10
+ * viewer.plugins = [window.TriiiceratopsPlugins.AnnotationEditor];
11
+ * </script>
12
+ */
13
+ import AnnotationEditorController from './AnnotationEditorController.svelte';
14
+ import PencilSimple from 'phosphor-svelte/lib/PencilSimple';
15
+ import { LocalStorageAdapter } from './adapters/LocalStorageAdapter';
16
+ // Pre-configured plugin for IIFE usage
17
+ const AnnotationEditorPlugin = {
18
+ id: 'annotation-editor',
19
+ name: 'annotation_editor_title',
20
+ icon: PencilSimple,
21
+ panel: AnnotationEditorController,
22
+ position: 'left',
23
+ props: {
24
+ config: {
25
+ adapter: new LocalStorageAdapter(),
26
+ tools: ['rectangle', 'polygon'],
27
+ defaultTool: 'rectangle',
28
+ },
29
+ },
30
+ };
31
+ // Ensure the namespace exists (should already be created by the element script)
32
+ window.TriiiceratopsPlugins = window.TriiiceratopsPlugins || {};
33
+ // Register the plugin
34
+ window.TriiiceratopsPlugins.AnnotationEditor = AnnotationEditorPlugin;
35
+ export { AnnotationEditorPlugin };
@@ -0,0 +1,41 @@
1
+ import type { PluginDef } from '../../types/plugin';
2
+ import AnnotationEditorController from './AnnotationEditorController.svelte';
3
+ import type { AnnotationEditorConfig } from './types';
4
+ /**
5
+ * Create an Annotation Editor plugin with custom configuration.
6
+ *
7
+ * @example
8
+ * ```svelte
9
+ * <script>
10
+ * import { createAnnotationEditorPlugin, LocalStorageAdapter } from 'triiiceratops/plugins/annotation-editor';
11
+ *
12
+ * const annotationPlugin = createAnnotationEditorPlugin({
13
+ * adapter: new LocalStorageAdapter(),
14
+ * user: { id: 'user-123', name: 'Jane Doe' },
15
+ * });
16
+ * </script>
17
+ *
18
+ * <TriiiceratopsViewer plugins={[annotationPlugin]} />
19
+ * ```
20
+ */
21
+ export declare function createAnnotationEditorPlugin(config?: AnnotationEditorConfig): PluginDef;
22
+ /**
23
+ * Pre-configured Annotation Editor plugin with localStorage adapter.
24
+ * For advanced configuration, use createAnnotationEditorPlugin().
25
+ *
26
+ * @example
27
+ * ```svelte
28
+ * <script>
29
+ * import { AnnotationEditorPlugin } from 'triiiceratops/plugins/annotation-editor';
30
+ * </script>
31
+ *
32
+ * <TriiiceratopsViewer plugins={[AnnotationEditorPlugin]} />
33
+ * ```
34
+ */
35
+ export declare const AnnotationEditorPlugin: PluginDef;
36
+ export { AnnotationEditorController };
37
+ export { default as AnnotationEditorIcon } from 'phosphor-svelte/lib/PencilSimple';
38
+ export type { AnnotationEditorConfig, DrawingTool, W3CAnnotationBody, W3CPurpose, AnnotationStorageAdapter, } from './types';
39
+ export { W3C_PURPOSES } from './types';
40
+ export type { W3CAnnotation } from './adapters/types';
41
+ export { LocalStorageAdapter } from './adapters/LocalStorageAdapter';