triiiceratops 0.8.1 → 0.9.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 (94) hide show
  1. package/dist/chunks/TriiiceratopsViewer-BGtsfUPF.js +10298 -0
  2. package/dist/chunks/openseadragon-BTypULhm.js +12427 -0
  3. package/dist/components/AnnotationOverlay.svelte +288 -0
  4. package/dist/components/AnnotationOverlay.svelte.d.ts +3 -0
  5. package/dist/components/CanvasNavigation.svelte +32 -0
  6. package/dist/components/CanvasNavigation.svelte.d.ts +11 -0
  7. package/dist/components/DemoHeader.svelte +703 -0
  8. package/dist/components/DemoHeader.svelte.d.ts +9 -0
  9. package/dist/components/FloatingMenu.svelte +208 -0
  10. package/dist/components/FloatingMenu.svelte.d.ts +3 -0
  11. package/dist/components/LeftFab.svelte +69 -0
  12. package/dist/components/LeftFab.svelte.d.ts +3 -0
  13. package/dist/components/MetadataDialog.svelte +151 -0
  14. package/dist/components/MetadataDialog.svelte.d.ts +3 -0
  15. package/dist/components/OSDViewer.svelte +260 -0
  16. package/dist/components/OSDViewer.svelte.d.ts +8 -0
  17. package/dist/components/SearchPanel.svelte +150 -0
  18. package/dist/components/SearchPanel.svelte.d.ts +3 -0
  19. package/dist/components/ThemeToggle.svelte +118 -0
  20. package/dist/components/ThemeToggle.svelte.d.ts +3 -0
  21. package/dist/components/ThumbnailGallery.svelte +601 -0
  22. package/dist/components/ThumbnailGallery.svelte.d.ts +36 -0
  23. package/dist/components/TriiiceratopsViewer.svelte +434 -0
  24. package/dist/components/TriiiceratopsViewer.svelte.d.ts +20 -0
  25. package/dist/components/TriiiceratopsViewerElement.svelte +139 -0
  26. package/dist/components/TriiiceratopsViewerElement.svelte.d.ts +27 -0
  27. package/dist/components/TriiiceratopsViewerElementImage.svelte +143 -0
  28. package/dist/components/TriiiceratopsViewerElementImage.svelte.d.ts +27 -0
  29. package/dist/custom-element-image.d.ts +1 -0
  30. package/dist/custom-element-image.js +2 -0
  31. package/dist/custom-element.d.ts +1 -0
  32. package/dist/custom-element.js +3 -0
  33. package/dist/{src/lib/index.d.ts → index.d.ts} +1 -0
  34. package/dist/index.js +10 -4480
  35. package/dist/plugins/image-manipulation/ImageManipulationPanel.svelte +134 -0
  36. package/dist/plugins/image-manipulation/ImageManipulationPanel.svelte.d.ts +10 -0
  37. package/dist/{src/lib/plugins → plugins}/image-manipulation/ImageManipulationPlugin.svelte.d.ts +2 -2
  38. package/dist/plugins/image-manipulation/ImageManipulationPlugin.svelte.js +122 -0
  39. package/dist/{src/lib/plugins → plugins}/image-manipulation/filters.d.ts +1 -1
  40. package/dist/plugins/image-manipulation/filters.js +48 -0
  41. package/dist/plugins/image-manipulation/index.js +2 -0
  42. package/dist/plugins/image-manipulation/types.js +7 -0
  43. package/dist/state/i18n.svelte.d.ts +4 -0
  44. package/dist/state/i18n.svelte.js +18 -0
  45. package/dist/state/manifests.svelte.js +210 -0
  46. package/dist/state/manifests.test.d.ts +1 -0
  47. package/dist/state/manifests.test.js +242 -0
  48. package/dist/{src/lib/state → state}/viewer.svelte.d.ts +4 -4
  49. package/dist/state/viewer.svelte.js +693 -0
  50. package/dist/theme/colorUtils.js +196 -0
  51. package/dist/theme/colorUtils.test.d.ts +1 -0
  52. package/dist/theme/colorUtils.test.js +90 -0
  53. package/dist/theme/index.js +52 -0
  54. package/dist/{src/lib/theme → theme}/themeManager.d.ts +4 -1
  55. package/dist/theme/themeManager.js +177 -0
  56. package/dist/theme/types.js +40 -0
  57. package/dist/triiiceratops-bundle.js +4676 -0
  58. package/dist/triiiceratops-element-image.js +1 -1
  59. package/dist/triiiceratops-element.js +1 -1
  60. package/dist/types/config.js +1 -0
  61. package/dist/{src/lib/types → types}/plugin.d.ts +3 -3
  62. package/dist/types/plugin.js +36 -0
  63. package/dist/utils/annotationAdapter.js +354 -0
  64. package/dist/utils/annotationAdapter.test.d.ts +1 -0
  65. package/dist/utils/annotationAdapter.test.js +91 -0
  66. package/package.json +6 -5
  67. package/dist/chunks/TriiiceratopsViewer-CyamQrMe.js +0 -22698
  68. package/dist/plugin-De14WKQl.js +0 -546
  69. package/dist/plugins/image-manipulation.js +0 -454
  70. package/dist/src/lib/components/AnnotationOverlay.svelte.d.ts +0 -1
  71. package/dist/src/lib/components/CanvasNavigation.svelte.d.ts +0 -1
  72. package/dist/src/lib/components/FloatingMenu.svelte.d.ts +0 -1
  73. package/dist/src/lib/components/LeftFab.svelte.d.ts +0 -1
  74. package/dist/src/lib/components/MetadataDialog.svelte.d.ts +0 -1
  75. package/dist/src/lib/components/OSDViewer.svelte.d.ts +0 -1
  76. package/dist/src/lib/components/SearchPanel.svelte.d.ts +0 -1
  77. package/dist/src/lib/components/ThumbnailGallery.svelte.d.ts +0 -1
  78. package/dist/src/lib/components/TriiiceratopsViewer.svelte.d.ts +0 -1
  79. package/dist/src/lib/custom-element-image.d.ts +0 -0
  80. package/dist/src/lib/custom-element.d.ts +0 -0
  81. package/dist/src/lib/paraglide/messages/de.d.ts +0 -96
  82. package/dist/src/lib/paraglide/messages/en.d.ts +0 -96
  83. package/dist/src/lib/paraglide/messages.d.ts +0 -272
  84. package/dist/src/lib/paraglide/runtime.d.ts +0 -52
  85. package/dist/src/lib/plugins/image-manipulation/ImageManipulationPanel.svelte.d.ts +0 -1
  86. package/dist/src/lib/state/i18n.svelte.d.ts +0 -5
  87. /package/dist/{src/lib/plugins → plugins}/image-manipulation/index.d.ts +0 -0
  88. /package/dist/{src/lib/plugins → plugins}/image-manipulation/types.d.ts +0 -0
  89. /package/dist/{src/lib/state → state}/manifests.svelte.d.ts +0 -0
  90. /package/dist/{src/lib/theme → theme}/colorUtils.d.ts +0 -0
  91. /package/dist/{src/lib/theme → theme}/index.d.ts +0 -0
  92. /package/dist/{src/lib/theme → theme}/types.d.ts +0 -0
  93. /package/dist/{src/lib/types → types}/config.d.ts +0 -0
  94. /package/dist/{src/lib/utils → utils}/annotationAdapter.d.ts +0 -0
