triiiceratops 0.9.13 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/dist/ArrowCounterClockwise-C6n_F9YZ.js +136 -0
  2. package/dist/X-BUzsFa3u.js +886 -0
  3. package/dist/annotation_tool_point-CCJi2I8J.js +290 -0
  4. package/dist/components/AnnotationOverlay.svelte +179 -86
  5. package/dist/components/LeftFab.svelte +21 -9
  6. package/dist/components/OSDViewer.svelte +20 -3
  7. package/dist/components/ThumbnailGallery.svelte +4 -4
  8. package/dist/components/TriiiceratopsViewer.svelte +167 -49
  9. package/dist/components/TriiiceratopsViewer.svelte.d.ts +5 -5
  10. package/dist/components/TriiiceratopsViewerElement.svelte +2 -2
  11. package/dist/components/TriiiceratopsViewerElement.svelte.d.ts +2 -2
  12. package/dist/custom-element.d.ts +10 -0
  13. package/dist/custom-element.js +13 -0
  14. package/dist/image_filters_reset-CWe7vTJU.js +108 -0
  15. package/dist/index.d.ts +1 -2
  16. package/dist/index.js +0 -1
  17. package/dist/paraglide/messages/_index.d.ts +30 -0
  18. package/dist/paraglide/messages/_index.js +31 -1
  19. package/dist/paraglide/messages/annotation_editor_add_content.d.ts +4 -0
  20. package/dist/paraglide/messages/annotation_editor_add_content.js +34 -0
  21. package/dist/paraglide/messages/annotation_editor_cancel.d.ts +4 -0
  22. package/dist/paraglide/messages/annotation_editor_cancel.js +34 -0
  23. package/dist/paraglide/messages/annotation_editor_create_mode.d.ts +4 -0
  24. package/dist/paraglide/messages/annotation_editor_create_mode.js +34 -0
  25. package/dist/paraglide/messages/annotation_editor_delete.d.ts +4 -0
  26. package/dist/paraglide/messages/annotation_editor_delete.js +34 -0
  27. package/dist/paraglide/messages/annotation_editor_delete_message.d.ts +4 -0
  28. package/dist/paraglide/messages/annotation_editor_delete_message.js +34 -0
  29. package/dist/paraglide/messages/annotation_editor_delete_title.d.ts +4 -0
  30. package/dist/paraglide/messages/annotation_editor_delete_title.js +34 -0
  31. package/dist/paraglide/messages/annotation_editor_delete_tooltip.d.ts +4 -0
  32. package/dist/paraglide/messages/annotation_editor_delete_tooltip.js +34 -0
  33. package/dist/paraglide/messages/annotation_editor_edit_mode.d.ts +4 -0
  34. package/dist/paraglide/messages/annotation_editor_edit_mode.js +34 -0
  35. package/dist/paraglide/messages/annotation_editor_edit_section.d.ts +4 -0
  36. package/dist/paraglide/messages/annotation_editor_edit_section.js +34 -0
  37. package/dist/paraglide/messages/annotation_editor_instruction_create.d.ts +4 -0
  38. package/dist/paraglide/messages/annotation_editor_instruction_create.js +34 -0
  39. package/dist/paraglide/messages/annotation_editor_instruction_edit.d.ts +4 -0
  40. package/dist/paraglide/messages/annotation_editor_instruction_edit.js +34 -0
  41. package/dist/paraglide/messages/annotation_editor_link_placeholder.d.ts +4 -0
  42. package/dist/paraglide/messages/annotation_editor_link_placeholder.js +34 -0
  43. package/dist/paraglide/messages/annotation_editor_redo.d.ts +4 -0
  44. package/dist/paraglide/messages/annotation_editor_redo.js +34 -0
  45. package/dist/paraglide/messages/annotation_editor_save.d.ts +4 -0
  46. package/dist/paraglide/messages/annotation_editor_save.js +34 -0
  47. package/dist/paraglide/messages/annotation_editor_tag_placeholder.d.ts +4 -0
  48. package/dist/paraglide/messages/annotation_editor_tag_placeholder.js +34 -0
  49. package/dist/paraglide/messages/annotation_editor_text_placeholder.d.ts +4 -0
  50. package/dist/paraglide/messages/annotation_editor_text_placeholder.js +34 -0
  51. package/dist/paraglide/messages/annotation_editor_title.d.ts +4 -0
  52. package/dist/paraglide/messages/annotation_editor_title.js +34 -0
  53. package/dist/paraglide/messages/annotation_editor_tool_label.d.ts +4 -0
  54. package/dist/paraglide/messages/annotation_editor_tool_label.js +34 -0
  55. package/dist/paraglide/messages/annotation_editor_undo.d.ts +4 -0
  56. package/dist/paraglide/messages/annotation_editor_undo.js +34 -0
  57. package/dist/paraglide/messages/annotation_tool_point.d.ts +4 -0
  58. package/dist/paraglide/messages/annotation_tool_point.js +34 -0
  59. package/dist/paraglide/messages/annotation_tool_polygon.d.ts +4 -0
  60. package/dist/paraglide/messages/annotation_tool_polygon.js +34 -0
  61. package/dist/paraglide/messages/annotation_tool_rectangle.d.ts +4 -0
  62. package/dist/paraglide/messages/annotation_tool_rectangle.js +34 -0
  63. package/dist/paraglide/messages/image_adjustments_title.d.ts +4 -0
  64. package/dist/paraglide/messages/image_adjustments_title.js +34 -0
  65. package/dist/paraglide/messages/image_filters_brightness.d.ts +4 -0
  66. package/dist/paraglide/messages/image_filters_brightness.js +34 -0
  67. package/dist/paraglide/messages/image_filters_contrast.d.ts +4 -0
  68. package/dist/paraglide/messages/image_filters_contrast.js +34 -0
  69. package/dist/paraglide/messages/image_filters_effects.d.ts +4 -0
  70. package/dist/paraglide/messages/image_filters_effects.js +34 -0
  71. package/dist/paraglide/messages/image_filters_grayscale.d.ts +4 -0
  72. package/dist/paraglide/messages/image_filters_grayscale.js +34 -0
  73. package/dist/paraglide/messages/image_filters_invert.d.ts +4 -0
  74. package/dist/paraglide/messages/image_filters_invert.js +34 -0
  75. package/dist/paraglide/messages/image_filters_reset.d.ts +4 -0
  76. package/dist/paraglide/messages/image_filters_reset.js +34 -0
  77. package/dist/paraglide/messages/image_filters_saturation.d.ts +4 -0
  78. package/dist/paraglide/messages/image_filters_saturation.js +34 -0
  79. package/dist/paraglide/messages/plugins_tooltip.js +1 -1
  80. package/dist/plugins/annotation-editor/AnnotationEditorController.svelte +166 -0
  81. package/dist/plugins/annotation-editor/AnnotationEditorController.svelte.d.ts +9 -0
  82. package/dist/plugins/annotation-editor/AnnotationEditorPanel.svelte +315 -0
  83. package/dist/plugins/annotation-editor/AnnotationEditorPanel.svelte.d.ts +24 -0
  84. package/dist/plugins/annotation-editor/AnnotationManager.svelte.d.ts +39 -0
  85. package/dist/plugins/annotation-editor/AnnotationManager.svelte.js +433 -0
  86. package/dist/plugins/annotation-editor/adapters/LocalStorageAdapter.d.ts +20 -0
  87. package/dist/plugins/annotation-editor/adapters/LocalStorageAdapter.js +67 -0
  88. package/dist/plugins/annotation-editor/adapters/index.d.ts +2 -0
  89. package/dist/plugins/annotation-editor/adapters/index.js +1 -0
  90. package/dist/plugins/annotation-editor/adapters/types.d.ts +23 -0
  91. package/dist/plugins/annotation-editor/adapters/types.js +1 -0
  92. package/dist/plugins/annotation-editor/iife-entry.d.ts +15 -0
  93. package/dist/plugins/annotation-editor/iife-entry.js +35 -0
  94. package/dist/plugins/annotation-editor/index.d.ts +41 -0
  95. package/dist/plugins/annotation-editor/index.js +57 -0
  96. package/dist/plugins/annotation-editor/loader.svelte.d.ts +7 -0
  97. package/dist/plugins/annotation-editor/loader.svelte.js +32 -0
  98. package/dist/plugins/annotation-editor/types.d.ts +41 -0
  99. package/dist/plugins/annotation-editor/types.js +13 -0
  100. package/dist/plugins/annotation-editor.js +32824 -0
  101. package/dist/plugins/image-manipulation/ImageManipulationController.svelte +54 -0
  102. package/dist/plugins/image-manipulation/ImageManipulationController.svelte.d.ts +6 -0
  103. package/dist/plugins/image-manipulation/ImageManipulationPanel.svelte +19 -9
  104. package/dist/plugins/image-manipulation/ImageManipulationPanel.svelte.d.ts +1 -0
  105. package/dist/plugins/image-manipulation/iife-entry.d.ts +13 -0
  106. package/dist/plugins/image-manipulation/iife-entry.js +17 -0
  107. package/dist/plugins/image-manipulation/index.d.ts +15 -1
  108. package/dist/plugins/image-manipulation/index.js +21 -1
  109. package/dist/plugins/image-manipulation.js +265 -0
  110. package/dist/state/i18n.svelte.js +4 -2
  111. package/dist/state/manifests.svelte.d.ts +7 -2
  112. package/dist/state/manifests.svelte.js +45 -13
  113. package/dist/state/viewer.svelte.d.ts +16 -13
  114. package/dist/state/viewer.svelte.js +75 -84
  115. package/dist/triiiceratops-bundle.js +3260 -3155
  116. package/dist/triiiceratops-element.css +1 -0
  117. package/dist/triiiceratops-element.iife.js +99 -0
  118. package/dist/triiiceratops.css +1 -1
  119. package/dist/types/plugin.d.ts +21 -62
  120. package/dist/types/plugin.js +1 -23
  121. package/dist/utils/annotationAdapter.d.ts +12 -1
  122. package/dist/utils/annotationAdapter.js +98 -39
  123. package/package.json +13 -6
  124. package/dist/chunks/TriiiceratopsViewer-EViTQO_n.js +0 -10437
  125. package/dist/chunks/openseadragon-CHvATAD9.js +0 -12427
  126. package/dist/components/TriiiceratopsViewerElementImage.svelte +0 -143
  127. package/dist/components/TriiiceratopsViewerElementImage.svelte.d.ts +0 -27
  128. package/dist/custom-element-image.d.ts +0 -1
  129. package/dist/custom-element-image.js +0 -2
  130. package/dist/plugins/image-manipulation/ImageManipulationPlugin.svelte.d.ts +0 -19
  131. package/dist/plugins/image-manipulation/ImageManipulationPlugin.svelte.js +0 -87
  132. package/dist/triiiceratops-element-image.js +0 -555
  133. package/dist/triiiceratops-element.js +0 -114
