triiiceratops 0.8.2 → 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.
- package/dist/components/AnnotationOverlay.svelte +288 -0
- package/dist/components/AnnotationOverlay.svelte.d.ts +3 -0
- package/dist/components/CanvasNavigation.svelte +32 -0
- package/dist/components/CanvasNavigation.svelte.d.ts +11 -0
- package/dist/components/DemoHeader.svelte +703 -0
- package/dist/components/DemoHeader.svelte.d.ts +9 -0
- package/dist/components/FloatingMenu.svelte +208 -0
- package/dist/components/FloatingMenu.svelte.d.ts +3 -0
- package/dist/components/LeftFab.svelte +69 -0
- package/dist/components/LeftFab.svelte.d.ts +3 -0
- package/dist/components/MetadataDialog.svelte +151 -0
- package/dist/components/MetadataDialog.svelte.d.ts +3 -0
- package/dist/components/OSDViewer.svelte +260 -0
- package/dist/components/OSDViewer.svelte.d.ts +8 -0
- package/dist/components/SearchPanel.svelte +150 -0
- package/dist/components/SearchPanel.svelte.d.ts +3 -0
- package/dist/components/ThemeToggle.svelte +118 -0
- package/dist/components/ThemeToggle.svelte.d.ts +3 -0
- package/dist/components/ThumbnailGallery.svelte +601 -0
- package/dist/components/ThumbnailGallery.svelte.d.ts +36 -0
- package/dist/components/TriiiceratopsViewer.svelte +434 -0
- package/dist/components/TriiiceratopsViewer.svelte.d.ts +20 -0
- package/dist/components/TriiiceratopsViewerElement.svelte +139 -0
- package/dist/components/TriiiceratopsViewerElement.svelte.d.ts +27 -0
- package/dist/components/TriiiceratopsViewerElementImage.svelte +143 -0
- package/dist/components/TriiiceratopsViewerElementImage.svelte.d.ts +27 -0
- package/dist/custom-element-image.d.ts +1 -0
- package/dist/custom-element-image.js +2 -0
- package/dist/custom-element.d.ts +1 -0
- package/dist/custom-element.js +3 -0
- package/dist/{src/lib/index.d.ts → index.d.ts} +1 -0
- package/dist/index.js +10 -4153
- package/dist/plugins/image-manipulation/ImageManipulationPanel.svelte +134 -0
- package/dist/plugins/image-manipulation/ImageManipulationPanel.svelte.d.ts +10 -0
- package/dist/{src/lib/plugins → plugins}/image-manipulation/ImageManipulationPlugin.svelte.d.ts +2 -2
- package/dist/plugins/image-manipulation/ImageManipulationPlugin.svelte.js +122 -0
- package/dist/{src/lib/plugins → plugins}/image-manipulation/filters.d.ts +1 -1
- package/dist/plugins/image-manipulation/filters.js +48 -0
- package/dist/plugins/image-manipulation/index.js +2 -0
- package/dist/plugins/image-manipulation/types.js +7 -0
- package/dist/state/i18n.svelte.d.ts +4 -0
- package/dist/state/i18n.svelte.js +18 -0
- package/dist/state/manifests.svelte.js +210 -0
- package/dist/state/manifests.test.d.ts +1 -0
- package/dist/state/manifests.test.js +242 -0
- package/dist/{src/lib/state → state}/viewer.svelte.d.ts +4 -4
- package/dist/state/viewer.svelte.js +693 -0
- package/dist/theme/colorUtils.js +196 -0
- package/dist/theme/colorUtils.test.d.ts +1 -0
- package/dist/theme/colorUtils.test.js +90 -0
- package/dist/theme/index.js +52 -0
- package/dist/{src/lib/theme → theme}/themeManager.d.ts +4 -1
- package/dist/theme/themeManager.js +177 -0
- package/dist/theme/types.js +40 -0
- package/dist/triiiceratops-bundle.js +4676 -0
- package/dist/types/config.js +1 -0
- package/dist/{src/lib/types → types}/plugin.d.ts +3 -3
- package/dist/types/plugin.js +36 -0
- package/dist/utils/annotationAdapter.js +354 -0
- package/dist/utils/annotationAdapter.test.d.ts +1 -0
- package/dist/utils/annotationAdapter.test.js +91 -0
- package/package.json +6 -5
- package/dist/plugin-CHYleMsW.js +0 -538
- package/dist/plugins/image-manipulation.js +0 -411
- package/dist/src/lib/components/AnnotationOverlay.svelte.d.ts +0 -1
- package/dist/src/lib/components/CanvasNavigation.svelte.d.ts +0 -1
- package/dist/src/lib/components/FloatingMenu.svelte.d.ts +0 -1
- package/dist/src/lib/components/LeftFab.svelte.d.ts +0 -1
- package/dist/src/lib/components/MetadataDialog.svelte.d.ts +0 -1
- package/dist/src/lib/components/OSDViewer.svelte.d.ts +0 -1
- package/dist/src/lib/components/SearchPanel.svelte.d.ts +0 -1
- package/dist/src/lib/components/ThumbnailGallery.svelte.d.ts +0 -1
- package/dist/src/lib/components/TriiiceratopsViewer.svelte.d.ts +0 -1
- package/dist/src/lib/custom-element-image.d.ts +0 -0
- package/dist/src/lib/custom-element.d.ts +0 -0
- package/dist/src/lib/paraglide/messages/de.d.ts +0 -96
- package/dist/src/lib/paraglide/messages/en.d.ts +0 -96
- package/dist/src/lib/paraglide/messages.d.ts +0 -272
- package/dist/src/lib/paraglide/runtime.d.ts +0 -52
- package/dist/src/lib/plugins/image-manipulation/ImageManipulationPanel.svelte.d.ts +0 -1
- package/dist/src/lib/state/i18n.svelte.d.ts +0 -5
- /package/dist/{src/lib/plugins → plugins}/image-manipulation/index.d.ts +0 -0
- /package/dist/{src/lib/plugins → plugins}/image-manipulation/types.d.ts +0 -0
- /package/dist/{src/lib/state → state}/manifests.svelte.d.ts +0 -0
- /package/dist/{src/lib/theme → theme}/colorUtils.d.ts +0 -0
- /package/dist/{src/lib/theme → theme}/index.d.ts +0 -0
- /package/dist/{src/lib/theme → theme}/types.d.ts +0 -0
- /package/dist/{src/lib/types → types}/config.d.ts +0 -0
- /package/dist/{src/lib/utils → utils}/annotationAdapter.d.ts +0 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
|
+
import CaretDown from 'phosphor-svelte/lib/CaretDown';
|
|
4
|
+
import Eye from 'phosphor-svelte/lib/Eye';
|
|
5
|
+
import EyeSlash from 'phosphor-svelte/lib/EyeSlash';
|
|
6
|
+
import { manifestsState } from '../state/manifests.svelte';
|
|
7
|
+
import { VIEWER_STATE_KEY, type ViewerState } from '../state/viewer.svelte';
|
|
8
|
+
import { m } from '../state/i18n.svelte';
|
|
9
|
+
|
|
10
|
+
const viewerState = getContext<ViewerState>(VIEWER_STATE_KEY);
|
|
11
|
+
|
|
12
|
+
let annotations = $derived.by(() => {
|
|
13
|
+
if (!viewerState.manifestId || !viewerState.canvasId) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
const manifestAnnotations = manifestsState.getAnnotations(
|
|
17
|
+
viewerState.manifestId,
|
|
18
|
+
viewerState.canvasId,
|
|
19
|
+
);
|
|
20
|
+
// Add search hits for current canvas
|
|
21
|
+
// These behave as ephemeral annotations
|
|
22
|
+
const searchAnnotations = viewerState.currentCanvasSearchAnnotations;
|
|
23
|
+
|
|
24
|
+
return [...manifestAnnotations, ...searchAnnotations];
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Helper to get ID from annotation object
|
|
28
|
+
function getAnnotationId(anno: any): string {
|
|
29
|
+
return (
|
|
30
|
+
anno.id ||
|
|
31
|
+
anno['@id'] ||
|
|
32
|
+
(typeof anno.getId === 'function' ? anno.getId() : '') ||
|
|
33
|
+
''
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Effect to initialize visibility when annotations load
|
|
38
|
+
$effect(() => {
|
|
39
|
+
// When annotations array changes (e.g. canvas change)
|
|
40
|
+
if (annotations.length > 0) {
|
|
41
|
+
const shouldBeVisible =
|
|
42
|
+
viewerState.config.annotations?.visible ?? true;
|
|
43
|
+
|
|
44
|
+
if (shouldBeVisible) {
|
|
45
|
+
const newSet = new Set<string>();
|
|
46
|
+
annotations.forEach((a: any) => {
|
|
47
|
+
const id = getAnnotationId(a);
|
|
48
|
+
if (id) newSet.add(id);
|
|
49
|
+
});
|
|
50
|
+
viewerState.visibleAnnotationIds = newSet;
|
|
51
|
+
} else {
|
|
52
|
+
viewerState.visibleAnnotationIds = new Set();
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
viewerState.visibleAnnotationIds = new Set();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Derived state for "All Visible" status
|
|
60
|
+
let isAllVisible = $derived.by(() => {
|
|
61
|
+
if (annotations.length === 0) return false;
|
|
62
|
+
return annotations.every((a: any) => {
|
|
63
|
+
const id = getAnnotationId(a);
|
|
64
|
+
return !id || viewerState.visibleAnnotationIds.has(id);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
function toggleAnnotation(id: string) {
|
|
69
|
+
if (viewerState.visibleAnnotationIds.has(id)) {
|
|
70
|
+
viewerState.visibleAnnotationIds.delete(id);
|
|
71
|
+
} else {
|
|
72
|
+
viewerState.visibleAnnotationIds.add(id);
|
|
73
|
+
}
|
|
74
|
+
// Reassign to trigger reactivity
|
|
75
|
+
viewerState.visibleAnnotationIds = new Set(
|
|
76
|
+
viewerState.visibleAnnotationIds,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function toggleAllAnnotations() {
|
|
81
|
+
if (isAllVisible) {
|
|
82
|
+
// Hide all
|
|
83
|
+
viewerState.visibleAnnotationIds = new Set();
|
|
84
|
+
} else {
|
|
85
|
+
// Show all
|
|
86
|
+
const newSet = new Set<string>();
|
|
87
|
+
annotations.forEach((a: any) => {
|
|
88
|
+
const id = getAnnotationId(a);
|
|
89
|
+
if (id) newSet.add(id);
|
|
90
|
+
});
|
|
91
|
+
viewerState.visibleAnnotationIds = newSet;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let renderedAnnotations = $derived.by(() => {
|
|
96
|
+
if (!annotations.length) return [];
|
|
97
|
+
|
|
98
|
+
return annotations.map((anno: any) => {
|
|
99
|
+
let content = '';
|
|
100
|
+
let isHtml = false;
|
|
101
|
+
|
|
102
|
+
// Extract content (support IIIF v2 and v3)
|
|
103
|
+
if (typeof anno.getBody === 'function') {
|
|
104
|
+
const body = anno.getBody();
|
|
105
|
+
if (body && body.length) {
|
|
106
|
+
const getValue = (b: any) => {
|
|
107
|
+
const val = b.getValue ? b.getValue() : null;
|
|
108
|
+
if (val) return val;
|
|
109
|
+
return '';
|
|
110
|
+
};
|
|
111
|
+
content = body
|
|
112
|
+
.map((b: any) => getValue(b))
|
|
113
|
+
.filter(Boolean)
|
|
114
|
+
.join(' ');
|
|
115
|
+
isHtml = body.some((b: any) => {
|
|
116
|
+
const fmt = b.getFormat ? b.getFormat() : '';
|
|
117
|
+
return (
|
|
118
|
+
fmt === 'text/html' || fmt === 'application/html'
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
if (!content && typeof anno.getLabel === 'function') {
|
|
123
|
+
const label = anno.getLabel();
|
|
124
|
+
if (label) content = label;
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
// Raw JSON Parsing (Fallback)
|
|
128
|
+
const getText = (r: any) => {
|
|
129
|
+
if (!r) return '';
|
|
130
|
+
return r.chars || r.value || r['cnt:chars'] || '';
|
|
131
|
+
};
|
|
132
|
+
const isHtmlFormat = (r: any) => {
|
|
133
|
+
if (!r) return false;
|
|
134
|
+
return r.format === 'text/html' || r.type === 'TextualBody';
|
|
135
|
+
};
|
|
136
|
+
if (anno.resource) {
|
|
137
|
+
if (Array.isArray(anno.resource)) {
|
|
138
|
+
content = anno.resource
|
|
139
|
+
.map((r: any) => getText(r))
|
|
140
|
+
.join(' ');
|
|
141
|
+
if (anno.resource.some((r: any) => isHtmlFormat(r)))
|
|
142
|
+
isHtml = true;
|
|
143
|
+
} else {
|
|
144
|
+
content = getText(anno.resource);
|
|
145
|
+
if (isHtmlFormat(anno.resource)) isHtml = true;
|
|
146
|
+
}
|
|
147
|
+
} else if (anno.body) {
|
|
148
|
+
if (Array.isArray(anno.body)) {
|
|
149
|
+
content = anno.body
|
|
150
|
+
.map((b: any) => getText(b))
|
|
151
|
+
.join(' ');
|
|
152
|
+
if (anno.body.some((b: any) => isHtmlFormat(b)))
|
|
153
|
+
isHtml = true;
|
|
154
|
+
} else {
|
|
155
|
+
content = getText(anno.body);
|
|
156
|
+
if (isHtmlFormat(anno.body)) isHtml = true;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (!content) {
|
|
160
|
+
const fallback = anno.label || anno.name || anno.title;
|
|
161
|
+
if (fallback)
|
|
162
|
+
content = Array.isArray(fallback)
|
|
163
|
+
? fallback.join(' ')
|
|
164
|
+
: fallback;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
id: getAnnotationId(anno),
|
|
170
|
+
content,
|
|
171
|
+
isHtml,
|
|
172
|
+
label:
|
|
173
|
+
(typeof anno.getLabel === 'function'
|
|
174
|
+
? anno.getLabel()
|
|
175
|
+
: anno.label) || '',
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
</script>
|
|
180
|
+
|
|
181
|
+
<!-- Unified Annotation Toolbar -->
|
|
182
|
+
{#if viewerState.showAnnotations}
|
|
183
|
+
<div
|
|
184
|
+
class="absolute top-4 right-4 z-40 pointer-events-auto transition-all duration-300"
|
|
185
|
+
>
|
|
186
|
+
<!-- z-index increased for Leaflet (z-400 is map) -->
|
|
187
|
+
<details class="group relative">
|
|
188
|
+
<summary
|
|
189
|
+
class="flex items-center gap-2 bg-base-200/90 backdrop-blur shadow-lg rounded-full px-4 py-2 cursor-pointer list-none hover:bg-base-200 transition-all select-none border border-base-300 pointer-events-auto"
|
|
190
|
+
>
|
|
191
|
+
<!-- Toggle Button -->
|
|
192
|
+
<button
|
|
193
|
+
class="btn btn-xs btn-circle btn-ghost"
|
|
194
|
+
onclick={(e) => {
|
|
195
|
+
e.preventDefault();
|
|
196
|
+
toggleAllAnnotations();
|
|
197
|
+
}}
|
|
198
|
+
title={isAllVisible
|
|
199
|
+
? m.hide_all_annotations()
|
|
200
|
+
: m.show_all_annotations()}
|
|
201
|
+
>
|
|
202
|
+
{#if isAllVisible}
|
|
203
|
+
<Eye size={16} weight="bold" />
|
|
204
|
+
{:else}
|
|
205
|
+
<EyeSlash size={16} weight="bold" />
|
|
206
|
+
{/if}
|
|
207
|
+
</button>
|
|
208
|
+
|
|
209
|
+
<!-- Badge Text -->
|
|
210
|
+
<span class="text-sm font-medium">
|
|
211
|
+
{m.annotations_count({ count: annotations.length })}
|
|
212
|
+
<span class="opacity-50 text-xs font-normal ml-1">
|
|
213
|
+
{m.visible_count({
|
|
214
|
+
count: viewerState.visibleAnnotationIds.size,
|
|
215
|
+
})}
|
|
216
|
+
</span>
|
|
217
|
+
</span>
|
|
218
|
+
|
|
219
|
+
<CaretDown
|
|
220
|
+
size={16}
|
|
221
|
+
weight="bold"
|
|
222
|
+
class="group-open:rotate-180 transition-transform opacity-80"
|
|
223
|
+
/>
|
|
224
|
+
</summary>
|
|
225
|
+
|
|
226
|
+
<!-- Expanded List -->
|
|
227
|
+
<div
|
|
228
|
+
class="absolute right-0 mt-2 w-96 bg-base-200/95 backdrop-blur shadow-xl rounded-box p-0 max-h-[60vh] overflow-y-auto border border-base-300 flex flex-col divide-y divide-base-300"
|
|
229
|
+
>
|
|
230
|
+
{#each renderedAnnotations as anno, i}
|
|
231
|
+
{@const isVisible = viewerState.visibleAnnotationIds.has(
|
|
232
|
+
anno.id,
|
|
233
|
+
)}
|
|
234
|
+
<!-- List Item Row: Click toggles visibility -->
|
|
235
|
+
<button
|
|
236
|
+
class="w-full text-left p-3 hover:bg-base-300 transition-colors cursor-pointer flex gap-3 group/item items-start focus:outline-none focus:bg-base-300"
|
|
237
|
+
onclick={(e) => {
|
|
238
|
+
e.preventDefault();
|
|
239
|
+
toggleAnnotation(anno.id);
|
|
240
|
+
}}
|
|
241
|
+
>
|
|
242
|
+
<!-- Visual Toggle Indicator (formerly a button) -->
|
|
243
|
+
<div
|
|
244
|
+
class="btn btn-xs btn-circle btn-ghost mt-0.5 shrink-0 pointer-events-none"
|
|
245
|
+
>
|
|
246
|
+
{#if isVisible}
|
|
247
|
+
<Eye size={16} weight="bold" />
|
|
248
|
+
{:else}
|
|
249
|
+
<EyeSlash size={16} weight="bold" />
|
|
250
|
+
{/if}
|
|
251
|
+
</div>
|
|
252
|
+
|
|
253
|
+
<div class="flex-1 min-w-0">
|
|
254
|
+
<div class="flex items-start justify-between">
|
|
255
|
+
<span class="font-bold text-sm text-primary"
|
|
256
|
+
>#{i + 1}</span
|
|
257
|
+
>
|
|
258
|
+
<!-- Only show label separately if it's different from the content being displayed -->
|
|
259
|
+
{#if anno.label && anno.label !== anno.content}
|
|
260
|
+
<span
|
|
261
|
+
class="text-xs opacity-50 truncate max-w-[150px]"
|
|
262
|
+
>{anno.label}</span
|
|
263
|
+
>
|
|
264
|
+
{/if}
|
|
265
|
+
</div>
|
|
266
|
+
<div
|
|
267
|
+
class="text-sm prose prose-sm max-w-none prose-p:my-0 prose-a:text-blue-500 wrap-break-word text-left {isVisible
|
|
268
|
+
? ''
|
|
269
|
+
: 'opacity-50'}"
|
|
270
|
+
>
|
|
271
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
272
|
+
{#if anno.isHtml}
|
|
273
|
+
{@html anno.content}
|
|
274
|
+
{:else}
|
|
275
|
+
{anno.content || '(No content)'}
|
|
276
|
+
{/if}
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
</button>
|
|
280
|
+
{:else}
|
|
281
|
+
<div class="p-4 text-center opacity-50 text-sm">
|
|
282
|
+
No annotations available.
|
|
283
|
+
</div>
|
|
284
|
+
{/each}
|
|
285
|
+
</div>
|
|
286
|
+
</details>
|
|
287
|
+
</div>
|
|
288
|
+
{/if}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import CaretLeft from 'phosphor-svelte/lib/CaretLeft';
|
|
3
|
+
import CaretRight from 'phosphor-svelte/lib/CaretRight';
|
|
4
|
+
import { m } from '../state/i18n.svelte';
|
|
5
|
+
let { viewerState } = $props();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<div
|
|
9
|
+
class="select-none absolute left-1/2 -translate-x-1/2 bg-base-200/90 backdrop-blur rounded-full shadow-lg flex items-center gap-4 z-10 border border-base-300 transition-all duration-200 bottom-4"
|
|
10
|
+
>
|
|
11
|
+
<button
|
|
12
|
+
class="btn btn-circle btn-sm btn-ghost"
|
|
13
|
+
disabled={!viewerState.hasPrevious}
|
|
14
|
+
onclick={() => viewerState.previousCanvas()}
|
|
15
|
+
aria-label={m.previous_canvas()}
|
|
16
|
+
>
|
|
17
|
+
<CaretLeft size={20} weight="bold" />
|
|
18
|
+
</button>
|
|
19
|
+
|
|
20
|
+
<span class="text-sm font-mono tabular-nums text-nowrap">
|
|
21
|
+
{viewerState.currentCanvasIndex + 1} / {viewerState.canvases.length}
|
|
22
|
+
</span>
|
|
23
|
+
|
|
24
|
+
<button
|
|
25
|
+
class="btn btn-circle btn-sm btn-ghost"
|
|
26
|
+
disabled={!viewerState.hasNext}
|
|
27
|
+
onclick={() => viewerState.nextCanvas()}
|
|
28
|
+
aria-label={m.next_canvas()}
|
|
29
|
+
>
|
|
30
|
+
<CaretRight size={20} weight="bold" />
|
|
31
|
+
</button>
|
|
32
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export default CanvasNavigation;
|
|
2
|
+
type CanvasNavigation = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const CanvasNavigation: import("svelte").Component<{
|
|
7
|
+
viewerState: any;
|
|
8
|
+
}, {}, "">;
|
|
9
|
+
type $$ComponentProps = {
|
|
10
|
+
viewerState: any;
|
|
11
|
+
};
|