@@ -0,0 +1,693 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __generator = (this && this.__generator) || function (thisArg, body) {
11
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
12
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
13
+ function verb(n) { return function (v) { return step([n, v]); }; }
14
+ function step(op) {
15
+ if (f) throw new TypeError("Generator is already executing.");
16
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
17
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
18
+ if (y = 0, t) op = [op[0] & 2, t.value];
19
+ switch (op[0]) {
20
+ case 0: case 1: t = op; break;
21
+ case 4: _.label++; return { value: op[1], done: false };
22
+ case 5: _.label++; y = op[1]; op = [0]; continue;
23
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
24
+ default:
25
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
26
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
27
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
28
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
29
+ if (t[2]) _.ops.pop();
30
+ _.trys.pop(); continue;
31
+ }
32
+ op = body.call(thisArg, _);
33
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
34
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35
+ }
36
+ };
37
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
38
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
39
+ if (ar || !(i in from)) {
40
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
41
+ ar[i] = from[i];
42
+ }
43
+ }
44
+ return to.concat(ar || Array.prototype.slice.call(from));
45
+ };
46
+ import { manifestsState } from './manifests.svelte.js';
47
+ var ViewerState = /** @class */ (function () {
48
+ function ViewerState(initialManifestId, initialCanvasId, initialPlugins) {
49
+ if (initialManifestId === void 0) { initialManifestId = null; }
50
+ if (initialCanvasId === void 0) { initialCanvasId = null; }
51
+ if (initialPlugins === void 0) { initialPlugins = []; }
52
+ this.manifestId = $state(null);
53
+ this.canvasId = $state(null);
54
+ this.showAnnotations = $state(false);
55
+ this.showThumbnailGallery = $state(false);
56
+ this.isGalleryDockedBottom = $state(false);
57
+ this.isGalleryDockedRight = $state(false);
58
+ this.isFullScreen = $state(false);
59
+ this.showMetadataDialog = $state(false);
60
+ this.dockSide = $state('bottom');
61
+ this.visibleAnnotationIds = $state(new Set());
62
+ // UI Configuration
63
+ this.config = $state({});
64
+ // Gallery State (Lifted for persistence during re-docking)
65
+ this.galleryPosition = $state({ x: 20, y: 100 });
66
+ this.gallerySize = $state({ width: 300, height: 400 });
67
+ this.isGalleryDragging = $state(false);
68
+ this.galleryDragOffset = $state({ x: 0, y: 0 });
69
+ this.dragOverSide = $state(null);
70
+ this.galleryCenterPanelRect = $state(null);
71
+ // ==================== EVENT DISPATCH (Web Component Only) ====================
72
+ /**
73
+ * Event target for dispatching CustomEvents.
74
+ * Only set by TriiiceratopsViewerElement (web component build).
75
+ * Remains null for Svelte component usage → no events dispatched.
76
+ */
77
+ this.eventTarget = null;
78
+ this.searchQuery = $state('');
79
+ this.searchResults = $state([]);
80
+ this.isSearching = $state(false);
81
+ this.showSearchPanel = $state(false);
82
+ this.searchAnnotations = $state([]);
83
+ // ==================== PLUGIN STATE ====================
84
+ /** Registered plugins */
85
+ this.plugins = $state([]);
86
+ /** Plugin-registered menu buttons */
87
+ this.pluginMenuButtons = $state([]);
88
+ /** Plugin-registered panels */
89
+ this.pluginPanels = $state([]);
90
+ /** OpenSeadragon viewer instance (set by OSDViewer) */
91
+ this.osdViewer = $state(null);
92
+ /** Event handlers for inter-plugin communication */
93
+ this.pluginEventHandlers = new Map();
94
+ this.manifestId = initialManifestId || null;
95
+ this.canvasId = initialCanvasId || null;
96
+ // Fetch manifest immediately
97
+ if (this.manifestId) {
98
+ manifestsState.fetchManifest(this.manifestId);
99
+ }
100
+ // Register initial plugins
101
+ for (var _i = 0, initialPlugins_1 = initialPlugins; _i < initialPlugins_1.length; _i++) {
102
+ var initialPlugin = initialPlugins_1[_i];
103
+ this.registerPlugin(initialPlugin);
104
+ }
105
+ }
106
+ Object.defineProperty(ViewerState.prototype, "showRightMenu", {
107
+ // Derived configuration specific getters
108
+ get: function () {
109
+ var _a;
110
+ return (_a = this.config.showRightMenu) !== null && _a !== void 0 ? _a : true;
111
+ },
112
+ enumerable: false,
113
+ configurable: true
114
+ });
115
+ Object.defineProperty(ViewerState.prototype, "showLeftMenu", {
116
+ get: function () {
117
+ var _a;
118
+ return (_a = this.config.showLeftMenu) !== null && _a !== void 0 ? _a : true;
119
+ },
120
+ enumerable: false,
121
+ configurable: true
122
+ });
123
+ Object.defineProperty(ViewerState.prototype, "showCanvasNav", {
124
+ get: function () {
125
+ var _a;
126
+ return (_a = this.config.showCanvasNav) !== null && _a !== void 0 ? _a : true;
127
+ },
128
+ enumerable: false,
129
+ configurable: true
130
+ });
131
+ /**
132
+ * Set the event target for dispatching state change events.
133
+ * Called by TriiiceratopsViewerElement to enable event-driven API.
134
+ */
135
+ ViewerState.prototype.setEventTarget = function (target) {
136
+ this.eventTarget = target;
137
+ };
138
+ /**
139
+ * Get current state as a plain object snapshot.
140
+ * Safe to use outside Svelte's reactive system.
141
+ * NOTE: We calculate currentCanvasIndex inline to avoid triggering the canvases getter
142
+ * which can cause infinite loops when it auto-sets canvasId.
143
+ */
144
+ ViewerState.prototype.getSnapshot = function () {
145
+ var _this = this;
146
+ // Calculate canvas index without triggering reactive side effects
147
+ var canvasIndex = -1;
148
+ if (this.manifestId && this.canvasId) {
149
+ var canvases = manifestsState.getCanvases(this.manifestId);
150
+ canvasIndex = canvases.findIndex(function (c) { return c.id === _this.canvasId; });
151
+ }
152
+ return {
153
+ manifestId: this.manifestId,
154
+ canvasId: this.canvasId,
155
+ currentCanvasIndex: canvasIndex,
156
+ showAnnotations: this.showAnnotations,
157
+ showThumbnailGallery: this.showThumbnailGallery,
158
+ showSearchPanel: this.showSearchPanel,
159
+ searchQuery: this.searchQuery,
160
+ isFullScreen: this.isFullScreen,
161
+ dockSide: this.dockSide,
162
+ };
163
+ };
164
+ /**
165
+ * Dispatch a state change event to the web component.
166
+ * No-op if eventTarget is null (Svelte component usage).
167
+ *
168
+ * Uses queueMicrotask to dispatch asynchronously AFTER the current
169
+ * reactive cycle completes, preventing infinite update loops.
170
+ */
171
+ ViewerState.prototype.dispatchStateChange = function (eventName) {
172
+ var _this = this;
173
+ if (eventName === void 0) { eventName = 'statechange'; }
174
+ console.log("[ViewerState] Dispatching ".concat(eventName), JSON.stringify(this.getSnapshot()));
175
+ if (!this.eventTarget)
176
+ return;
177
+ // Dispatch asynchronously to break reactive loops
178
+ queueMicrotask(function () {
179
+ var _a;
180
+ (_a = _this.eventTarget) === null || _a === void 0 ? void 0 : _a.dispatchEvent(new CustomEvent(eventName, {
181
+ detail: _this.getSnapshot(),
182
+ bubbles: true,
183
+ composed: true,
184
+ }));
185
+ });
186
+ };
187
+ Object.defineProperty(ViewerState.prototype, "manifest", {
188
+ get: function () {
189
+ if (!this.manifestId)
190
+ return null;
191
+ return manifestsState.getManifest(this.manifestId);
192
+ },
193
+ enumerable: false,
194
+ configurable: true
195
+ });
196
+ Object.defineProperty(ViewerState.prototype, "canvases", {
197
+ get: function () {
198
+ var _this = this;
199
+ if (!this.manifestId)
200
+ return [];
201
+ var canvases = manifestsState.getCanvases(this.manifestId);
202
+ // Auto-initialize canvasId to first canvas if not set
203
+ if (canvases.length > 0 && !this.canvasId) {
204
+ // Use setTimeout to avoid updating state during a derived computation
205
+ setTimeout(function () {
206
+ if (!_this.canvasId && canvases.length > 0) {
207
+ _this.canvasId = canvases[0].id;
208
+ }
209
+ }, 0);
210
+ }
211
+ return canvases;
212
+ },
213
+ enumerable: false,
214
+ configurable: true
215
+ });
216
+ Object.defineProperty(ViewerState.prototype, "currentCanvasIndex", {
217
+ get: function () {
218
+ var _this = this;
219
+ if (!this.canvasId) {
220
+ if (this.canvases.length > 0)
221
+ return 0;
222
+ return -1;
223
+ }
224
+ // Manifesto canvases have an id property
225
+ return this.canvases.findIndex(function (c) { return c.id === _this.canvasId; });
226
+ },
227
+ enumerable: false,
228
+ configurable: true
229
+ });
230
+ Object.defineProperty(ViewerState.prototype, "hasNext", {
231
+ get: function () {
232
+ return this.currentCanvasIndex < this.canvases.length - 1;
233
+ },
234
+ enumerable: false,
235
+ configurable: true
236
+ });
237
+ Object.defineProperty(ViewerState.prototype, "hasPrevious", {
238
+ get: function () {
239
+ return this.currentCanvasIndex > 0;
240
+ },
241
+ enumerable: false,
242
+ configurable: true
243
+ });
244
+ ViewerState.prototype.nextCanvas = function () {
245
+ if (this.hasNext) {
246
+ var nextIndex = this.currentCanvasIndex + 1;
247
+ var canvas = this.canvases[nextIndex];
248
+ this.setCanvas(canvas.id);
249
+ }
250
+ };
251
+ ViewerState.prototype.previousCanvas = function () {
252
+ if (this.hasPrevious) {
253
+ var prevIndex = this.currentCanvasIndex - 1;
254
+ var canvas = this.canvases[prevIndex];
255
+ this.setCanvas(canvas.id);
256
+ }
257
+ };
258
+ ViewerState.prototype.setManifest = function (manifestId) {
259
+ this.manifestId = manifestId;
260
+ this.canvasId = null;
261
+ manifestsState.fetchManifest(manifestId);
262
+ this.dispatchStateChange('manifestchange');
263
+ };
264
+ ViewerState.prototype.setCanvas = function (canvasId) {
265
+ this.canvasId = canvasId;
266
+ this.dispatchStateChange('canvaschange');
267
+ };
268
+ ViewerState.prototype.updateConfig = function (newConfig) {
269
+ var _a;
270
+ var oldConfig = this.config;
271
+ this.config = newConfig;
272
+ // Sync state from config
273
+ if (newConfig.gallery) {
274
+ if (newConfig.gallery.open !== undefined) {
275
+ this.showThumbnailGallery = newConfig.gallery.open;
276
+ }
277
+ if (newConfig.gallery.dockPosition !== undefined) {
278
+ this.dockSide = newConfig.gallery.dockPosition;
279
+ }
280
+ }
281
+ if (newConfig.search) {
282
+ if (newConfig.search.open !== undefined) {
283
+ this.showSearchPanel = newConfig.search.open;
284
+ }
285
+ // Only search if the CONFIG has changed its query requirement.
286
+ // This prevents stale config updates (e.g. from other property changes)
287
+ // from overwriting a newer internal search state.
288
+ var newQuery = newConfig.search.query;
289
+ var oldQuery = (_a = oldConfig.search) === null || _a === void 0 ? void 0 : _a.query;
290
+ if (newQuery !== undefined &&
291
+ newQuery !== oldQuery &&
292
+ newQuery !== this.searchQuery) {
293
+ this._performSearch(newQuery);
294
+ }
295
+ }
296
+ if (newConfig.annotations) {
297
+ if (newConfig.annotations.open !== undefined) {
298
+ this.showAnnotations = newConfig.annotations.open;
299
+ }
300
+ }
301
+ // NOTE: We intentionally do NOT dispatch events here.
302
+ // Config updates are external configuration, not user-initiated state changes.
303
+ // Dispatching here would cause infinite loops when the consumer re-renders.
304
+ };
305
+ ViewerState.prototype.toggleAnnotations = function () {
306
+ this.showAnnotations = !this.showAnnotations;
307
+ this.dispatchStateChange();
308
+ };
309
+ ViewerState.prototype.toggleThumbnailGallery = function () {
310
+ this.showThumbnailGallery = !this.showThumbnailGallery;
311
+ this.dispatchStateChange();
312
+ };
313
+ ViewerState.prototype.toggleFullScreen = function () {
314
+ if (!document.fullscreenElement) {
315
+ var el = document.getElementById('triiiceratops-viewer');
316
+ if (el) {
317
+ el.requestFullscreen().catch(function (e) {
318
+ console.warn('Fullscreen request failed', e);
319
+ });
320
+ }
321
+ }
322
+ else {
323
+ document.exitFullscreen();
324
+ }
325
+ };
326
+ ViewerState.prototype.toggleMetadataDialog = function () {
327
+ this.showMetadataDialog = !this.showMetadataDialog;
328
+ };
329
+ ViewerState.prototype.toggleSearchPanel = function () {
330
+ this.showSearchPanel = !this.showSearchPanel;
331
+ if (!this.showSearchPanel) {
332
+ // Clear ephemeral annotations when closing search
333
+ this.searchAnnotations = [];
334
+ }
335
+ this.dispatchStateChange();
336
+ };
337
+ Object.defineProperty(ViewerState.prototype, "currentCanvasSearchAnnotations", {
338
+ get: function () {
339
+ var _this = this;
340
+ if (!this.canvasId)
341
+ return [];
342
+ return this.searchAnnotations.filter(function (a) { return a.canvasId === _this.canvasId; });
343
+ },
344
+ enumerable: false,
345
+ configurable: true
346
+ });
347
+ ViewerState.prototype.search = function (query) {
348
+ return __awaiter(this, void 0, void 0, function () {
349
+ return __generator(this, function (_a) {
350
+ switch (_a.label) {
351
+ case 0:
352
+ this.dispatchStateChange();
353
+ return [4 /*yield*/, this._performSearch(query)];
354
+ case 1:
355
+ _a.sent();
356
+ this.dispatchStateChange();
357
+ return [2 /*return*/];
358
+ }
359
+ });
360
+ });
361
+ };
362
+ ViewerState.prototype._performSearch = function (query) {
363
+ return __awaiter(this, void 0, void 0, function () {
364
+ var manifest, service, services, serviceId, searchUrl, response, data, resources, processedResults, parseSelector, _i, _a, hit, annotations, hitBoundsByCanvas, _loop_1, this_1, _b, annotations_1, annoId, _c, hitBoundsByCanvas_1, _d, canvasIndex, data_1, _loop_2, this_2, _e, resources_1, res, annotationIndex_1, e_1;
365
+ var _this = this;
366
+ var _f, _g;
367
+ return __generator(this, function (_h) {
368
+ switch (_h.label) {
369
+ case 0:
370
+ if (!query.trim())
371
+ return [2 /*return*/];
372
+ this.isSearching = true;
373
+ this.searchQuery = query;
374
+ this.searchResults = [];
375
+ _h.label = 1;
376
+ case 1:
377
+ _h.trys.push([1, 4, 5, 6]);
378
+ manifest = this.manifest;
379
+ if (!manifest)
380
+ throw new Error('No manifest loaded');
381
+ service = manifest.getService('http://iiif.io/api/search/1/search') ||
382
+ manifest.getService('http://iiif.io/api/search/0/search');
383
+ if (!service) {
384
+ // Fallback: check json directly if manifesto fails me
385
+ if (manifest.__jsonld && manifest.__jsonld.service) {
386
+ services = Array.isArray(manifest.__jsonld.service)
387
+ ? manifest.__jsonld.service
388
+ : [manifest.__jsonld.service];
389
+ service = services.find(function (s) {
390
+ return s.profile ===
391
+ 'http://iiif.io/api/search/1/search' ||
392
+ s.profile === 'http://iiif.io/api/search/0/search';
393
+ });
394
+ }
395
+ }
396
+ if (!service) {
397
+ console.warn('No IIIF search service found in manifest');
398
+ this.isSearching = false;
399
+ return [2 /*return*/];
400
+ }
401
+ serviceId = service.id || service['@id'];
402
+ searchUrl = "".concat(serviceId, "?q=").concat(encodeURIComponent(query));
403
+ return [4 /*yield*/, fetch(searchUrl)];
404
+ case 2:
405
+ response = _h.sent();
406
+ if (!response.ok)
407
+ throw new Error('Search request failed');
408
+ return [4 /*yield*/, response.json()];
409
+ case 3:
410
+ data = _h.sent();
411
+ resources = data.resources || [];
412
+ processedResults = [];
413
+ parseSelector = function (onVal) {
414
+ var val = typeof onVal === 'string'
415
+ ? onVal
416
+ : onVal['@id'] || onVal.id;
417
+ if (!val)
418
+ return null;
419
+ var parts = val.split('#xywh=');
420
+ if (parts.length < 2)
421
+ return null;
422
+ var coords = parts[1].split(',').map(Number);
423
+ if (coords.length === 4)
424
+ return coords; // [x, y, w, h]
425
+ return null;
426
+ };
427
+ if (data.hits) {
428
+ for (_i = 0, _a = data.hits; _i < _a.length; _i++) {
429
+ hit = _a[_i];
430
+ annotations = hit.annotations || [];
431
+ hitBoundsByCanvas = new Map();
432
+ _loop_1 = function (annoId) {
433
+ var annotation = resources.find(function (r) { return r['@id'] === annoId || r.id === annoId; });
434
+ if (annotation && annotation.on) {
435
+ // annotation.on can be "canvas-id" or "canvas-id#xywh=..."
436
+ var onVal = typeof annotation.on === 'string'
437
+ ? annotation.on
438
+ : annotation.on['@id'] || annotation.on.id;
439
+ var cleanOn_1 = onVal.split('#')[0];
440
+ var bounds = parseSelector(onVal);
441
+ var canvasIndex = this_1.canvases.findIndex(function (c) { return c.id === cleanOn_1; });
442
+ if (canvasIndex >= 0) {
443
+ if (!hitBoundsByCanvas.has(canvasIndex)) {
444
+ var canvas = this_1.canvases[canvasIndex];
445
+ // Try to get a label
446
+ var label = 'Canvas ' + (canvasIndex + 1);
447
+ try {
448
+ if (canvas.getLabel) {
449
+ var l = canvas.getLabel();
450
+ if (Array.isArray(l) &&
451
+ l.length > 0)
452
+ label = l[0].value;
453
+ else if (typeof l === 'string')
454
+ label = l;
455
+ }
456
+ else if (canvas.label) {
457
+ // Fallback if raw object
458
+ if (typeof canvas.label === 'string')
459
+ label = canvas.label;
460
+ else if (Array.isArray(canvas.label))
461
+ label = (_f = canvas.label[0]) === null || _f === void 0 ? void 0 : _f.value;
462
+ }
463
+ }
464
+ catch (e) {
465
+ /* ignore */
466
+ }
467
+ hitBoundsByCanvas.set(canvasIndex, {
468
+ label: String(label),
469
+ bounds: [],
470
+ });
471
+ }
472
+ if (bounds) {
473
+ hitBoundsByCanvas
474
+ .get(canvasIndex)
475
+ .bounds.push(bounds);
476
+ }
477
+ }
478
+ }
479
+ };
480
+ this_1 = this;
481
+ for (_b = 0, annotations_1 = annotations; _b < annotations_1.length; _b++) {
482
+ annoId = annotations_1[_b];
483
+ _loop_1(annoId);
484
+ }
485
+ // Create one result per canvas for this hit
486
+ for (_c = 0, hitBoundsByCanvas_1 = hitBoundsByCanvas; _c < hitBoundsByCanvas_1.length; _c++) {
487
+ _d = hitBoundsByCanvas_1[_c], canvasIndex = _d[0], data_1 = _d[1];
488
+ processedResults.push({
489
+ type: 'hit',
490
+ before: hit.before,
491
+ match: hit.match,
492
+ after: hit.after,
493
+ canvasIndex: canvasIndex,
494
+ canvasLabel: data_1.label,
495
+ // Store all bounds for this hit on this canvas
496
+ allBounds: data_1.bounds,
497
+ // Keep first bounds for backwards compatibility
498
+ bounds: data_1.bounds.length > 0 ? data_1.bounds[0] : null,
499
+ });
500
+ }
501
+ }
502
+ }
503
+ else if (resources.length > 0) {
504
+ _loop_2 = function (res) {
505
+ var onVal = typeof res.on === 'string'
506
+ ? res.on
507
+ : res.on['@id'] || res.on.id;
508
+ var cleanOn = onVal.split('#')[0];
509
+ var bounds = parseSelector(onVal);
510
+ var canvasIndex = this_2.canvases.findIndex(function (c) { return c.id === cleanOn; });
511
+ if (canvasIndex >= 0) {
512
+ var canvas = this_2.canvases[canvasIndex];
513
+ var label = 'Canvas ' + (canvasIndex + 1);
514
+ try {
515
+ if (canvas.getLabel) {
516
+ var l = canvas.getLabel();
517
+ if (Array.isArray(l) && l.length > 0)
518
+ label = l[0].value;
519
+ else if (typeof l === 'string')
520
+ label = l;
521
+ }
522
+ else if (canvas.label) {
523
+ // Fallback if raw object
524
+ if (typeof canvas.label === 'string')
525
+ label = canvas.label;
526
+ else if (Array.isArray(canvas.label))
527
+ label = (_g = canvas.label[0]) === null || _g === void 0 ? void 0 : _g.value;
528
+ }
529
+ }
530
+ catch (e) {
531
+ /* ignore */
532
+ }
533
+ processedResults.push({
534
+ type: 'resource',
535
+ match: res.resource && res.resource.chars
536
+ ? res.resource.chars
537
+ : res.chars || '',
538
+ canvasIndex: canvasIndex,
539
+ canvasLabel: String(label),
540
+ bounds: bounds,
541
+ });
542
+ }
543
+ };
544
+ this_2 = this;
545
+ // No hits (Basic level?), just annotations
546
+ for (_e = 0, resources_1 = resources; _e < resources_1.length; _e++) {
547
+ res = resources_1[_e];
548
+ _loop_2(res);
549
+ }
550
+ }
551
+ this.searchResults = processedResults;
552
+ annotationIndex_1 = 0;
553
+ this.searchAnnotations = processedResults.flatMap(function (r) {
554
+ var canvas = _this.canvases[r.canvasIndex];
555
+ // Use allBounds if available, otherwise fall back to single bounds
556
+ var boundsArray = r.allBounds && r.allBounds.length > 0
557
+ ? r.allBounds
558
+ : r.bounds
559
+ ? [r.bounds]
560
+ : [];
561
+ return boundsArray.map(function (bounds) {
562
+ var on = "".concat(canvas.id, "#xywh=").concat(bounds.join(','));
563
+ return {
564
+ '@id': "urn:search-hit:".concat(annotationIndex_1++),
565
+ '@type': 'oa:Annotation',
566
+ motivation: 'sc:painting',
567
+ on: on,
568
+ canvasId: canvas.id,
569
+ resource: {
570
+ '@type': 'cnt:ContentAsText',
571
+ chars: r.match,
572
+ },
573
+ // Flag to identify styling in Overlay?
574
+ // Or just standard rendering.
575
+ isSearchHit: true,
576
+ };
577
+ });
578
+ });
579
+ return [3 /*break*/, 6];
580
+ case 4:
581
+ e_1 = _h.sent();
582
+ console.error('Search error:', e_1);
583
+ return [3 /*break*/, 6];
584
+ case 5:
585
+ this.isSearching = false;
586
+ return [7 /*endfinally*/];
587
+ case 6: return [2 /*return*/];
588
+ }
589
+ });
590
+ });
591
+ };
592
+ // ==================== PLUGIN METHODS ====================
593
+ /**
594
+ * Create plugin context - the stable API surface for plugins.
595
+ */
596
+ ViewerState.prototype.createPluginContext = function () {
597
+ var self = this;
598
+ return {
599
+ viewerState: self,
600
+ getOSDViewer: function () { return self.osdViewer; },
601
+ registerMenuButton: function (button) {
602
+ if (!self.pluginMenuButtons.find(function (b) { return b.id === button.id; })) {
603
+ self.pluginMenuButtons = __spreadArray(__spreadArray([], self.pluginMenuButtons, true), [
604
+ button,
605
+ ], false);
606
+ }
607
+ },
608
+ unregisterMenuButton: function (buttonId) {
609
+ self.pluginMenuButtons = self.pluginMenuButtons.filter(function (b) { return b.id !== buttonId; });
610
+ },
611
+ registerPanel: function (panel) {
612
+ if (!self.pluginPanels.find(function (p) { return p.id === panel.id; })) {
613
+ self.pluginPanels = __spreadArray(__spreadArray([], self.pluginPanels, true), [panel], false);
614
+ }
615
+ },
616
+ unregisterPanel: function (panelId) {
617
+ self.pluginPanels = self.pluginPanels.filter(function (p) { return p.id !== panelId; });
618
+ },
619
+ emit: function (eventName, data) {
620
+ var handlers = self.pluginEventHandlers.get(eventName);
621
+ handlers === null || handlers === void 0 ? void 0 : handlers.forEach(function (handler) { return handler(data); });
622
+ },
623
+ on: function (eventName, handler) {
624
+ if (!self.pluginEventHandlers.has(eventName)) {
625
+ self.pluginEventHandlers.set(eventName, new Set());
626
+ }
627
+ self.pluginEventHandlers.get(eventName).add(handler);
628
+ return function () {
629
+ var _a;
630
+ (_a = self.pluginEventHandlers.get(eventName)) === null || _a === void 0 ? void 0 : _a.delete(handler);
631
+ };
632
+ },
633
+ };
634
+ };
635
+ /**
636
+ * Register a plugin with this viewer instance.
637
+ */
638
+ ViewerState.prototype.registerPlugin = function (plugin) {
639
+ if (this.plugins.find(function (p) { return p.id === plugin.id; })) {
640
+ console.warn("[Triiiceratops] Plugin \"".concat(plugin.id, "\" already registered"));
641
+ return;
642
+ }
643
+ this.plugins = __spreadArray(__spreadArray([], this.plugins, true), [plugin], false);
644
+ plugin.onRegister(this.createPluginContext());
645
+ // If OSD already ready, notify immediately
646
+ if (this.osdViewer && plugin.onViewerReady) {
647
+ plugin.onViewerReady(this.osdViewer);
648
+ }
649
+ };
650
+ /**
651
+ * Unregister a plugin by ID.
652
+ */
653
+ ViewerState.prototype.unregisterPlugin = function (pluginId) {
654
+ var _a;
655
+ var plugin = this.plugins.find(function (p) { return p.id === pluginId; });
656
+ if (plugin) {
657
+ (_a = plugin.onDestroy) === null || _a === void 0 ? void 0 : _a.call(plugin);
658
+ this.plugins = this.plugins.filter(function (p) { return p.id !== pluginId; });
659
+ // Remove plugin's UI registrations
660
+ this.pluginMenuButtons = this.pluginMenuButtons.filter(function (b) { return !b.id.startsWith("".concat(pluginId, ":")); });
661
+ this.pluginPanels = this.pluginPanels.filter(function (p) { return !p.id.startsWith("".concat(pluginId, ":")); });
662
+ }
663
+ };
664
+ /**
665
+ * Called by OSDViewer when OpenSeadragon is ready.
666
+ */
667
+ ViewerState.prototype.notifyOSDReady = function (viewer) {
668
+ var _a;
669
+ this.osdViewer = viewer;
670
+ for (var _i = 0, _b = this.plugins; _i < _b.length; _i++) {
671
+ var plugin = _b[_i];
672
+ (_a = plugin.onViewerReady) === null || _a === void 0 ? void 0 : _a.call(plugin, viewer);
673
+ }
674
+ };
675
+ /**
676
+ * Destroy all plugins (called on viewer unmount).
677
+ */
678
+ ViewerState.prototype.destroyAllPlugins = function () {
679
+ var _a;
680
+ for (var _i = 0, _b = this.plugins; _i < _b.length; _i++) {
681
+ var plugin = _b[_i];
682
+ (_a = plugin.onDestroy) === null || _a === void 0 ? void 0 : _a.call(plugin);
683
+ }
684
+ this.plugins = [];
685
+ this.pluginMenuButtons = [];
686
+ this.pluginPanels = [];
687
+ this.pluginEventHandlers.clear();
688
+ };
689
+ return ViewerState;
690
+ }());
691
+ export { ViewerState };
692
+ // Context key for providing/injecting ViewerState in components
693
+ export var VIEWER_STATE_KEY = 'triiiceratops:viewerState';