triiiceratops 0.8.2 → 0.9.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/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/index.js +9 -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/triiiceratops.css +0 -1
- /package/dist/{src/lib/index.d.ts → index.d.ts} +0 -0
- /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,703 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import GithubLogo from 'phosphor-svelte/lib/GithubLogo';
|
|
3
|
+
import Gear from 'phosphor-svelte/lib/Gear';
|
|
4
|
+
import Copy from 'phosphor-svelte/lib/Copy';
|
|
5
|
+
import MagnifyingGlass from 'phosphor-svelte/lib/MagnifyingGlass';
|
|
6
|
+
import ThemeToggle from './ThemeToggle.svelte';
|
|
7
|
+
|
|
8
|
+
import { m, language } from '../state/i18n.svelte';
|
|
9
|
+
import { manifestsState } from '../state/manifests.svelte';
|
|
10
|
+
import {
|
|
11
|
+
availableLanguageTags,
|
|
12
|
+
setLanguageTag,
|
|
13
|
+
} from '../paraglide/runtime.js';
|
|
14
|
+
|
|
15
|
+
import { onMount } from 'svelte';
|
|
16
|
+
|
|
17
|
+
const isDev = import.meta.env.DEV;
|
|
18
|
+
|
|
19
|
+
const SUGGESTED_MANIFESTS = [
|
|
20
|
+
{
|
|
21
|
+
label: 'Wellcome Collection (b18035723)',
|
|
22
|
+
url: 'https://iiif.wellcomecollection.org/presentation/v2/b18035723',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
label: 'Self-Portrait Dedicated to Paul Gauguin',
|
|
26
|
+
url: 'https://iiif.harvardartmuseums.org/manifests/object/299843',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
label: 'CSNTM (MNTGRCP40)',
|
|
30
|
+
url: 'https://collections.csntm.org/image-service/iiif/artifacts/MNTGRCP40/default/manifest/',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
label: 'Bodleian Library MS. Ind. Inst. Misc. 22',
|
|
34
|
+
url: 'https://iiif.bodleian.ox.ac.uk/iiif/manifest/e32a277e-91e2-4a6d-8ba6-cc4bad230410.json',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
label: 'Yugoslavia',
|
|
38
|
+
url: 'https://zavicajna.digitalna.rs/iiif/api/presentation/3/96571949-03d6-478e-ab44-a2d5ad68f935%252F00000001%252Fostalo01%252F00000071/manifest',
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
let {
|
|
43
|
+
manifestUrl = $bindable(),
|
|
44
|
+
onLoad,
|
|
45
|
+
viewerMode = $bindable('core'),
|
|
46
|
+
canvasId = $bindable(''),
|
|
47
|
+
config = $bindable({}),
|
|
48
|
+
} = $props();
|
|
49
|
+
|
|
50
|
+
onMount(() => {
|
|
51
|
+
if (!manifestUrl) {
|
|
52
|
+
manifestUrl = SUGGESTED_MANIFESTS[0].url;
|
|
53
|
+
onLoad();
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
let isCustom = $derived(
|
|
58
|
+
!SUGGESTED_MANIFESTS.some((m) => m.url === manifestUrl),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
let canvases = $derived(
|
|
62
|
+
manifestUrl ? manifestsState.getCanvases(manifestUrl) : [],
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
function getCanvasLabel(canvas: any, index: number) {
|
|
66
|
+
try {
|
|
67
|
+
if (canvas.getLabel) {
|
|
68
|
+
const l = canvas.getLabel();
|
|
69
|
+
if (Array.isArray(l) && l.length > 0) return l[0].value;
|
|
70
|
+
if (typeof l === 'string') return l;
|
|
71
|
+
} else if (canvas.label) {
|
|
72
|
+
if (typeof canvas.label === 'string') return canvas.label;
|
|
73
|
+
if (
|
|
74
|
+
typeof canvas.label === 'object' &&
|
|
75
|
+
!Array.isArray(canvas.label)
|
|
76
|
+
) {
|
|
77
|
+
const keys = Object.keys(canvas.label);
|
|
78
|
+
if (keys.length > 0) {
|
|
79
|
+
const val = canvas.label[keys[0]];
|
|
80
|
+
if (Array.isArray(val)) return val[0];
|
|
81
|
+
return val;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} catch (e) {
|
|
86
|
+
/* ignore */
|
|
87
|
+
}
|
|
88
|
+
return `Canvas ${index + 1}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function handleSelectChange(e: Event) {
|
|
92
|
+
const value = (e.currentTarget as HTMLSelectElement).value;
|
|
93
|
+
if (value !== 'custom') {
|
|
94
|
+
manifestUrl = value;
|
|
95
|
+
onLoad();
|
|
96
|
+
} else {
|
|
97
|
+
manifestUrl = '';
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const languageNames: Record<string, string> = {
|
|
102
|
+
en: 'English',
|
|
103
|
+
de: 'Deutsch',
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
107
|
+
if (e.key === 'Enter') {
|
|
108
|
+
onLoad();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
import Check from 'phosphor-svelte/lib/Check';
|
|
112
|
+
|
|
113
|
+
let copied = $state(false);
|
|
114
|
+
|
|
115
|
+
function copyConfig() {
|
|
116
|
+
navigator.clipboard.writeText(JSON.stringify(config, null, 2));
|
|
117
|
+
copied = true;
|
|
118
|
+
setTimeout(() => {
|
|
119
|
+
copied = false;
|
|
120
|
+
}, 2000);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Initialize from config
|
|
124
|
+
let activeSearchTerm = $state(config.search?.query || '');
|
|
125
|
+
let searchInitialized = false;
|
|
126
|
+
|
|
127
|
+
$effect(() => {
|
|
128
|
+
// Only update if not yet initialized and config has a value (e.g. from URL load)
|
|
129
|
+
if (!searchInitialized && config.search?.query) {
|
|
130
|
+
activeSearchTerm = config.search.query;
|
|
131
|
+
searchInitialized = true;
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
function handleSearchKeydown(e: KeyboardEvent) {
|
|
136
|
+
if (e.key === 'Enter' && config.search) {
|
|
137
|
+
config.search.query = activeSearchTerm;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
</script>
|
|
141
|
+
|
|
142
|
+
<header
|
|
143
|
+
class="flex flex-col bg-base-200 shrink-0 border-b border-base-300 relative z-800"
|
|
144
|
+
>
|
|
145
|
+
<!-- Top Row: Branding & Global Settings -->
|
|
146
|
+
<div class="flex gap-4 items-center p-2 px-4 border-b border-base-300/50">
|
|
147
|
+
<a href="/triiiceratops/" class="btn btn-sm btn-ghost font-bold text-lg"
|
|
148
|
+
>Triiiceratops</a
|
|
149
|
+
>
|
|
150
|
+
<a href="/triiiceratops/" class="btn btn-sm btn-outline btn-primary"
|
|
151
|
+
>{m.docs()}</a
|
|
152
|
+
>
|
|
153
|
+
|
|
154
|
+
<div class="flex-1"></div>
|
|
155
|
+
|
|
156
|
+
<div class="join">
|
|
157
|
+
<div
|
|
158
|
+
class="tooltip tooltip-bottom"
|
|
159
|
+
data-tip={m.viewer_variant_tooltip_core()}
|
|
160
|
+
>
|
|
161
|
+
<input
|
|
162
|
+
class="join-item btn btn-sm"
|
|
163
|
+
type="radio"
|
|
164
|
+
name="viewerMode"
|
|
165
|
+
aria-label={m.viewer_variant_core()}
|
|
166
|
+
value="core"
|
|
167
|
+
bind:group={viewerMode}
|
|
168
|
+
/>
|
|
169
|
+
</div>
|
|
170
|
+
<div
|
|
171
|
+
class="tooltip tooltip-bottom"
|
|
172
|
+
data-tip={m.viewer_variant_tooltip_full()}
|
|
173
|
+
>
|
|
174
|
+
<input
|
|
175
|
+
class="join-item btn btn-sm"
|
|
176
|
+
type="radio"
|
|
177
|
+
name="viewerMode"
|
|
178
|
+
aria-label={m.viewer_variant_full()}
|
|
179
|
+
value="image"
|
|
180
|
+
bind:group={viewerMode}
|
|
181
|
+
/>
|
|
182
|
+
</div>
|
|
183
|
+
{#if isDev}
|
|
184
|
+
<div
|
|
185
|
+
class="tooltip tooltip-bottom"
|
|
186
|
+
data-tip={m.viewer_variant_tooltip_custom_theme()}
|
|
187
|
+
>
|
|
188
|
+
<input
|
|
189
|
+
class="join-item btn btn-sm"
|
|
190
|
+
type="radio"
|
|
191
|
+
name="viewerMode"
|
|
192
|
+
aria-label={m.viewer_variant_custom_theme()}
|
|
193
|
+
value="custom-theme"
|
|
194
|
+
bind:group={viewerMode}
|
|
195
|
+
/>
|
|
196
|
+
</div>
|
|
197
|
+
<div
|
|
198
|
+
class="tooltip tooltip-bottom"
|
|
199
|
+
data-tip={m.viewer_variant_svelte_component_tooltip()}
|
|
200
|
+
>
|
|
201
|
+
<input
|
|
202
|
+
class="join-item btn btn-sm"
|
|
203
|
+
type="radio"
|
|
204
|
+
name="viewerMode"
|
|
205
|
+
aria-label={m.viewer_variant_svelte()}
|
|
206
|
+
value="svelte"
|
|
207
|
+
bind:group={viewerMode}
|
|
208
|
+
/>
|
|
209
|
+
</div>
|
|
210
|
+
{/if}
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<select
|
|
214
|
+
class="select select-bordered select-sm w-auto"
|
|
215
|
+
value={language.current}
|
|
216
|
+
onchange={(e) => setLanguageTag(e.currentTarget.value as any)}
|
|
217
|
+
aria-label={m.language_select_label()}
|
|
218
|
+
>
|
|
219
|
+
{#each availableLanguageTags as lang}
|
|
220
|
+
<option value={lang}>{languageNames[lang] || lang}</option>
|
|
221
|
+
{/each}
|
|
222
|
+
</select>
|
|
223
|
+
|
|
224
|
+
<ThemeToggle />
|
|
225
|
+
|
|
226
|
+
<!-- Settings Dropdown -->
|
|
227
|
+
<div class="dropdown dropdown-end group">
|
|
228
|
+
<div
|
|
229
|
+
tabindex="0"
|
|
230
|
+
role="button"
|
|
231
|
+
class="btn btn-ghost btn-sm"
|
|
232
|
+
aria-label={m.settings_label()}
|
|
233
|
+
>
|
|
234
|
+
<Gear size={20} />
|
|
235
|
+
</div>
|
|
236
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
237
|
+
<ul
|
|
238
|
+
tabindex="-1"
|
|
239
|
+
class="dropdown-content z-20 menu bg-base-100 rounded-box w-80 p-2 shadow border border-base-300 max-h-[80vh] overflow-y-auto block invisible pointer-events-none group-focus-within:visible group-focus-within:pointer-events-auto"
|
|
240
|
+
>
|
|
241
|
+
<li class="menu-title px-4 py-2">
|
|
242
|
+
{m.settings_category_general()}
|
|
243
|
+
</li>
|
|
244
|
+
<li>
|
|
245
|
+
<label class="label cursor-pointer py-1">
|
|
246
|
+
<span class="label-text"
|
|
247
|
+
>{m.settings_toggle_left_menu()}</span
|
|
248
|
+
>
|
|
249
|
+
<input
|
|
250
|
+
type="checkbox"
|
|
251
|
+
class="toggle toggle-sm"
|
|
252
|
+
bind:checked={config.showLeftMenu}
|
|
253
|
+
/>
|
|
254
|
+
</label>
|
|
255
|
+
</li>
|
|
256
|
+
<li>
|
|
257
|
+
<label class="label cursor-pointer py-1">
|
|
258
|
+
<span class="label-text"
|
|
259
|
+
>{m.settings_toggle_right_menu()}</span
|
|
260
|
+
>
|
|
261
|
+
<input
|
|
262
|
+
type="checkbox"
|
|
263
|
+
class="toggle toggle-sm"
|
|
264
|
+
bind:checked={config.showRightMenu}
|
|
265
|
+
/>
|
|
266
|
+
</label>
|
|
267
|
+
</li>
|
|
268
|
+
<li>
|
|
269
|
+
<label class="label cursor-pointer py-1">
|
|
270
|
+
<span class="label-text"
|
|
271
|
+
>{m.settings_toggle_canvas_nav()}</span
|
|
272
|
+
>
|
|
273
|
+
<input
|
|
274
|
+
type="checkbox"
|
|
275
|
+
class="toggle toggle-sm"
|
|
276
|
+
bind:checked={config.showCanvasNav}
|
|
277
|
+
/>
|
|
278
|
+
</label>
|
|
279
|
+
</li>
|
|
280
|
+
|
|
281
|
+
<div class="divider my-1"></div>
|
|
282
|
+
|
|
283
|
+
<li class="menu-title px-4 py-2">
|
|
284
|
+
{m.settings_category_configuration()}
|
|
285
|
+
</li>
|
|
286
|
+
|
|
287
|
+
<li>
|
|
288
|
+
<details>
|
|
289
|
+
<summary
|
|
290
|
+
>{m.settings_submenu_right_menu_items()}</summary
|
|
291
|
+
>
|
|
292
|
+
<ul>
|
|
293
|
+
<li>
|
|
294
|
+
<label class="label cursor-pointer py-1">
|
|
295
|
+
<span class="label-text"
|
|
296
|
+
>{m.settings_toggle_show_search()}</span
|
|
297
|
+
>
|
|
298
|
+
<input
|
|
299
|
+
type="checkbox"
|
|
300
|
+
class="checkbox checkbox-xs"
|
|
301
|
+
checked={config.rightMenu?.showSearch ??
|
|
302
|
+
true}
|
|
303
|
+
onchange={(e) => {
|
|
304
|
+
if (!config.rightMenu)
|
|
305
|
+
config.rightMenu = {};
|
|
306
|
+
config.rightMenu.showSearch =
|
|
307
|
+
e.currentTarget.checked;
|
|
308
|
+
}}
|
|
309
|
+
/>
|
|
310
|
+
</label>
|
|
311
|
+
</li>
|
|
312
|
+
<li>
|
|
313
|
+
<label class="label cursor-pointer py-1">
|
|
314
|
+
<span class="label-text"
|
|
315
|
+
>{m.settings_toggle_show_gallery()}</span
|
|
316
|
+
>
|
|
317
|
+
<input
|
|
318
|
+
type="checkbox"
|
|
319
|
+
class="checkbox checkbox-xs"
|
|
320
|
+
checked={config.rightMenu
|
|
321
|
+
?.showGallery ?? true}
|
|
322
|
+
onchange={(e) => {
|
|
323
|
+
if (!config.rightMenu)
|
|
324
|
+
config.rightMenu = {};
|
|
325
|
+
config.rightMenu.showGallery =
|
|
326
|
+
e.currentTarget.checked;
|
|
327
|
+
}}
|
|
328
|
+
/>
|
|
329
|
+
</label>
|
|
330
|
+
</li>
|
|
331
|
+
<li>
|
|
332
|
+
<label class="label cursor-pointer py-1">
|
|
333
|
+
<span class="label-text"
|
|
334
|
+
>{m.settings_toggle_show_annotations()}</span
|
|
335
|
+
>
|
|
336
|
+
<input
|
|
337
|
+
type="checkbox"
|
|
338
|
+
class="checkbox checkbox-xs"
|
|
339
|
+
checked={config.rightMenu
|
|
340
|
+
?.showAnnotations ?? true}
|
|
341
|
+
onchange={(e) => {
|
|
342
|
+
if (!config.rightMenu)
|
|
343
|
+
config.rightMenu = {};
|
|
344
|
+
config.rightMenu.showAnnotations =
|
|
345
|
+
e.currentTarget.checked;
|
|
346
|
+
}}
|
|
347
|
+
/>
|
|
348
|
+
</label>
|
|
349
|
+
</li>
|
|
350
|
+
<li>
|
|
351
|
+
<label class="label cursor-pointer py-1">
|
|
352
|
+
<span class="label-text"
|
|
353
|
+
>{m.settings_toggle_show_fullscreen()}</span
|
|
354
|
+
>
|
|
355
|
+
<input
|
|
356
|
+
type="checkbox"
|
|
357
|
+
class="checkbox checkbox-xs"
|
|
358
|
+
checked={config.rightMenu
|
|
359
|
+
?.showFullscreen ?? true}
|
|
360
|
+
onchange={(e) => {
|
|
361
|
+
if (!config.rightMenu)
|
|
362
|
+
config.rightMenu = {};
|
|
363
|
+
config.rightMenu.showFullscreen =
|
|
364
|
+
e.currentTarget.checked;
|
|
365
|
+
}}
|
|
366
|
+
/>
|
|
367
|
+
</label>
|
|
368
|
+
</li>
|
|
369
|
+
<li>
|
|
370
|
+
<label class="label cursor-pointer py-1">
|
|
371
|
+
<span class="label-text"
|
|
372
|
+
>{m.settings_toggle_show_info()}</span
|
|
373
|
+
>
|
|
374
|
+
<input
|
|
375
|
+
type="checkbox"
|
|
376
|
+
class="checkbox checkbox-xs"
|
|
377
|
+
checked={config.rightMenu?.showInfo ??
|
|
378
|
+
true}
|
|
379
|
+
onchange={(e) => {
|
|
380
|
+
if (!config.rightMenu)
|
|
381
|
+
config.rightMenu = {};
|
|
382
|
+
config.rightMenu.showInfo =
|
|
383
|
+
e.currentTarget.checked;
|
|
384
|
+
}}
|
|
385
|
+
/>
|
|
386
|
+
</label>
|
|
387
|
+
</li>
|
|
388
|
+
</ul>
|
|
389
|
+
</details>
|
|
390
|
+
</li>
|
|
391
|
+
|
|
392
|
+
<li>
|
|
393
|
+
<details>
|
|
394
|
+
<summary>{m.settings_submenu_gallery()}</summary>
|
|
395
|
+
<ul>
|
|
396
|
+
<li>
|
|
397
|
+
<label class="label cursor-pointer py-1">
|
|
398
|
+
<span class="label-text"
|
|
399
|
+
>{m.settings_toggle_open()}</span
|
|
400
|
+
>
|
|
401
|
+
<input
|
|
402
|
+
type="checkbox"
|
|
403
|
+
class="toggle toggle-xs"
|
|
404
|
+
checked={config.gallery?.open ?? false}
|
|
405
|
+
onchange={(e) => {
|
|
406
|
+
if (!config.gallery)
|
|
407
|
+
config.gallery = {};
|
|
408
|
+
config.gallery.open =
|
|
409
|
+
e.currentTarget.checked;
|
|
410
|
+
}}
|
|
411
|
+
/>
|
|
412
|
+
</label>
|
|
413
|
+
</li>
|
|
414
|
+
<li>
|
|
415
|
+
<label class="label cursor-pointer py-1">
|
|
416
|
+
<span class="label-text"
|
|
417
|
+
>{m.settings_toggle_draggable()}</span
|
|
418
|
+
>
|
|
419
|
+
<input
|
|
420
|
+
type="checkbox"
|
|
421
|
+
class="checkbox checkbox-xs"
|
|
422
|
+
checked={config.gallery?.draggable ??
|
|
423
|
+
true}
|
|
424
|
+
onchange={(e) => {
|
|
425
|
+
if (!config.gallery)
|
|
426
|
+
config.gallery = {};
|
|
427
|
+
config.gallery.draggable =
|
|
428
|
+
e.currentTarget.checked;
|
|
429
|
+
}}
|
|
430
|
+
/>
|
|
431
|
+
</label>
|
|
432
|
+
</li>
|
|
433
|
+
<li>
|
|
434
|
+
<label class="label cursor-pointer py-1">
|
|
435
|
+
<span class="label-text"
|
|
436
|
+
>{m.settings_toggle_close_button()}</span
|
|
437
|
+
>
|
|
438
|
+
<input
|
|
439
|
+
type="checkbox"
|
|
440
|
+
class="checkbox checkbox-xs"
|
|
441
|
+
checked={config.gallery
|
|
442
|
+
?.showCloseButton ?? true}
|
|
443
|
+
onchange={(e) => {
|
|
444
|
+
if (!config.gallery)
|
|
445
|
+
config.gallery = {};
|
|
446
|
+
config.gallery.showCloseButton =
|
|
447
|
+
e.currentTarget.checked;
|
|
448
|
+
}}
|
|
449
|
+
/>
|
|
450
|
+
</label>
|
|
451
|
+
</li>
|
|
452
|
+
<li>
|
|
453
|
+
<label class="label cursor-pointer py-1 gap-2">
|
|
454
|
+
<span class="label-text"
|
|
455
|
+
>{m.settings_select_dock_position()}</span
|
|
456
|
+
>
|
|
457
|
+
<select
|
|
458
|
+
class="select select-bordered select-xs w-24"
|
|
459
|
+
value={config.gallery?.dockPosition ??
|
|
460
|
+
'bottom'}
|
|
461
|
+
onchange={(e) => {
|
|
462
|
+
if (!config.gallery)
|
|
463
|
+
config.gallery = {};
|
|
464
|
+
config.gallery.dockPosition = (
|
|
465
|
+
e.currentTarget as HTMLSelectElement
|
|
466
|
+
).value;
|
|
467
|
+
}}
|
|
468
|
+
onclick={(e) => e.stopPropagation()}
|
|
469
|
+
>
|
|
470
|
+
<option value="bottom"
|
|
471
|
+
>{m.settings_position_bottom()}</option
|
|
472
|
+
>
|
|
473
|
+
<option value="top"
|
|
474
|
+
>{m.settings_position_top()}</option
|
|
475
|
+
>
|
|
476
|
+
<option value="left"
|
|
477
|
+
>{m.settings_position_left()}</option
|
|
478
|
+
>
|
|
479
|
+
<option value="right"
|
|
480
|
+
>{m.settings_position_right()}</option
|
|
481
|
+
>
|
|
482
|
+
<option value="none"
|
|
483
|
+
>{m.settings_position_floating()}</option
|
|
484
|
+
>
|
|
485
|
+
</select>
|
|
486
|
+
</label>
|
|
487
|
+
</li>
|
|
488
|
+
</ul>
|
|
489
|
+
</details>
|
|
490
|
+
</li>
|
|
491
|
+
|
|
492
|
+
<li>
|
|
493
|
+
<details>
|
|
494
|
+
<summary>{m.settings_submenu_search()}</summary>
|
|
495
|
+
<ul>
|
|
496
|
+
<li>
|
|
497
|
+
<label class="label cursor-pointer py-1">
|
|
498
|
+
<span class="label-text"
|
|
499
|
+
>{m.settings_toggle_open()}</span
|
|
500
|
+
>
|
|
501
|
+
<input
|
|
502
|
+
type="checkbox"
|
|
503
|
+
class="toggle toggle-xs"
|
|
504
|
+
checked={config.search?.open ?? false}
|
|
505
|
+
onchange={(e) => {
|
|
506
|
+
if (!config.search)
|
|
507
|
+
config.search = {};
|
|
508
|
+
config.search.open =
|
|
509
|
+
e.currentTarget.checked;
|
|
510
|
+
}}
|
|
511
|
+
/>
|
|
512
|
+
</label>
|
|
513
|
+
</li>
|
|
514
|
+
<li>
|
|
515
|
+
<label class="label cursor-pointer py-1">
|
|
516
|
+
<span class="label-text"
|
|
517
|
+
>{m.settings_toggle_close_button()}</span
|
|
518
|
+
>
|
|
519
|
+
<input
|
|
520
|
+
type="checkbox"
|
|
521
|
+
class="checkbox checkbox-xs"
|
|
522
|
+
checked={config.search
|
|
523
|
+
?.showCloseButton ?? true}
|
|
524
|
+
onchange={(e) => {
|
|
525
|
+
if (!config.search)
|
|
526
|
+
config.search = {};
|
|
527
|
+
config.search.showCloseButton =
|
|
528
|
+
e.currentTarget.checked;
|
|
529
|
+
}}
|
|
530
|
+
/>
|
|
531
|
+
</label>
|
|
532
|
+
</li>
|
|
533
|
+
</ul>
|
|
534
|
+
</details>
|
|
535
|
+
</li>
|
|
536
|
+
|
|
537
|
+
<li>
|
|
538
|
+
<details>
|
|
539
|
+
<summary>{m.settings_submenu_annotations()}</summary>
|
|
540
|
+
<ul>
|
|
541
|
+
<li>
|
|
542
|
+
<label class="label cursor-pointer py-1">
|
|
543
|
+
<span class="label-text"
|
|
544
|
+
>{m.settings_toggle_panel_open()}</span
|
|
545
|
+
>
|
|
546
|
+
<input
|
|
547
|
+
type="checkbox"
|
|
548
|
+
class="toggle toggle-xs"
|
|
549
|
+
checked={config.annotations?.open ??
|
|
550
|
+
false}
|
|
551
|
+
onchange={(e) => {
|
|
552
|
+
if (!config.annotations)
|
|
553
|
+
config.annotations = {};
|
|
554
|
+
config.annotations.open =
|
|
555
|
+
e.currentTarget.checked;
|
|
556
|
+
}}
|
|
557
|
+
/>
|
|
558
|
+
</label>
|
|
559
|
+
</li>
|
|
560
|
+
<li>
|
|
561
|
+
<label class="label cursor-pointer py-1">
|
|
562
|
+
<span class="label-text"
|
|
563
|
+
>{m.settings_toggle_visible_by_default()}</span
|
|
564
|
+
>
|
|
565
|
+
<input
|
|
566
|
+
type="checkbox"
|
|
567
|
+
class="checkbox checkbox-xs"
|
|
568
|
+
checked={config.annotations?.visible ??
|
|
569
|
+
true}
|
|
570
|
+
onchange={(e) => {
|
|
571
|
+
if (!config.annotations)
|
|
572
|
+
config.annotations = {};
|
|
573
|
+
config.annotations.visible =
|
|
574
|
+
e.currentTarget.checked;
|
|
575
|
+
}}
|
|
576
|
+
/>
|
|
577
|
+
</label>
|
|
578
|
+
</li>
|
|
579
|
+
</ul>
|
|
580
|
+
</details>
|
|
581
|
+
</li>
|
|
582
|
+
<div class="divider my-1"></div>
|
|
583
|
+
<li>
|
|
584
|
+
<button
|
|
585
|
+
class="btn btn-sm btn-ghost w-full justify-start gap-2"
|
|
586
|
+
class:text-success={copied}
|
|
587
|
+
onclick={copyConfig}
|
|
588
|
+
>
|
|
589
|
+
{#if copied}
|
|
590
|
+
<Check size={16} />
|
|
591
|
+
{m.copied()}
|
|
592
|
+
{:else}
|
|
593
|
+
<Copy size={16} />
|
|
594
|
+
{m.copy_config()}
|
|
595
|
+
{/if}
|
|
596
|
+
</button>
|
|
597
|
+
</li>
|
|
598
|
+
</ul>
|
|
599
|
+
</div>
|
|
600
|
+
|
|
601
|
+
<div class="tooltip tooltip-bottom" data-tip={m.github()}>
|
|
602
|
+
<a
|
|
603
|
+
href="https://github.com/d-flood/triiiceratops"
|
|
604
|
+
class="btn btn-ghost btn-sm"
|
|
605
|
+
>
|
|
606
|
+
<GithubLogo size={20} />
|
|
607
|
+
</a>
|
|
608
|
+
</div>
|
|
609
|
+
</div>
|
|
610
|
+
|
|
611
|
+
<!-- Bottom Row: External Controls -->
|
|
612
|
+
<div class="flex gap-4 items-center p-2 px-4 bg-base-300/30">
|
|
613
|
+
<span class="text-xs font-bold uppercase tracking-wider opacity-70"
|
|
614
|
+
>{m.demo_header_external_controls()}</span
|
|
615
|
+
>
|
|
616
|
+
|
|
617
|
+
<!-- Manifest Selector -->
|
|
618
|
+
<div class="flex gap-2 items-center">
|
|
619
|
+
<label
|
|
620
|
+
for="manifest-select"
|
|
621
|
+
class="text-base-content text-sm whitespace-nowrap sr-only"
|
|
622
|
+
>
|
|
623
|
+
{m.iiif_manifest_label()}
|
|
624
|
+
</label>
|
|
625
|
+
<div class="flex gap-2 items-center">
|
|
626
|
+
<select
|
|
627
|
+
id="manifest-select"
|
|
628
|
+
class="select select-bordered select-xs max-w-xs"
|
|
629
|
+
value={isCustom ? 'custom' : manifestUrl}
|
|
630
|
+
onchange={handleSelectChange}
|
|
631
|
+
>
|
|
632
|
+
{#each SUGGESTED_MANIFESTS as manifest}
|
|
633
|
+
<option value={manifest.url}>{manifest.label}</option>
|
|
634
|
+
{/each}
|
|
635
|
+
<option value="custom">{m.try_your_own()}</option>
|
|
636
|
+
</select>
|
|
637
|
+
|
|
638
|
+
{#if isCustom}
|
|
639
|
+
<input
|
|
640
|
+
id="manifest-input"
|
|
641
|
+
type="text"
|
|
642
|
+
bind:value={manifestUrl}
|
|
643
|
+
onkeydown={handleKeydown}
|
|
644
|
+
placeholder={m.manifest_placeholder()}
|
|
645
|
+
class="input input-bordered input-xs w-[300px] fade-in"
|
|
646
|
+
autocomplete="off"
|
|
647
|
+
/>
|
|
648
|
+
<button onclick={onLoad} class="btn btn-primary btn-xs">
|
|
649
|
+
{m.load()}
|
|
650
|
+
</button>
|
|
651
|
+
{/if}
|
|
652
|
+
</div>
|
|
653
|
+
</div>
|
|
654
|
+
|
|
655
|
+
<div class="w-px h-4 bg-base-content/20 mx-2"></div>
|
|
656
|
+
|
|
657
|
+
<!-- Canvas Selector -->
|
|
658
|
+
<div class="flex gap-2 items-center">
|
|
659
|
+
<label class="text-xs opacity-70" for="canvas-id-select">
|
|
660
|
+
{m.demo_header_active_canvas()}
|
|
661
|
+
</label>
|
|
662
|
+
<select
|
|
663
|
+
id="canvas-id-select"
|
|
664
|
+
bind:value={canvasId}
|
|
665
|
+
class="select select-bordered select-xs w-[200px]"
|
|
666
|
+
disabled={canvases.length === 0}
|
|
667
|
+
>
|
|
668
|
+
{#if canvases.length === 0}
|
|
669
|
+
<option value="" disabled>{m.no_canvases_loaded()}</option>
|
|
670
|
+
{:else}
|
|
671
|
+
{#each canvases as canvas, i}
|
|
672
|
+
<option value={canvas.id}>
|
|
673
|
+
{getCanvasLabel(canvas, i)}
|
|
674
|
+
</option>
|
|
675
|
+
{/each}
|
|
676
|
+
{/if}
|
|
677
|
+
</select>
|
|
678
|
+
</div>
|
|
679
|
+
|
|
680
|
+
<div class="w-px h-4 bg-base-content/20 mx-2"></div>
|
|
681
|
+
|
|
682
|
+
<!-- Search Input -->
|
|
683
|
+
<div class="flex gap-2 items-center">
|
|
684
|
+
<label
|
|
685
|
+
class="text-xs opacity-70 flex items-center gap-1"
|
|
686
|
+
for="external-search-input"
|
|
687
|
+
>
|
|
688
|
+
<MagnifyingGlass size={14} />
|
|
689
|
+
<span class="sr-only">{m.search()}</span>
|
|
690
|
+
</label>
|
|
691
|
+
{#if config.search}
|
|
692
|
+
<input
|
|
693
|
+
id="external-search-input"
|
|
694
|
+
type="text"
|
|
695
|
+
placeholder={m.search_panel_placeholder()}
|
|
696
|
+
class="input input-bordered input-xs w-[150px]"
|
|
697
|
+
bind:value={activeSearchTerm}
|
|
698
|
+
onkeydown={handleSearchKeydown}
|
|
699
|
+
/>
|
|
700
|
+
{/if}
|
|
701
|
+
</div>
|
|
702
|
+
</div>
|
|
703
|
+
</header>
|