triiiceratops 0.11.1 → 0.12.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/README.md +17 -9
- package/dist/{ArrowCounterClockwise-CN8KGaI0.js → ArrowCounterClockwise-CM9mGGcp.js} +1 -1
- package/dist/X-Bn7S7vUL.js +963 -0
- package/dist/{annotation_tool_point-BpZXtX5D.js → annotation_tool_point-LoRp_nrI.js} +1 -1
- package/dist/annotorious-openseadragon.es-tb5X-LtF.js +33045 -0
- package/dist/components/AnnotationOverlay.svelte +10 -17
- package/dist/components/DemoHeader.svelte +73 -5
- package/dist/components/MetadataDialog.svelte +4 -1
- package/dist/components/OSDViewer.svelte +39 -3
- package/dist/components/SearchPanel.svelte +8 -5
- package/dist/components/ThemeToggle.svelte +1 -1
- package/dist/components/ThumbnailGallery.svelte +229 -38
- package/dist/components/Toolbar.svelte +105 -6
- package/dist/components/TriiiceratopsViewer.svelte +37 -12
- package/dist/components/TriiiceratopsViewerElement.svelte +3 -1
- package/dist/custom-element.js +1 -0
- package/dist/{image_filters_reset-CyWg622b.js → image_filters_reset-CmWuQiOc.js} +1 -1
- package/dist/paraglide/messages/_index.d.ts +9 -0
- package/dist/paraglide/messages/_index.js +10 -1
- package/dist/paraglide/messages/settings_toggle_show_viewing_mode.d.ts +4 -0
- package/dist/paraglide/messages/settings_toggle_show_viewing_mode.js +33 -0
- package/dist/paraglide/messages/show_mode_toggle.d.ts +4 -0
- package/dist/paraglide/messages/show_mode_toggle.js +33 -0
- package/dist/paraglide/messages/toggle_single_page_mode.d.ts +4 -0
- package/dist/paraglide/messages/toggle_single_page_mode.js +33 -0
- package/dist/paraglide/messages/toggle_two_page_mode.d.ts +4 -0
- package/dist/paraglide/messages/toggle_two_page_mode.js +33 -0
- package/dist/paraglide/messages/two_page_mode.d.ts +4 -0
- package/dist/paraglide/messages/two_page_mode.js +33 -0
- package/dist/paraglide/messages/viewing_mode_individuals.d.ts +4 -0
- package/dist/paraglide/messages/viewing_mode_individuals.js +33 -0
- package/dist/paraglide/messages/viewing_mode_label.d.ts +4 -0
- package/dist/paraglide/messages/viewing_mode_label.js +33 -0
- package/dist/paraglide/messages/viewing_mode_paged.d.ts +4 -0
- package/dist/paraglide/messages/viewing_mode_paged.js +33 -0
- package/dist/paraglide/messages/viewing_mode_shift_pairing.d.ts +4 -0
- package/dist/paraglide/messages/viewing_mode_shift_pairing.js +33 -0
- package/dist/plugins/annotation-editor/AnnotationEditorController.svelte +5 -3
- package/dist/plugins/annotation-editor/AnnotationEditorPanel.svelte +3 -3
- package/dist/plugins/annotation-editor/AnnotationManager.svelte.d.ts +3 -0
- package/dist/plugins/annotation-editor/AnnotationManager.svelte.js +19 -14
- package/dist/plugins/annotation-editor/loader.svelte.js +2 -2
- package/dist/plugins/annotation-editor.js +1228 -32159
- package/dist/plugins/image-manipulation/ImageManipulationController.svelte +1 -1
- package/dist/plugins/image-manipulation.js +3 -3
- package/dist/state/manifests.svelte.d.ts +2 -1
- package/dist/state/manifests.svelte.js +5 -9
- package/dist/state/manifests.test.js +52 -50
- package/dist/state/viewer.svelte.d.ts +20 -1
- package/dist/state/viewer.svelte.js +150 -16
- package/dist/triiiceratops-bundle.js +3107 -2584
- package/dist/triiiceratops-element.iife.js +26 -26
- package/dist/triiiceratops.css +1 -1
- package/dist/types/config.d.ts +33 -0
- package/dist/utils/annotationAdapter.js +2 -2
- package/dist/utils/annotationAdapter.test.js +0 -1
- package/package.json +12 -2
- package/dist/X-i_EmjXwW.js +0 -906
|
@@ -61,6 +61,28 @@
|
|
|
61
61
|
};
|
|
62
62
|
let galleryElement: HTMLElement | null = $state(null);
|
|
63
63
|
|
|
64
|
+
// Initialize position and size from config if available (only once on mount)
|
|
65
|
+
$effect(() => {
|
|
66
|
+
if (
|
|
67
|
+
viewerState.config.gallery?.width &&
|
|
68
|
+
viewerState.config.gallery?.height
|
|
69
|
+
) {
|
|
70
|
+
viewerState.gallerySize = {
|
|
71
|
+
width: viewerState.config.gallery.width,
|
|
72
|
+
height: viewerState.config.gallery.height,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (
|
|
76
|
+
viewerState.config.gallery?.x !== undefined &&
|
|
77
|
+
viewerState.config.gallery?.y !== undefined
|
|
78
|
+
) {
|
|
79
|
+
viewerState.galleryPosition = {
|
|
80
|
+
x: viewerState.config.gallery.x,
|
|
81
|
+
y: viewerState.config.gallery.y,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
64
86
|
// Generate thumbnail data
|
|
65
87
|
let thumbnails = $derived.by(() => {
|
|
66
88
|
if (!canvases || !Array.isArray(canvases))
|
|
@@ -83,8 +105,8 @@
|
|
|
83
105
|
: thumb.id || thumb['@id'];
|
|
84
106
|
}
|
|
85
107
|
}
|
|
86
|
-
} catch
|
|
87
|
-
console.warn('Error getting thumbnail'
|
|
108
|
+
} catch {
|
|
109
|
+
console.warn('Error getting thumbnail');
|
|
88
110
|
}
|
|
89
111
|
|
|
90
112
|
// Fallback to first image if no thumbnail service
|
|
@@ -169,7 +191,7 @@
|
|
|
169
191
|
(pObj['@id'] as string | undefined) ||
|
|
170
192
|
JSON.stringify(pObj);
|
|
171
193
|
}
|
|
172
|
-
} catch
|
|
194
|
+
} catch {
|
|
173
195
|
// ignore
|
|
174
196
|
}
|
|
175
197
|
|
|
@@ -322,7 +344,26 @@
|
|
|
322
344
|
}
|
|
323
345
|
|
|
324
346
|
function selectCanvas(canvasId: string) {
|
|
325
|
-
viewerState.
|
|
347
|
+
if (viewerState.viewingMode === 'paged') {
|
|
348
|
+
const canvasIndex = thumbnails.findIndex((t) => t.id === canvasId);
|
|
349
|
+
const singlePages = viewerState.pagedOffset;
|
|
350
|
+
// If within single pages section, select directly
|
|
351
|
+
if (canvasIndex < singlePages) {
|
|
352
|
+
viewerState.setCanvas(canvasId);
|
|
353
|
+
} else {
|
|
354
|
+
// Check if this is a left-hand page (start of a pair)
|
|
355
|
+
const pairPosition = (canvasIndex - singlePages) % 2;
|
|
356
|
+
if (pairPosition === 0) {
|
|
357
|
+
viewerState.setCanvas(canvasId);
|
|
358
|
+
} else {
|
|
359
|
+
// Right-hand page, select the left page of this pair
|
|
360
|
+
const prevCanvas = thumbnails[canvasIndex - 1];
|
|
361
|
+
viewerState.setCanvas(prevCanvas.id);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
} else {
|
|
365
|
+
viewerState.setCanvas(canvasId);
|
|
366
|
+
}
|
|
326
367
|
}
|
|
327
368
|
|
|
328
369
|
// State for docking
|
|
@@ -384,6 +425,8 @@
|
|
|
384
425
|
(dockSide === 'none' && viewerState.gallerySize.height < 320),
|
|
385
426
|
);
|
|
386
427
|
|
|
428
|
+
let fixedHeight = $derived(viewerState.galleryFixedHeight);
|
|
429
|
+
|
|
387
430
|
function startDrag(e: MouseEvent) {
|
|
388
431
|
if (!draggable) return; // Dragging disabled in config
|
|
389
432
|
if ((e.target as HTMLElement).closest('.resize-handle')) return; // Don't drag if resizing
|
|
@@ -444,6 +487,53 @@
|
|
|
444
487
|
dockSide = 'none';
|
|
445
488
|
}
|
|
446
489
|
}
|
|
490
|
+
|
|
491
|
+
// Grouped thumbnail mode (for two-page mode)
|
|
492
|
+
const groupedThumbnailIndices = $derived.by(() => {
|
|
493
|
+
const indices: number[] = [];
|
|
494
|
+
if (viewerState.viewingMode === 'paged' && canvases) {
|
|
495
|
+
// Single pages at the start: pagedOffset (default 0, shifted = 1)
|
|
496
|
+
const singlePages = viewerState.pagedOffset;
|
|
497
|
+
// Add indices for single pages
|
|
498
|
+
for (let i = 0; i < singlePages && i < canvases.length; i++) {
|
|
499
|
+
indices.push(i);
|
|
500
|
+
}
|
|
501
|
+
// Add indices for paired pages (step by 2 starting from singlePages)
|
|
502
|
+
for (let i = singlePages; i < canvases.length; i += 2) {
|
|
503
|
+
indices.push(i);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return indices;
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
const groupedThumbnails = $derived.by(() => {
|
|
510
|
+
const groups: Array<{
|
|
511
|
+
id: string;
|
|
512
|
+
label: string;
|
|
513
|
+
srcs: string[];
|
|
514
|
+
index: number;
|
|
515
|
+
}> = [];
|
|
516
|
+
const thumbs = thumbnails;
|
|
517
|
+
const singlePages = viewerState.pagedOffset;
|
|
518
|
+
for (const i of groupedThumbnailIndices) {
|
|
519
|
+
const first = thumbs[i];
|
|
520
|
+
// Only pair if we're past the single pages section
|
|
521
|
+
const second = i < singlePages ? null : thumbs[i + 1];
|
|
522
|
+
const groupId = first.id;
|
|
523
|
+
const groupLabel = first.label;
|
|
524
|
+
const groupSrcs = [first.src];
|
|
525
|
+
if (second) {
|
|
526
|
+
groupSrcs.push(second.src);
|
|
527
|
+
}
|
|
528
|
+
groups.push({
|
|
529
|
+
id: groupId,
|
|
530
|
+
label: groupLabel,
|
|
531
|
+
srcs: groupSrcs,
|
|
532
|
+
index: i,
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
return groups;
|
|
536
|
+
});
|
|
447
537
|
</script>
|
|
448
538
|
|
|
449
539
|
{#if viewerState.showThumbnailGallery}
|
|
@@ -514,44 +604,145 @@
|
|
|
514
604
|
: 'grid gap-2'}
|
|
515
605
|
style={isHorizontal
|
|
516
606
|
? ''
|
|
517
|
-
:
|
|
607
|
+
: `grid-template-columns: repeat(auto-fill, minmax(${fixedHeight}px, 1fr));`}
|
|
518
608
|
>
|
|
519
|
-
{#
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
:
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
609
|
+
{#if viewerState.viewingMode === 'paged'}
|
|
610
|
+
<!-- grouped thumbnail display -->
|
|
611
|
+
{#each groupedThumbnails as thumbGroup}
|
|
612
|
+
<button
|
|
613
|
+
class="group flex flex-col gap-1 p-1 rounded hover:bg-base-200 transition-colors text-left relative shrink-0 {isHorizontal
|
|
614
|
+
? 'w-auto'
|
|
615
|
+
: thumbGroup.srcs.length > 1
|
|
616
|
+
? 'col-span-2'
|
|
617
|
+
: ''} {viewerState.canvasId === thumbGroup.id
|
|
618
|
+
? 'ring-2 ring-primary bg-primary/5'
|
|
619
|
+
: ''}"
|
|
620
|
+
style={isHorizontal
|
|
621
|
+
? `height: ${fixedHeight + 24}px`
|
|
622
|
+
: ''}
|
|
623
|
+
onclick={() => selectCanvas(thumbGroup.id)}
|
|
624
|
+
data-id={thumbGroup.id}
|
|
625
|
+
aria-label="Select canvas {thumbGroup.label}"
|
|
532
626
|
>
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
627
|
+
<div
|
|
628
|
+
class="{isHorizontal
|
|
629
|
+
? 'h-full w-auto flex-row'
|
|
630
|
+
: thumbGroup.srcs.length > 1
|
|
631
|
+
? 'aspect-3/2 w-full'
|
|
632
|
+
: 'aspect-3/4 w-full'} bg-base-300 rounded overflow-hidden relative flex items-center justify-center gap-px"
|
|
633
|
+
style={isHorizontal
|
|
634
|
+
? `height: ${fixedHeight}px`
|
|
635
|
+
: ''}
|
|
636
|
+
>
|
|
637
|
+
<div
|
|
638
|
+
class="flex items-center justify-center overflow-hidden {isHorizontal
|
|
639
|
+
? 'h-full w-auto'
|
|
640
|
+
: 'h-full ' +
|
|
641
|
+
(thumbGroup.srcs.length > 1
|
|
642
|
+
? 'w-1/2'
|
|
643
|
+
: 'w-full')}"
|
|
644
|
+
>
|
|
645
|
+
{#if thumbGroup.srcs[0]}
|
|
646
|
+
<img
|
|
647
|
+
src={thumbGroup.srcs[0]}
|
|
648
|
+
alt={thumbGroup.label}
|
|
649
|
+
class="object-contain {isHorizontal
|
|
650
|
+
? 'h-full w-auto'
|
|
651
|
+
: 'w-full h-full'} {thumbGroup
|
|
652
|
+
.srcs.length > 1
|
|
653
|
+
? 'object-right'
|
|
654
|
+
: 'object-center'}"
|
|
655
|
+
loading="lazy"
|
|
656
|
+
draggable="false"
|
|
657
|
+
/>
|
|
658
|
+
{:else}
|
|
659
|
+
<span class="opacity-20 text-4xl"
|
|
660
|
+
>?</span
|
|
661
|
+
>
|
|
662
|
+
{/if}
|
|
663
|
+
</div>
|
|
664
|
+
{#if thumbGroup.srcs.length > 1}
|
|
665
|
+
<div
|
|
666
|
+
class="flex items-center justify-center overflow-hidden {isHorizontal
|
|
667
|
+
? 'h-full w-auto'
|
|
668
|
+
: 'h-full w-1/2'}"
|
|
669
|
+
>
|
|
670
|
+
{#if thumbGroup.srcs[1]}
|
|
671
|
+
<img
|
|
672
|
+
src={thumbGroup.srcs[1]}
|
|
673
|
+
alt={thumbGroup.label}
|
|
674
|
+
class="object-contain {isHorizontal
|
|
675
|
+
? 'h-full w-auto'
|
|
676
|
+
: 'w-full h-full'} object-left"
|
|
677
|
+
loading="lazy"
|
|
678
|
+
draggable="false"
|
|
679
|
+
/>
|
|
680
|
+
{:else}
|
|
681
|
+
<span class="opacity-20 text-4xl"
|
|
682
|
+
>?</span
|
|
683
|
+
>
|
|
684
|
+
{/if}
|
|
685
|
+
</div>
|
|
686
|
+
{/if}
|
|
687
|
+
</div>
|
|
688
|
+
<div
|
|
689
|
+
class="text-xs font-medium truncate w-full opacity-70 group-hover:opacity-100"
|
|
690
|
+
>
|
|
691
|
+
<span class="font-bold mr-1"
|
|
692
|
+
>{thumbGroup.index + 1}.</span
|
|
693
|
+
>
|
|
694
|
+
{thumbGroup.label}
|
|
695
|
+
</div>
|
|
696
|
+
</button>
|
|
697
|
+
{/each}
|
|
698
|
+
{:else}
|
|
699
|
+
{#each thumbnails as thumb}
|
|
700
|
+
<button
|
|
701
|
+
class="group flex flex-col gap-1 p-1 rounded hover:bg-base-200 transition-colors text-left relative shrink-0 {isHorizontal
|
|
702
|
+
? 'w-auto'
|
|
703
|
+
: ''} {viewerState.canvasId === thumb.id
|
|
704
|
+
? 'ring-2 ring-primary bg-primary/5'
|
|
705
|
+
: ''}"
|
|
706
|
+
style={isHorizontal
|
|
707
|
+
? `height: ${fixedHeight + 24}px`
|
|
708
|
+
: ''}
|
|
709
|
+
onclick={() => selectCanvas(thumb.id)}
|
|
710
|
+
data-id={thumb.id}
|
|
711
|
+
aria-label="Select canvas {thumb.label}"
|
|
547
712
|
>
|
|
548
|
-
<
|
|
549
|
-
|
|
713
|
+
<div
|
|
714
|
+
class="{isHorizontal
|
|
715
|
+
? 'h-full w-auto'
|
|
716
|
+
: 'aspect-3/4 w-full'} bg-base-300 rounded overflow-hidden relative flex items-center justify-center"
|
|
717
|
+
style={isHorizontal
|
|
718
|
+
? `height: ${fixedHeight}px`
|
|
719
|
+
: ''}
|
|
720
|
+
>
|
|
721
|
+
{#if thumb.src}
|
|
722
|
+
<img
|
|
723
|
+
src={thumb.src}
|
|
724
|
+
alt={thumb.label}
|
|
725
|
+
class="object-contain {isHorizontal
|
|
726
|
+
? 'h-full w-auto'
|
|
727
|
+
: 'w-full h-full'}"
|
|
728
|
+
loading="lazy"
|
|
729
|
+
draggable="false"
|
|
730
|
+
/>
|
|
731
|
+
{:else}
|
|
732
|
+
<span class="opacity-20 text-4xl">?</span>
|
|
733
|
+
{/if}
|
|
734
|
+
</div>
|
|
735
|
+
<div
|
|
736
|
+
class="text-xs font-medium truncate w-full opacity-70 group-hover:opacity-100"
|
|
550
737
|
>
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
738
|
+
<span class="font-bold mr-1"
|
|
739
|
+
>{thumb.index + 1}.</span
|
|
740
|
+
>
|
|
741
|
+
{thumb.label}
|
|
742
|
+
</div>
|
|
743
|
+
</button>
|
|
744
|
+
{/each}
|
|
745
|
+
{/if}
|
|
555
746
|
</div>
|
|
556
747
|
</div>
|
|
557
748
|
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
import ChatCenteredText from 'phosphor-svelte/lib/ChatCenteredText';
|
|
8
8
|
import Info from 'phosphor-svelte/lib/Info';
|
|
9
9
|
import List from 'phosphor-svelte/lib/List';
|
|
10
|
+
import BookOpen from 'phosphor-svelte/lib/BookOpen';
|
|
11
|
+
import Scroll from 'phosphor-svelte/lib/Scroll';
|
|
10
12
|
import X from 'phosphor-svelte/lib/X';
|
|
11
13
|
import { VIEWER_STATE_KEY, type ViewerState } from '../state/viewer.svelte';
|
|
12
14
|
import { m, language } from '../state/i18n.svelte';
|
|
@@ -34,10 +36,11 @@
|
|
|
34
36
|
const showFullscreen = $derived(toolbarConfig.showFullscreen !== false);
|
|
35
37
|
const showAnnotations = $derived(toolbarConfig.showAnnotations !== false);
|
|
36
38
|
const showInfo = $derived(toolbarConfig.showInfo !== false);
|
|
39
|
+
const showViewingMode = $derived(toolbarConfig.showViewingMode !== false);
|
|
37
40
|
|
|
38
41
|
// Derived list of sorted plugin buttons
|
|
39
42
|
let sortedPluginButtons = $derived.by(() => {
|
|
40
|
-
language.current;
|
|
43
|
+
void language.current;
|
|
41
44
|
return [...viewerState.pluginMenuButtons].sort(
|
|
42
45
|
(a, b) => (a.order ?? 100) - (b.order ?? 100),
|
|
43
46
|
);
|
|
@@ -46,6 +49,18 @@
|
|
|
46
49
|
function toggleOpen() {
|
|
47
50
|
viewerState.toggleToolbar();
|
|
48
51
|
}
|
|
52
|
+
|
|
53
|
+
let isOverflowVisible = $state(false);
|
|
54
|
+
$effect(() => {
|
|
55
|
+
if (isOpen) {
|
|
56
|
+
const timer = setTimeout(() => {
|
|
57
|
+
isOverflowVisible = true;
|
|
58
|
+
}, 320); // Slightly longer than 300ms to ensure transition is done
|
|
59
|
+
return () => clearTimeout(timer);
|
|
60
|
+
} else {
|
|
61
|
+
isOverflowVisible = false;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
49
64
|
</script>
|
|
50
65
|
|
|
51
66
|
<div
|
|
@@ -60,7 +75,7 @@
|
|
|
60
75
|
<!-- Collapsible Toolbar -->
|
|
61
76
|
<div
|
|
62
77
|
class={[
|
|
63
|
-
'pointer-events-auto bg-base-100/95 backdrop-blur shadow-xl transition-all duration-300 ease-in-out flex
|
|
78
|
+
'pointer-events-auto bg-base-100/95 backdrop-blur shadow-xl transition-all duration-300 ease-in-out flex',
|
|
64
79
|
// Layout based on position
|
|
65
80
|
isTop &&
|
|
66
81
|
'flex-row-reverse h-12 w-auto max-w-full rounded-b-xl border-x border-b border-base-200 origin-top',
|
|
@@ -76,6 +91,9 @@
|
|
|
76
91
|
!isOpen && !isTop && 'w-0 opacity-0',
|
|
77
92
|
!isOpen && !isTop && isLeft && '-translate-x-full',
|
|
78
93
|
!isOpen && !isTop && !isLeft && 'translate-x-full',
|
|
94
|
+
|
|
95
|
+
// Overflow handling
|
|
96
|
+
isOverflowVisible ? 'overflow-visible' : 'overflow-hidden',
|
|
79
97
|
]}
|
|
80
98
|
>
|
|
81
99
|
<!-- Close Button (Inside Menu) -->
|
|
@@ -104,13 +122,19 @@
|
|
|
104
122
|
<div class={isTop ? 'w-2' : 'h-2'}></div>
|
|
105
123
|
{/if}
|
|
106
124
|
|
|
125
|
+
<!-- Scrollable Actions -->
|
|
107
126
|
<ul
|
|
108
127
|
class={[
|
|
109
128
|
'menu menu-md gap-2 flex-nowrap items-center min-h-0',
|
|
129
|
+
isTop && 'px-2 py-1 menu-horizontal w-auto flex-row-reverse',
|
|
110
130
|
isTop &&
|
|
111
|
-
|
|
131
|
+
!isOverflowVisible &&
|
|
132
|
+
'overflow-x-auto overflow-y-hidden',
|
|
133
|
+
isTop && isOverflowVisible && 'overflow-visible',
|
|
112
134
|
!isTop &&
|
|
135
|
+
!isOverflowVisible &&
|
|
113
136
|
'py-2 px-1 flex-1 overflow-y-auto overflow-x-hidden w-12',
|
|
137
|
+
!isTop && isOverflowVisible && 'py-2 px-1 flex-1 w-12',
|
|
114
138
|
]}
|
|
115
139
|
>
|
|
116
140
|
<!-- --- Standard Actions --- -->
|
|
@@ -157,6 +181,81 @@
|
|
|
157
181
|
</li>
|
|
158
182
|
{/if}
|
|
159
183
|
|
|
184
|
+
{#if showViewingMode}
|
|
185
|
+
<li
|
|
186
|
+
class="dropdown {isTop
|
|
187
|
+
? 'dropdown-bottom'
|
|
188
|
+
: isLeft
|
|
189
|
+
? 'dropdown-right'
|
|
190
|
+
: 'dropdown-left'}"
|
|
191
|
+
>
|
|
192
|
+
<div
|
|
193
|
+
tabindex="0"
|
|
194
|
+
role="button"
|
|
195
|
+
class="flex items-center justify-center"
|
|
196
|
+
use:tooltip={{
|
|
197
|
+
content: m.viewing_mode_label(),
|
|
198
|
+
position: tooltipPos,
|
|
199
|
+
}}
|
|
200
|
+
aria-label={m.viewing_mode_label()}
|
|
201
|
+
>
|
|
202
|
+
{#if viewerState.viewingMode === 'paged'}
|
|
203
|
+
<BookOpen size={24} weight="bold" />
|
|
204
|
+
{:else}
|
|
205
|
+
<Scroll size={24} weight="bold" />
|
|
206
|
+
{/if}
|
|
207
|
+
</div>
|
|
208
|
+
<ul
|
|
209
|
+
tabindex="-1"
|
|
210
|
+
class="dropdown-content z-50 menu p-2 shadow bg-base-100 rounded-box w-48 border border-base-200 font-normal {isTop
|
|
211
|
+
? 'left-1/2 -translate-x-1/2'
|
|
212
|
+
: ''}"
|
|
213
|
+
>
|
|
214
|
+
<li>
|
|
215
|
+
<button
|
|
216
|
+
class={viewerState.viewingMode === 'individuals'
|
|
217
|
+
? 'active'
|
|
218
|
+
: ''}
|
|
219
|
+
onclick={() =>
|
|
220
|
+
viewerState.setViewingMode('individuals')}
|
|
221
|
+
>
|
|
222
|
+
<Scroll size={16} />
|
|
223
|
+
{m.viewing_mode_individuals()}
|
|
224
|
+
</button>
|
|
225
|
+
</li>
|
|
226
|
+
<li>
|
|
227
|
+
<button
|
|
228
|
+
class={viewerState.viewingMode === 'paged'
|
|
229
|
+
? 'active'
|
|
230
|
+
: ''}
|
|
231
|
+
onclick={() =>
|
|
232
|
+
viewerState.setViewingMode('paged')}
|
|
233
|
+
>
|
|
234
|
+
<BookOpen size={16} />
|
|
235
|
+
{m.viewing_mode_paged()}
|
|
236
|
+
</button>
|
|
237
|
+
</li>
|
|
238
|
+
{#if viewerState.viewingMode === 'paged'}
|
|
239
|
+
<div class="divider my-1"></div>
|
|
240
|
+
<li>
|
|
241
|
+
<label class="label cursor-pointer py-1 gap-2">
|
|
242
|
+
<span class="label-text text-sm"
|
|
243
|
+
>{m.viewing_mode_shift_pairing()}</span
|
|
244
|
+
>
|
|
245
|
+
<input
|
|
246
|
+
type="checkbox"
|
|
247
|
+
class="checkbox checkbox-sm"
|
|
248
|
+
checked={viewerState.pagedOffset === 1}
|
|
249
|
+
onchange={() =>
|
|
250
|
+
viewerState.togglePagedOffset()}
|
|
251
|
+
/>
|
|
252
|
+
</label>
|
|
253
|
+
</li>
|
|
254
|
+
{/if}
|
|
255
|
+
</ul>
|
|
256
|
+
</li>
|
|
257
|
+
{/if}
|
|
258
|
+
|
|
160
259
|
{#if showFullscreen}
|
|
161
260
|
<li>
|
|
162
261
|
<button
|
|
@@ -227,7 +326,7 @@
|
|
|
227
326
|
{/if}
|
|
228
327
|
|
|
229
328
|
<!-- Separator if both groups exist -->
|
|
230
|
-
{#if (showSearch || showGallery || showFullscreen || showAnnotations || showInfo) && sortedPluginButtons.length > 0}
|
|
329
|
+
{#if (showSearch || showGallery || showFullscreen || showAnnotations || showInfo || showViewingMode) && sortedPluginButtons.length > 0}
|
|
231
330
|
<div
|
|
232
331
|
class={[
|
|
233
332
|
'divider',
|
|
@@ -241,9 +340,9 @@
|
|
|
241
340
|
{#each sortedPluginButtons as button (button.id)}
|
|
242
341
|
{@const Icon = button.icon}
|
|
243
342
|
{@const tooltipText =
|
|
244
|
-
// @ts-
|
|
343
|
+
// @ts-expect-error - m[button.tooltip] might be a function
|
|
245
344
|
typeof m[button.tooltip] === 'function'
|
|
246
|
-
? // @ts-
|
|
345
|
+
? // @ts-expect-error - m[button.tooltip] is a function
|
|
247
346
|
m[button.tooltip]()
|
|
248
347
|
: button.tooltip}
|
|
249
348
|
<li>
|
|
@@ -234,6 +234,19 @@
|
|
|
234
234
|
|
|
235
235
|
// Use Manifesto to get images
|
|
236
236
|
let images = canvas.getImages();
|
|
237
|
+
if (internalViewerState.viewingMode === 'paged') {
|
|
238
|
+
// Single pages at the start: pagedOffset (default 0, shifted = 1)
|
|
239
|
+
const singlePages = internalViewerState.pagedOffset;
|
|
240
|
+
// Only show two-page spread if we're past the single pages section
|
|
241
|
+
if (currentCanvasIndex >= singlePages) {
|
|
242
|
+
const nextIndex = currentCanvasIndex + 1;
|
|
243
|
+
if (nextIndex < canvases.length) {
|
|
244
|
+
const nextCanvas = canvases[nextIndex];
|
|
245
|
+
const nextImages = nextCanvas.getImages();
|
|
246
|
+
images = images.concat(nextImages);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
237
250
|
|
|
238
251
|
// Fallback for IIIF v3: iterate content if images is empty
|
|
239
252
|
if ((!images || !images.length) && canvas.getContent) {
|
|
@@ -251,7 +264,14 @@
|
|
|
251
264
|
return null;
|
|
252
265
|
}
|
|
253
266
|
|
|
254
|
-
|
|
267
|
+
// Map images to tile sources, in two page mode, this will get two image sources
|
|
268
|
+
const tileSourcesArray = images.map((annotation: any) =>
|
|
269
|
+
getImageService(annotation),
|
|
270
|
+
);
|
|
271
|
+
return tileSourcesArray;
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
function getImageService(annotation: any) {
|
|
255
275
|
let resource = annotation.getResource ? annotation.getResource() : null;
|
|
256
276
|
|
|
257
277
|
// v3 fallback: getBody
|
|
@@ -357,7 +377,7 @@
|
|
|
357
377
|
);
|
|
358
378
|
const url = resourceId;
|
|
359
379
|
return { type: 'image', url };
|
|
360
|
-
}
|
|
380
|
+
}
|
|
361
381
|
</script>
|
|
362
382
|
|
|
363
383
|
<div
|
|
@@ -385,7 +405,11 @@
|
|
|
385
405
|
|
|
386
406
|
<!-- Gallery (when docked left) -->
|
|
387
407
|
{#if internalViewerState.showThumbnailGallery && internalViewerState.dockSide === 'left'}
|
|
388
|
-
<div
|
|
408
|
+
<div
|
|
409
|
+
class="h-full pointer-events-auto relative"
|
|
410
|
+
style="width: {internalViewerState.galleryFixedHeight +
|
|
411
|
+
40}px"
|
|
412
|
+
>
|
|
389
413
|
<ThumbnailGallery {canvases} />
|
|
390
414
|
</div>
|
|
391
415
|
{/if}
|
|
@@ -411,7 +435,8 @@
|
|
|
411
435
|
<!-- Top Area (Gallery) -->
|
|
412
436
|
{#if internalViewerState.showThumbnailGallery && internalViewerState.dockSide === 'top'}
|
|
413
437
|
<div
|
|
414
|
-
class="flex-none
|
|
438
|
+
class="flex-none w-full pointer-events-auto relative z-20"
|
|
439
|
+
style="height: {internalViewerState.galleryFixedHeight + 50}px"
|
|
415
440
|
>
|
|
416
441
|
<ThumbnailGallery {canvases} />
|
|
417
442
|
</div>
|
|
@@ -438,12 +463,7 @@
|
|
|
438
463
|
{manifestData.error}
|
|
439
464
|
</div>
|
|
440
465
|
{:else if tileSources}
|
|
441
|
-
{
|
|
442
|
-
<OSDViewer
|
|
443
|
-
{tileSources}
|
|
444
|
-
viewerState={internalViewerState}
|
|
445
|
-
/>
|
|
446
|
-
{/key}
|
|
466
|
+
<OSDViewer {tileSources} viewerState={internalViewerState} />
|
|
447
467
|
{:else if manifestData && !manifestData.isFetching && !tileSources}
|
|
448
468
|
<div
|
|
449
469
|
class="w-full h-full flex items-center justify-center text-base-content/50"
|
|
@@ -484,7 +504,8 @@
|
|
|
484
504
|
<!-- Bottom Area (Gallery) -->
|
|
485
505
|
{#if internalViewerState.showThumbnailGallery && internalViewerState.dockSide === 'bottom'}
|
|
486
506
|
<div
|
|
487
|
-
class="flex-none
|
|
507
|
+
class="flex-none w-full pointer-events-auto relative z-20"
|
|
508
|
+
style="height: {internalViewerState.galleryFixedHeight + 50}px"
|
|
488
509
|
>
|
|
489
510
|
<ThumbnailGallery {canvases} />
|
|
490
511
|
</div>
|
|
@@ -520,7 +541,11 @@
|
|
|
520
541
|
|
|
521
542
|
<!-- Gallery (when docked right) -->
|
|
522
543
|
{#if internalViewerState.showThumbnailGallery && internalViewerState.dockSide === 'right'}
|
|
523
|
-
<div
|
|
544
|
+
<div
|
|
545
|
+
class="h-full pointer-events-auto relative"
|
|
546
|
+
style="width: {internalViewerState.galleryFixedHeight +
|
|
547
|
+
40}px"
|
|
548
|
+
>
|
|
524
549
|
<ThumbnailGallery {canvases} />
|
|
525
550
|
</div>
|
|
526
551
|
{/if}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<!-- svelte-ignore options_missing_custom_element -->
|
|
1
2
|
<svelte:options
|
|
2
3
|
customElement={{
|
|
3
4
|
tag: 'triiiceratops-viewer',
|
|
@@ -115,7 +116,7 @@
|
|
|
115
116
|
if (typeof config === 'string') {
|
|
116
117
|
try {
|
|
117
118
|
return JSON.parse(config);
|
|
118
|
-
} catch
|
|
119
|
+
} catch {
|
|
119
120
|
console.warn(`Invalid config JSON: "${config}". Ignoring.`);
|
|
120
121
|
return undefined;
|
|
121
122
|
}
|
|
@@ -124,6 +125,7 @@
|
|
|
124
125
|
});
|
|
125
126
|
</script>
|
|
126
127
|
|
|
128
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
127
129
|
{@html `<style>${styles}</style>`}
|
|
128
130
|
|
|
129
131
|
<div bind:this={hostElement} class="w-full h-full">
|
package/dist/custom-element.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// Plugins built as IIFE need to share the same Svelte runtime instance
|
|
5
5
|
// so that getContext/setContext work correctly across bundle boundaries
|
|
6
6
|
// @ts-expect-error - svelte/internal/client is not typed but exists at runtime
|
|
7
|
+
// eslint-disable-next-line svelte/no-svelte-internal
|
|
7
8
|
import * as svelteInternal from 'svelte/internal/client';
|
|
8
9
|
import * as svelte from 'svelte';
|
|
9
10
|
window.__TriiiceratopsSvelteRuntime = {
|
|
@@ -121,4 +121,13 @@ export * from "./image_filters_reset.js";
|
|
|
121
121
|
export * from "./annotation_tool_rectangle.js";
|
|
122
122
|
export * from "./annotation_tool_polygon.js";
|
|
123
123
|
export * from "./annotation_tool_point.js";
|
|
124
|
+
export * from "./viewing_mode_label.js";
|
|
125
|
+
export * from "./viewing_mode_individuals.js";
|
|
126
|
+
export * from "./viewing_mode_paged.js";
|
|
127
|
+
export * from "./viewing_mode_shift_pairing.js";
|
|
128
|
+
export * from "./settings_toggle_show_viewing_mode.js";
|
|
129
|
+
export * from "./toggle_two_page_mode.js";
|
|
130
|
+
export * from "./toggle_single_page_mode.js";
|
|
131
|
+
export * from "./show_mode_toggle.js";
|
|
132
|
+
export * from "./two_page_mode.js";
|
|
124
133
|
export type LocalizedString = import("../runtime.js").LocalizedString;
|