@@ -11,7 +11,7 @@
11
11
  $props();
12
12
 
13
13
  let container: HTMLElement | undefined = $state();
14
- let viewer: any | undefined = $state();
14
+ let viewer: any | undefined = $state.raw();
15
15
  let OSD: any | undefined = $state();
16
16
 
17
17
  // Track OSD state changes for reactivity
@@ -61,6 +61,12 @@
61
61
 
62
62
  const results: any[] = [];
63
63
 
64
+ // Check if annotation editor is open to prevent double rendering
65
+ const editorPanel = viewerState.pluginPanels.find(
66
+ (p) => p.id === 'annotation-editor:panel',
67
+ );
68
+ const isEditorOpen = editorPanel ? editorPanel.isVisible() : false;
69
+
64
70
  for (const anno of parsedAnnotations) {
65
71
  // Filter based on visibility
66
72
  if (anno.isSearchHit) {
@@ -96,7 +102,9 @@
96
102
  height: pixelRect.height,
97
103
  },
98
104
  isSearchHit: anno.isSearchHit,
99
- tooltip: anno.body.value,
105
+ tooltip: anno.body.map((b) => b.value).join(' '),
106
+ // Disable pointer events if editor is open so blue annotations can be selected
107
+ pointerEvents: isEditorOpen ? 'none' : 'auto',
100
108
  });
