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.
- package/dist/ArrowCounterClockwise-C6n_F9YZ.js +136 -0
- package/dist/X-BUzsFa3u.js +886 -0
- package/dist/annotation_tool_point-CCJi2I8J.js +290 -0
- package/dist/components/AnnotationOverlay.svelte +179 -86
- package/dist/components/LeftFab.svelte +21 -9
- package/dist/components/OSDViewer.svelte +20 -3
- package/dist/components/ThumbnailGallery.svelte +4 -4
- package/dist/components/TriiiceratopsViewer.svelte +167 -49
- package/dist/components/TriiiceratopsViewer.svelte.d.ts +5 -5
- package/dist/components/TriiiceratopsViewerElement.svelte +2 -2
- package/dist/components/TriiiceratopsViewerElement.svelte.d.ts +2 -2
- package/dist/custom-element.d.ts +10 -0
- package/dist/custom-element.js +13 -0
- package/dist/image_filters_reset-CWe7vTJU.js +108 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +0 -1
- package/dist/paraglide/messages/_index.d.ts +30 -0
- package/dist/paraglide/messages/_index.js +31 -1
- package/dist/paraglide/messages/annotation_editor_add_content.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_add_content.js +34 -0
- package/dist/paraglide/messages/annotation_editor_cancel.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_cancel.js +34 -0
- package/dist/paraglide/messages/annotation_editor_create_mode.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_create_mode.js +34 -0
- package/dist/paraglide/messages/annotation_editor_delete.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_delete.js +34 -0
- package/dist/paraglide/messages/annotation_editor_delete_message.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_delete_message.js +34 -0
- package/dist/paraglide/messages/annotation_editor_delete_title.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_delete_title.js +34 -0
- package/dist/paraglide/messages/annotation_editor_delete_tooltip.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_delete_tooltip.js +34 -0
- package/dist/paraglide/messages/annotation_editor_edit_mode.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_edit_mode.js +34 -0
- package/dist/paraglide/messages/annotation_editor_edit_section.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_edit_section.js +34 -0
- package/dist/paraglide/messages/annotation_editor_instruction_create.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_instruction_create.js +34 -0
- package/dist/paraglide/messages/annotation_editor_instruction_edit.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_instruction_edit.js +34 -0
- package/dist/paraglide/messages/annotation_editor_link_placeholder.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_link_placeholder.js +34 -0
- package/dist/paraglide/messages/annotation_editor_redo.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_redo.js +34 -0
- package/dist/paraglide/messages/annotation_editor_save.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_save.js +34 -0
- package/dist/paraglide/messages/annotation_editor_tag_placeholder.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_tag_placeholder.js +34 -0
- package/dist/paraglide/messages/annotation_editor_text_placeholder.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_text_placeholder.js +34 -0
- package/dist/paraglide/messages/annotation_editor_title.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_title.js +34 -0
- package/dist/paraglide/messages/annotation_editor_tool_label.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_tool_label.js +34 -0
- package/dist/paraglide/messages/annotation_editor_undo.d.ts +4 -0
- package/dist/paraglide/messages/annotation_editor_undo.js +34 -0
- package/dist/paraglide/messages/annotation_tool_point.d.ts +4 -0
- package/dist/paraglide/messages/annotation_tool_point.js +34 -0
- package/dist/paraglide/messages/annotation_tool_polygon.d.ts +4 -0
- package/dist/paraglide/messages/annotation_tool_polygon.js +34 -0
- package/dist/paraglide/messages/annotation_tool_rectangle.d.ts +4 -0
- package/dist/paraglide/messages/annotation_tool_rectangle.js +34 -0
- package/dist/paraglide/messages/image_adjustments_title.d.ts +4 -0
- package/dist/paraglide/messages/image_adjustments_title.js +34 -0
- package/dist/paraglide/messages/image_filters_brightness.d.ts +4 -0
- package/dist/paraglide/messages/image_filters_brightness.js +34 -0
- package/dist/paraglide/messages/image_filters_contrast.d.ts +4 -0
- package/dist/paraglide/messages/image_filters_contrast.js +34 -0
- package/dist/paraglide/messages/image_filters_effects.d.ts +4 -0
- package/dist/paraglide/messages/image_filters_effects.js +34 -0
- package/dist/paraglide/messages/image_filters_grayscale.d.ts +4 -0
- package/dist/paraglide/messages/image_filters_grayscale.js +34 -0
- package/dist/paraglide/messages/image_filters_invert.d.ts +4 -0
- package/dist/paraglide/messages/image_filters_invert.js +34 -0
- package/dist/paraglide/messages/image_filters_reset.d.ts +4 -0
- package/dist/paraglide/messages/image_filters_reset.js +34 -0
- package/dist/paraglide/messages/image_filters_saturation.d.ts +4 -0
- package/dist/paraglide/messages/image_filters_saturation.js +34 -0
- package/dist/paraglide/messages/plugins_tooltip.js +1 -1
- package/dist/plugins/annotation-editor/AnnotationEditorController.svelte +166 -0
- package/dist/plugins/annotation-editor/AnnotationEditorController.svelte.d.ts +9 -0
- package/dist/plugins/annotation-editor/AnnotationEditorPanel.svelte +315 -0
- package/dist/plugins/annotation-editor/AnnotationEditorPanel.svelte.d.ts +24 -0
- package/dist/plugins/annotation-editor/AnnotationManager.svelte.d.ts +39 -0
- package/dist/plugins/annotation-editor/AnnotationManager.svelte.js +433 -0
- package/dist/plugins/annotation-editor/adapters/LocalStorageAdapter.d.ts +20 -0
- package/dist/plugins/annotation-editor/adapters/LocalStorageAdapter.js +67 -0
- package/dist/plugins/annotation-editor/adapters/index.d.ts +2 -0
- package/dist/plugins/annotation-editor/adapters/index.js +1 -0
- package/dist/plugins/annotation-editor/adapters/types.d.ts +23 -0
- package/dist/plugins/annotation-editor/adapters/types.js +1 -0
- package/dist/plugins/annotation-editor/iife-entry.d.ts +15 -0
- package/dist/plugins/annotation-editor/iife-entry.js +35 -0
- package/dist/plugins/annotation-editor/index.d.ts +41 -0
- package/dist/plugins/annotation-editor/index.js +57 -0
- package/dist/plugins/annotation-editor/loader.svelte.d.ts +7 -0
- package/dist/plugins/annotation-editor/loader.svelte.js +32 -0
- package/dist/plugins/annotation-editor/types.d.ts +41 -0
- package/dist/plugins/annotation-editor/types.js +13 -0
- package/dist/plugins/annotation-editor.js +32824 -0
- package/dist/plugins/image-manipulation/ImageManipulationController.svelte +54 -0
- package/dist/plugins/image-manipulation/ImageManipulationController.svelte.d.ts +6 -0
- package/dist/plugins/image-manipulation/ImageManipulationPanel.svelte +19 -9
- package/dist/plugins/image-manipulation/ImageManipulationPanel.svelte.d.ts +1 -0
- package/dist/plugins/image-manipulation/iife-entry.d.ts +13 -0
- package/dist/plugins/image-manipulation/iife-entry.js +17 -0
- package/dist/plugins/image-manipulation/index.d.ts +15 -1
- package/dist/plugins/image-manipulation/index.js +21 -1
- package/dist/plugins/image-manipulation.js +265 -0
- package/dist/state/i18n.svelte.js +4 -2
- package/dist/state/manifests.svelte.d.ts +7 -2
- package/dist/state/manifests.svelte.js +45 -13
- package/dist/state/viewer.svelte.d.ts +16 -13
- package/dist/state/viewer.svelte.js +75 -84
- package/dist/triiiceratops-bundle.js +3260 -3155
- package/dist/triiiceratops-element.css +1 -0
- package/dist/triiiceratops-element.iife.js +99 -0
- package/dist/triiiceratops.css +1 -1
- package/dist/types/plugin.d.ts +21 -62
- package/dist/types/plugin.js +1 -23
- package/dist/utils/annotationAdapter.d.ts +12 -1
- package/dist/utils/annotationAdapter.js +98 -39
- package/package.json +13 -6
- package/dist/chunks/TriiiceratopsViewer-EViTQO_n.js +0 -10437
- package/dist/chunks/openseadragon-CHvATAD9.js +0 -12427
- package/dist/components/TriiiceratopsViewerElementImage.svelte +0 -143
- package/dist/components/TriiiceratopsViewerElementImage.svelte.d.ts +0 -27
- package/dist/custom-element-image.d.ts +0 -1
- package/dist/custom-element-image.js +0 -2
- package/dist/plugins/image-manipulation/ImageManipulationPlugin.svelte.d.ts +0 -19
- package/dist/plugins/image-manipulation/ImageManipulationPlugin.svelte.js +0 -87
- package/dist/triiiceratops-element-image.js +0 -555
- 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 @@
|
|
|
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';
|