101
109
  } else if (anno.geometry.type === 'POLYGON') {
102
110
  // Convert each point from image to viewport to pixel
@@ -140,7 +148,8 @@
140
148
  },
141
149
  points: relativePoints,
142
150
  isSearchHit: anno.isSearchHit,
143
- tooltip: anno.body.value,
151
+ tooltip: anno.body.map((b) => b.value).join(' '),
152
+ pointerEvents: isEditorOpen ? 'none' : 'auto',
144
153
  });
145
154
  }
146
155
  }
@@ -170,6 +179,10 @@
170
179
  animationTime: 0.5,
171
180
  springStiffness: 7.0,
172
181
  zoomPerClick: 2.0,
182
+ // Disable click-to-zoom to allow Annotorious click drawing
183
+ gestureSettingsMouse: {
184
+ clickToZoom: false,
185
+ },
173
186
  });
174
187
 
175
188
  // Notify plugins that OSD is ready
@@ -228,6 +241,7 @@
228
241
  {#each renderedAnnotations as anno (anno.id)}
229
242
  {#if anno.type === 'RECTANGLE'}
230
243
  <div
244
+ id="annotation-visual-{anno.id}"
231
245
  class="absolute border-2 transition-colors cursor-pointer pointer-events-auto {anno.isSearchHit
232
246
  ? 'border-yellow-400 bg-yellow-400/40 hover:bg-yellow-400/60'
233
247
  : 'border-red-500 bg-red-500/20 hover:bg-red-500/40'}"
@@ -236,6 +250,7 @@
236
250
  top: {anno.rect.y}px;
237
251
  width: {anno.rect.width}px;
238
252
  height: {anno.rect.height}px;
253
+ pointer-events: {anno.pointerEvents};
239
254
  "
240
255
  title={anno.tooltip}
241
256
  ></div>
@@ -247,10 +262,12 @@
247
262
  top: {anno.bounds.y}px;
248
263
  width: {anno.bounds.width}px;
249
264
  height: {anno.bounds.height}px;
265
+ pointer-events: {anno.pointerEvents};
250
266
  "
251
267
  >
252
268
  <title>{anno.tooltip}</title>
253
269
  <polygon
270
+ id="annotation-visual-{anno.id}"
254
271
  points={anno.points.map((p: any) => p.join(',')).join(' ')}
255
272
  class="cursor-pointer transition-colors {anno.isSearchHit
256
273
  ? 'fill-yellow-400/40 stroke-yellow-400 hover:fill-yellow-400/60'
@@ -483,7 +483,7 @@
483
483
 
484
484
  <!-- Content (Grid or Horizontal Scroll) -->
485
485
  <div
486
- class="flex-1 p-1 bg-base-100 {isHorizontal
486
+ class="flex-1 px-1 bg-base-100 {isHorizontal
487
487
  ? 'overflow-x-auto overflow-y-hidden h-full'
488
488
  : 'overflow-y-auto overflow-x-hidden'}"
489
489
  >
@@ -493,12 +493,12 @@
493
493
  : 'grid gap-2'}
494
494
  style={isHorizontal
495
495
  ? ''
496
- : 'grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));'}
496
+ : 'grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));'}
497
497
  >
498
498
  {#each thumbnails as thumb}
499
499
  <button
500
500
  class="group flex flex-col gap-1 p-1 rounded hover:bg-base-200 transition-colors text-left relative shrink-0 {isHorizontal
501
- ? 'w-[140px]'
501
+ ? 'w-[90px]'
502
502
  : ''} {viewerState.canvasId === thumb.id
503
503
  ? 'ring-2 ring-primary bg-primary/5'
504
504
  : ''}"
@@ -506,7 +506,7 @@
506
506
  aria-label="Select canvas {thumb.label}"
507
507
  >
508
508
  <div
509
- class="aspect-4/3 bg-base-300 rounded overflow-hidden relative w-full flex items-center justify-center"
509
+ class="aspect-3/4 bg-base-300 rounded overflow-hidden relative w-full flex items-center justify-center"
510
510
  >
511
511
  {#if thumb.src}
512
512
  <img
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
- import { setContext, onDestroy } from 'svelte';
2
+ import { setContext, onDestroy, untrack } from 'svelte';
3
3
  import { ViewerState, VIEWER_STATE_KEY } from '../state/viewer.svelte';
4
- import type { TriiiceratopsPlugin } from '../types/plugin';
4
+ import type { PluginDef } from '../types/plugin';
5
5
  import type { DaisyUITheme, ThemeConfig } from '../theme/types';
6
6
  import type { ViewerConfig } from '../types/config';
7
7
  import { applyTheme } from '../theme/themeManager';
@@ -13,23 +13,15 @@
13
13
  import LeftFab from './LeftFab.svelte';
14
14
  import MetadataDialog from './MetadataDialog.svelte';
15
15
  import SearchPanel from './SearchPanel.svelte';
16
- import { m } from '../state/i18n.svelte';
16
+ import { m, language } from '../state/i18n.svelte';
17
17
 
18
18
  // SSR-safe browser detection for library consumers
19
19
  const browser = typeof window !== 'undefined';
20
20
 
21
- let {
22
- manifestId,
23
- canvasId,
24
- plugins = [],
25
- theme,
26
- themeConfig,
27
- config = {},
28
- viewerState = $bindable(),
29
- }: {
21
+ interface Props {
30
22
  manifestId?: string;
31
23
  canvasId?: string;
32
- plugins?: TriiiceratopsPlugin[];
24
+ plugins?: PluginDef[];
33
25
  /** Built-in DaisyUI theme name. Defaults to 'light' or 'dark' based on prefers-color-scheme. */
34
26
  theme?: DaisyUITheme;
35
27
  /** Custom theme configuration to override the base theme's values. */
@@ -38,7 +30,17 @@
38
30
  config?: ViewerConfig;
39
31
  /** Bindable viewer state instance for external access (Svelte consumers) */
40
32
  viewerState?: ViewerState;
41
- } = $props();
33
+ }
34
+
35
+ let {
36
+ manifestId,
37
+ canvasId,
38
+ plugins = [],
39
+ theme,
40
+ themeConfig,
41
+ config = {},
42
+ viewerState = $bindable(),
43
+ }: Props = $props();
42
44
 
43
45
  // Reference to root element for applying theme
44
46
  let rootElement: HTMLElement | undefined = $state();
@@ -101,11 +103,45 @@
101
103
  }
102
104
  });
103
105
 
104
- // Register plugins reactively
106
+ // Register plugins reactively with cleanup
107
+ let registeredPluginIds: string[] = [];
108
+
105
109
  $effect(() => {
106
- for (const plugin of plugins) {
107
- internalViewerState.registerPlugin(plugin);
108
- }
110
+ // Create dependency on plugins prop by accessing it
111
+ const currentPlugins = plugins;
112
+
113
+ // Use untrack so that operations inside (like registerPlugin accessing/writing state)
114
+ // do NOT become dependencies of this effect. This prevents infinite loops.
115
+ untrack(() => {
116
+ // Cleanup previous plugins first
117
+ for (const id of registeredPluginIds) {
118
+ internalViewerState.unregisterPlugin(id);
119
+ }
120
+ registeredPluginIds = [];
121
+
122
+ // Register new plugins
123
+ for (const plugin of currentPlugins) {
124
+ const id =
125
+ plugin.id ||
126
+ `plugin-${Math.random().toString(36).substr(2, 9)}`;
127
+ // Create a copy with the ID to ensure stability for THIS registration
128
+ const defWithId = { ...plugin, id };
129
+ internalViewerState.registerPlugin(defWithId);
130
+ registeredPluginIds.push(id);
131
+ }
132
+ });
133
+
134
+ // Cleanup on effect re-run
135
+ return () => {
136
+ for (const id of registeredPluginIds) {
137
+ internalViewerState.unregisterPlugin(id);
138
+ }
139
+ registeredPluginIds = [];
140
+ };
141
+ });
142
+
143
+ onDestroy(() => {
144
+ internalViewerState.destroyAllPlugins();
109
145
  });
110
146
 
111
147
  $effect(() => {
@@ -140,10 +176,45 @@
140
176
  ),
141
177
  );
142
178
 
143
- let manifestData = $derived(internalViewerState.manifest);
179
+ let manifestData = $derived(internalViewerState.manifestEntry);
144
180
  let canvases = $derived(internalViewerState.canvases);
145
181
  let currentCanvasIndex = $derived(internalViewerState.currentCanvasIndex);
146
182
 
183
+ // Effect to trigger deferred search once manifest is loaded
184
+ $effect(() => {
185
+ if (
186
+ internalViewerState.pendingSearchQuery &&
187
+ manifestData &&
188
+ !manifestData.isFetching &&
189
+ !manifestData.error &&
190
+ manifestData.manifesto
191
+ ) {
192
+ const query = internalViewerState.pendingSearchQuery;
193
+ internalViewerState.pendingSearchQuery = null;
194
+ console.log(
195
+ '[Viewer] Manifest loaded, triggering deferred search:',
196
+ query,
197
+ );
198
+ internalViewerState.search(query);
199
+ }
200
+ });
201
+
202
+ // Auto-select first canvas if none selected
203
+ $effect(() => {
204
+ if (
205
+ canvases &&
206
+ canvases.length > 0 &&
207
+ !internalViewerState.canvasId &&
208
+ !manifestData?.isFetching
209
+ ) {
210
+ console.log(
211
+ '[Viewer] Auto-selecting first canvas:',
212
+ canvases[0].id,
213
+ );
214
+ internalViewerState.setCanvas(canvases[0].id);
215
+ }
216
+ });
217
+
147
218
  let tileSources = $derived.by(() => {
148
219
  if (
149
220
  !canvases ||
@@ -187,13 +258,19 @@
187
258
  else if (body) resource = body;
188
259
  }
189
260
 
190
- // Check if resource is valid (Manifesto sometimes returns empty objects for v3 bodies)
191
- if (
261
+ // Check if resource is valid (Manifesto sometimes returns empty wrapper objects for v3 bodies)
262
+ // The wrapper may have getServices() that returns manifest-level services, so we can't rely on that.
263
+ // Instead, check if the resource has actual content (id, __jsonld, or a service property)
264
+ const resourceJson = resource?.__jsonld || resource;
265
+ const hasContent =
192
266
  resource &&
193
- !resource.id &&
194
- !resource.__jsonld &&
195
- (!resource.getServices || resource.getServices().length === 0)
196
- ) {
267
+ (resource.id ||
268
+ resource['@id'] ||
269
+ (resourceJson &&
270
+ (resourceJson.service ||
271
+ resourceJson.id ||
272
+ resourceJson['@id'])));
273
+ if (resource && !hasContent) {
197
274
  resource = null;
198
275
  }
199
276
 
@@ -214,28 +291,27 @@
214
291
  const getId = (thing: any) => thing.id || thing['@id'];
215
292
 
216
293
  // Start of service detection logic
217
- let services = [];
218
- if (resource.getServices) {
219
- services = resource.getServices();
294
+ // Check raw json service FIRST - Manifesto's getServices() often returns
295
+ // manifest-level services instead of the image body's services
296
+ let services: any[] = [];
297
+ const rJson = resource.__jsonld || resource;
298
+ if (rJson.service) {
299
+ services = Array.isArray(rJson.service)
300
+ ? rJson.service
301
+ : [rJson.service];
220
302
  }
221
303
 
222
- // Fallback: check raw json service
223
- if (!services.length) {
224
- const rJson = resource.__jsonld || resource;
225
- // console.log('Checking raw resource for services:', rJson);
226
- if (rJson.service) {
227
- services = Array.isArray(rJson.service)
228
- ? rJson.service
229
- : [rJson.service];
230
- }
304
+ // Fallback to getServices() only if raw json didn't have services
305
+ if (!services.length && resource.getServices) {
306
+ services = resource.getServices();
231
307
  }
232
308
 
233
- // console.log('Found services:', services);
234
-
235
309
  if (services.length > 0) {
236
310
  // Find a valid image service
237
311
  const service = services.find((s: any) => {
238
- const type = s.getType ? s.getType() : s.type || '';
312
+ const type = s.getType
313
+ ? s.getType()
314
+ : s.type || s['@type'] || '';
239
315
  const profile = s.getProfile ? s.getProfile() : s.profile || '';
240
316
  return (
241
317
  type === 'ImageService1' ||
@@ -299,7 +375,7 @@
299
375
  >
300
376
  <!-- Gallery (when docked left) -->
301
377
  {#if internalViewerState.showThumbnailGallery && internalViewerState.dockSide === 'left'}
302
- <div class="h-full w-[200px] pointer-events-auto relative">
378
+ <div class="h-full w-[140px] pointer-events-auto relative">
303
379
  <ThumbnailGallery {canvases} />
304
380
  </div>
305
381
  {/if}
@@ -307,7 +383,10 @@
307
383
  {#each internalViewerState.pluginPanels as panel (panel.id)}
308
384
  {#if panel.isVisible() && panel.position === 'left'}
309
385
  <div class="h-full relative pointer-events-auto">
310
- <panel.component {...panel.props ?? {}} />
386
+ <panel.component
387
+ {...panel.props ?? {}}
388
+ locale={language.current}
389
+ />
311
390
  </div>
312
391
  {/if}
313
392
  {/each}
@@ -322,7 +401,7 @@
322
401
  <!-- Top Area (Gallery) -->
323
402
  {#if internalViewerState.showThumbnailGallery && internalViewerState.dockSide === 'top'}
324
403
  <div
325
- class="flex-none h-[140px] w-full pointer-events-auto relative z-20"
404
+ class="flex-none h-[160px] w-full pointer-events-auto relative z-20"
326
405
  >
327
406
  <ThumbnailGallery {canvases} />
328
407
  </div>
@@ -355,7 +434,7 @@
355
434
  viewerState={internalViewerState}
356
435
  />
357
436
  {/key}
358
- {:else}
437
+ {:else if manifestData && !manifestData.isFetching && !tileSources}
359
438
  <div
360
439
  class="w-full h-full flex items-center justify-center text-base-content/50"
361
440
  >
@@ -378,7 +457,10 @@
378
457
  {#each internalViewerState.pluginPanels as panel (panel.id)}
379
458
  {#if panel.isVisible() && panel.position === 'overlay'}
380
459
  <div class="absolute inset-0 z-40 pointer-events-none">
381
- <panel.component {...panel.props ?? {}} />
460
+ <panel.component
461
+ {...panel.props ?? {}}
462
+ locale={language.current}
463
+ />
382
464
  </div>
383
465
  {/if}
384
466
  {/each}
@@ -397,7 +479,7 @@
397
479
  <!-- Bottom Area (Gallery) -->
398
480
  {#if internalViewerState.showThumbnailGallery && internalViewerState.dockSide === 'bottom'}
399
481
  <div
400
- class="flex-none h-[140px] w-full pointer-events-auto relative z-20"
482
+ class="flex-none h-[160px] w-full pointer-events-auto relative z-20"
401
483
  >
402
484
  <ThumbnailGallery {canvases} />
403
485
  </div>
@@ -407,7 +489,10 @@
407
489
  {#each internalViewerState.pluginPanels as panel (panel.id)}
408
490
  {#if panel.isVisible() && panel.position === 'bottom'}
409
491
  <div class="relative w-full z-40 pointer-events-auto">
410
- <panel.component {...panel.props ?? {}} />
492
+ <panel.component
493
+ {...panel.props ?? {}}
494
+ locale={language.current}
495
+ />
411
496
  </div>
412
497
  {/if}
413
498
  {/each}
@@ -430,7 +515,7 @@
430
515
 
431
516
  <!-- Gallery (when docked right) -->
432
517
  {#if internalViewerState.showThumbnailGallery && internalViewerState.dockSide === 'right'}
433
- <div class="h-full w-[200px] pointer-events-auto relative">
518
+ <div class="h-full w-[140px] pointer-events-auto relative">
434
519
  <ThumbnailGallery {canvases} />
435
520
  </div>
436
521
  {/if}
@@ -439,10 +524,43 @@
439
524
  {#each internalViewerState.pluginPanels as panel (panel.id)}
440
525
  {#if panel.isVisible() && panel.position === 'right'}
441
526
  <div class="h-full relative pointer-events-auto">
442
- <panel.component {...panel.props ?? {}} />
527
+ <panel.component
528
+ {...panel.props ?? {}}
529
+ locale={language.current}
530
+ />
443
531
  </div>
444
532
  {/if}
445
533
  {/each}
446
534
  </div>
447
535
  {/if}
448
536
  </div>
537
+
538
+ <style>
539
+ /* Scoped scrollbar styles for the viewer */
540
+ :global(#triiiceratops-viewer *) {
541
+ scrollbar-width: thin;
542
+ scrollbar-color: var(--fallback-bc, oklch(var(--bc) / 0.2)) transparent;
543
+ }
544
+
545
+ :global(#triiiceratops-viewer ::-webkit-scrollbar) {
546
+ width: 4px;
547
+ height: 4px;
548
+ }
549
+
550
+ :global(#triiiceratops-viewer ::-webkit-scrollbar-track) {
551
+ background: transparent;
552
+ }
553
+
554
+ :global(#triiiceratops-viewer ::-webkit-scrollbar-thumb) {
555
+ background-color: var(--fallback-bc, oklch(var(--bc) / 0.2));
556
+ border-radius: 9999px;
557
+ }
558
+
559
+ :global(#triiiceratops-viewer ::-webkit-scrollbar-thumb:hover) {
560
+ background-color: var(--fallback-bc, oklch(var(--bc) / 0.4));
561
+ }
562
+
563
+ :global(#triiiceratops-viewer ::-webkit-scrollbar-corner) {
564
+ background: transparent;
565
+ }
566
+ </style>
@@ -1,11 +1,11 @@
1
1
  import { ViewerState } from '../state/viewer.svelte';
2
- import type { TriiiceratopsPlugin } from '../types/plugin';
2
+ import type { PluginDef } from '../types/plugin';
3
3
  import type { DaisyUITheme, ThemeConfig } from '../theme/types';
4
4
  import type { ViewerConfig } from '../types/config';
5
- type $$ComponentProps = {
5
+ interface Props {
6
6
  manifestId?: string;
7
7
  canvasId?: string;
8
- plugins?: TriiiceratopsPlugin[];
8
+ plugins?: PluginDef[];
9
9
  /** Built-in DaisyUI theme name. Defaults to 'light' or 'dark' based on prefers-color-scheme. */
10
10
  theme?: DaisyUITheme;
11
11
  /** Custom theme configuration to override the base theme's values. */
@@ -14,7 +14,7 @@ type $$ComponentProps = {
14
14
  config?: ViewerConfig;
15
15
  /** Bindable viewer state instance for external access (Svelte consumers) */
16
16
  viewerState?: ViewerState;
17
- };
18
- declare const TriiiceratopsViewer: import("svelte").Component<$$ComponentProps, {}, "viewerState">;
17
+ }
18
+ declare const TriiiceratopsViewer: import("svelte").Component<Props, {}, "viewerState">;
19
19
  type TriiiceratopsViewer = ReturnType<typeof TriiiceratopsViewer>;
20
20
  export default TriiiceratopsViewer;
@@ -35,7 +35,7 @@
35
35
  <script lang="ts">
36
36
  import styles from '../../app.css?inline';
37
37
  import TriiiceratopsViewer from './TriiiceratopsViewer.svelte';
38
- import type { TriiiceratopsPlugin } from '../types/plugin';
38
+ import type { PluginDef } from '../types/plugin';
39
39
  import type { DaisyUITheme, ThemeConfig } from '../theme/types';
40
40
  import type { ViewerConfig } from '../types/config';
41
41
  import { isBuiltInTheme, parseThemeConfig } from '../theme/themeManager';
@@ -51,7 +51,7 @@
51
51
  }: {
52
52
  manifestId?: string;
53
53
  canvasId?: string;
54
- plugins?: TriiiceratopsPlugin[];
54
+ plugins?: PluginDef[];
55
55
  /**
56
56
  * Built-in DaisyUI theme name (e.g., 'light', 'dark', 'cupcake').
57
57
  * When not specified, inherits the theme from the parent context.
@@ -1,10 +1,10 @@
1
- import type { TriiiceratopsPlugin } from '../types/plugin';
1
+ import type { PluginDef } from '../types/plugin';
2
2
  import type { ThemeConfig } from '../theme/types';
3
3
  import type { ViewerConfig } from '../types/config';
4
4
  type $$ComponentProps = {
5
5
  manifestId?: string;
6
6
  canvasId?: string;
7
- plugins?: TriiiceratopsPlugin[];
7
+ plugins?: PluginDef[];
8
8
  /**
9
9
  * Built-in DaisyUI theme name (e.g., 'light', 'dark', 'cupcake').
10
10
  * When not specified, inherits the theme from the parent context.
@@ -1 +1,11 @@
1
+ import * as svelte from 'svelte';
2
+ declare global {
3
+ interface Window {
4
+ __TriiiceratopsSvelteRuntime: {
5
+ svelte: typeof svelte;
6
+ internal: unknown;
7
+ };
8
+ TriiiceratopsPlugins?: Record<string, unknown>;
9
+ }
10
+ }
1
11
  import './components/TriiiceratopsViewerElement.svelte';
@@ -1,3 +1,16 @@
1
1
  // Entry point for custom element build
2
2
  // Importing this module registers the <triiiceratops-viewer> custom element
3
+ // Expose Svelte internals for IIFE plugin builds
4
+ // Plugins built as IIFE need to share the same Svelte runtime instance
5
+ // so that getContext/setContext work correctly across bundle boundaries
6
+ // @ts-expect-error - svelte/internal/client is not typed but exists at runtime
7
+ import * as svelteInternal from 'svelte/internal/client';
8
+ import * as svelte from 'svelte';
9
+ window.__TriiiceratopsSvelteRuntime = {
10
+ svelte,
11
+ internal: svelteInternal,
12
+ };
13
+ // Initialize the plugins namespace
14
+ window.TriiiceratopsPlugins = window.TriiiceratopsPlugins || {};
15
+ // Register the custom element
3
16
  import './components/TriiiceratopsViewerElement.svelte';
@@ -0,0 +1,108 @@
1
+ import { a as t } from "./X-BUzsFa3u.js";
2
+ const s = (
3
+ /** @type {(inputs: {}) => LocalizedString} */
4
+ () => (
5
+ /** @type {LocalizedString} */
6
+ "Image Adjustments"
7
+ )
8
+ ), a = (
9
+ /** @type {(inputs: {}) => LocalizedString} */
10
+ () => (
11
+ /** @type {LocalizedString} */
12
+ "Bildanpassungen"
13
+ )
14
+ ), v = /* @__NO_SIDE_EFFECTS__ */ (n = {}, e = {}) => (e.locale ?? t()) === "en" ? s() : a(), l = (
15
+ /** @type {(inputs: {}) => LocalizedString} */
16
+ () => (
17
+ /** @type {LocalizedString} */
18
+ "Brightness"
19
+ )
20
+ ), i = (
21
+ /** @type {(inputs: {}) => LocalizedString} */
22
+ () => (
23
+ /** @type {LocalizedString} */
24
+ "Helligkeit"
25
+ )
26
+ ), y = /* @__NO_SIDE_EFFECTS__ */ (n = {}, e = {}) => (e.locale ?? t()) === "en" ? l() : i(), o = (
27
+ /** @type {(inputs: {}) => LocalizedString} */
28
+ () => (
29
+ /** @type {LocalizedString} */
30
+ "Contrast"
31
+ )
32
+ ), c = (
33
+ /** @type {(inputs: {}) => LocalizedString} */
34
+ () => (
35
+ /** @type {LocalizedString} */
36
+ "Kontrast"
37
+ )
38
+ ), B = /* @__NO_SIDE_EFFECTS__ */ (n = {}, e = {}) => (e.locale ?? t()) === "en" ? o() : c(), _ = (
39
+ /** @type {(inputs: {}) => LocalizedString} */
40
+ () => (
41
+ /** @type {LocalizedString} */
42
+ "Saturation"
43
+ )
44
+ ), u = (
45
+ /** @type {(inputs: {}) => LocalizedString} */
46
+ () => (
47
+ /** @type {LocalizedString} */
48
+ "Sättigung"
49
+ )
50
+ ), C = /* @__NO_SIDE_EFFECTS__ */ (n = {}, e = {}) => (e.locale ?? t()) === "en" ? _() : u(), f = (
51
+ /** @type {(inputs: {}) => LocalizedString} */
52
+ () => (
53
+ /** @type {LocalizedString} */
54
+ "Effects"
55
+ )
56
+ ), g = (
57
+ /** @type {(inputs: {}) => LocalizedString} */
58
+ () => (
59
+ /** @type {LocalizedString} */
60
+ "Effekte"
61
+ )
62
+ ), E = /* @__NO_SIDE_EFFECTS__ */ (n = {}, e = {}) => (e.locale ?? t()) === "en" ? f() : g(), m = (
63
+ /** @type {(inputs: {}) => LocalizedString} */
64
+ () => (
65
+ /** @type {LocalizedString} */
66
+ "Invert Colors"
67
+ )
68
+ ), d = (
69
+ /** @type {(inputs: {}) => LocalizedString} */
70
+ () => (
71
+ /** @type {LocalizedString} */
72
+ "Farben umkehren"
73
+ )
74
+ ), G = /* @__NO_SIDE_EFFECTS__ */ (n = {}, e = {}) => (e.locale ?? t()) === "en" ? m() : d(), p = (
75
+ /** @type {(inputs: {}) => LocalizedString} */
76
+ () => (
77
+ /** @type {LocalizedString} */
78
+ "Grayscale"
79
+ )
80
+ ), b = (
81
+ /** @type {(inputs: {}) => LocalizedString} */
82
+ () => (
83
+ /** @type {LocalizedString} */
84
+ "Graustufen"
85
+ )
86
+ ), I = /* @__NO_SIDE_EFFECTS__ */ (n = {}, e = {}) => (e.locale ?? t()) === "en" ? p() : b(), h = (
87
+ /** @type {(inputs: {}) => LocalizedString} */
88
+ () => (
89
+ /** @type {LocalizedString} */
90
+ "Reset to Default"
91
+ )
92
+ ), j = (
93
+ /** @type {(inputs: {}) => LocalizedString} */
94
+ () => (
95
+ /** @type {LocalizedString} */
96
+ "Zurücksetzen"
97
+ )
98
+ ), S = /* @__NO_SIDE_EFFECTS__ */ (n = {}, e = {}) => (e.locale ?? t()) === "en" ? h() : j();
99
+ export {
100
+ y as a,
101
+ B as b,
102
+ E as c,
103
+ I as d,
104
+ G as e,
105
+ S as f,
106
+ C as g,
107
+ v as i
108
+ };
package/dist/index.d.ts CHANGED
@@ -2,8 +2,7 @@ export { default as TriiiceratopsViewer } from './components/TriiiceratopsViewer
2
2
  export { ViewerState, VIEWER_STATE_KEY } from './state/viewer.svelte';
3
3
  export type { ViewerStateSnapshot } from './state/viewer.svelte';
4
4
  export { ManifestsState } from './state/manifests.svelte';
5
- export type { TriiiceratopsPlugin, PluginContext, PluginMenuButton, PluginPanel, } from './types/plugin';
6
- export { BasePlugin } from './types/plugin';
5
+ export type { PluginDef, PluginMenuButton, PluginPanel } from './types/plugin';
7
6
  export type { ThemeConfig, DaisyUITheme } from './theme/types';
8
7
  export { DAISYUI_THEMES } from './theme/types';
9
8
  export { applyTheme, applyBuiltInTheme, applyThemeConfig, clearThemeConfig, isBuiltInTheme, parseThemeConfig, } from './theme/themeManager';
package/dist/index.js CHANGED
@@ -3,7 +3,6 @@ export { default as TriiiceratopsViewer } from './components/TriiiceratopsViewer
3
3
  // Type exports for TypeScript users
4
4
  export { ViewerState, VIEWER_STATE_KEY } from './state/viewer.svelte';
5
5
  export { ManifestsState } from './state/manifests.svelte';
6
- export { BasePlugin } from './types/plugin';
7
6
  export { DAISYUI_THEMES } from './theme/types';
8
7
  export { applyTheme, applyBuiltInTheme, applyThemeConfig, clearThemeConfig, isBuiltInTheme, parseThemeConfig, } from './theme/themeManager';
9
8
  export { hexToOklch, normalizeColor } from './theme/colorUtils';