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,9 @@
|
|
|
1
|
+
declare const DemoHeader: import("svelte").Component<{
|
|
2
|
+
manifestUrl?: any;
|
|
3
|
+
onLoad: any;
|
|
4
|
+
viewerMode?: string;
|
|
5
|
+
canvasId?: string;
|
|
6
|
+
config?: Record<string, any>;
|
|
7
|
+
}, {}, "canvasId" | "config" | "manifestUrl" | "viewerMode">;
|
|
8
|
+
type DemoHeader = ReturnType<typeof DemoHeader>;
|
|
9
|
+
export default DemoHeader;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
|
+
import ChatCenteredText from 'phosphor-svelte/lib/ChatCenteredText';
|
|
4
|
+
import CornersIn from 'phosphor-svelte/lib/CornersIn';
|
|
5
|
+
import CornersOut from 'phosphor-svelte/lib/CornersOut';
|
|
6
|
+
import X from 'phosphor-svelte/lib/X';
|
|
7
|
+
import Info from 'phosphor-svelte/lib/Info';
|
|
8
|
+
import MagnifyingGlass from 'phosphor-svelte/lib/MagnifyingGlass';
|
|
9
|
+
import List from 'phosphor-svelte/lib/List';
|
|
10
|
+
import Slideshow from 'phosphor-svelte/lib/Slideshow';
|
|
11
|
+
import { VIEWER_STATE_KEY, type ViewerState } from '../state/viewer.svelte';
|
|
12
|
+
import { m } from '../state/i18n.svelte';
|
|
13
|
+
|
|
14
|
+
const viewerState = getContext<ViewerState>(VIEWER_STATE_KEY);
|
|
15
|
+
|
|
16
|
+
const menuConfig = $derived(viewerState.config.rightMenu || {});
|
|
17
|
+
|
|
18
|
+
const showSearch = $derived(menuConfig.showSearch !== false);
|
|
19
|
+
const showGallery = $derived(menuConfig.showGallery !== false);
|
|
20
|
+
const showFullscreen = $derived(menuConfig.showFullscreen !== false);
|
|
21
|
+
const showAnnotations = $derived(menuConfig.showAnnotations !== false);
|
|
22
|
+
const showInfo = $derived(menuConfig.showInfo !== false);
|
|
23
|
+
|
|
24
|
+
const activeButtons = $derived(
|
|
25
|
+
[
|
|
26
|
+
showSearch ? 'search' : null,
|
|
27
|
+
showGallery ? 'gallery' : null,
|
|
28
|
+
showFullscreen ? 'fullscreen' : null,
|
|
29
|
+
showAnnotations ? 'annotations' : null,
|
|
30
|
+
showInfo ? 'info' : null,
|
|
31
|
+
].filter((b): b is string => b !== null),
|
|
32
|
+
);
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
{#snippet searchBtn()}
|
|
36
|
+
<button
|
|
37
|
+
aria-label={m.toggle_search()}
|
|
38
|
+
class={[
|
|
39
|
+
'btn btn-circle btn-lg shadow-lg',
|
|
40
|
+
viewerState.showSearchPanel ? 'btn-primary' : 'btn-neutral',
|
|
41
|
+
]}
|
|
42
|
+
onclick={() => viewerState.toggleSearchPanel()}
|
|
43
|
+
>
|
|
44
|
+
<MagnifyingGlass size={28} weight="bold" />
|
|
45
|
+
</button>
|
|
46
|
+
{/snippet}
|
|
47
|
+
|
|
48
|
+
{#snippet galleryBtn()}
|
|
49
|
+
<button
|
|
50
|
+
aria-label={viewerState.showThumbnailGallery
|
|
51
|
+
? m.hide_gallery()
|
|
52
|
+
: m.show_gallery()}
|
|
53
|
+
class="btn btn-lg btn-circle shadow-lg {viewerState.showThumbnailGallery
|
|
54
|
+
? 'btn-info'
|
|
55
|
+
: 'btn-neutral'}"
|
|
56
|
+
onclick={() => viewerState.toggleThumbnailGallery()}
|
|
57
|
+
>
|
|
58
|
+
<Slideshow size={28} weight="bold" />
|
|
59
|
+
</button>
|
|
60
|
+
{/snippet}
|
|
61
|
+
|
|
62
|
+
{#snippet fullscreenBtn()}
|
|
63
|
+
<button
|
|
64
|
+
class="btn btn-circle btn-lg shadow-lg transition-all duration-300 ease-out {viewerState.isFullScreen
|
|
65
|
+
? 'btn-warning'
|
|
66
|
+
: 'btn-neutral'}"
|
|
67
|
+
onclick={() => viewerState.toggleFullScreen()}
|
|
68
|
+
>
|
|
69
|
+
{#if viewerState.isFullScreen}
|
|
70
|
+
<CornersIn size={28} weight="bold" />
|
|
71
|
+
{:else}
|
|
72
|
+
<CornersOut size={28} weight="bold" />
|
|
73
|
+
{/if}
|
|
74
|
+
</button>
|
|
75
|
+
{/snippet}
|
|
76
|
+
|
|
77
|
+
{#snippet annotationsBtn()}
|
|
78
|
+
<button
|
|
79
|
+
aria-label={m.toggle_annotations()}
|
|
80
|
+
class="btn btn-lg btn-circle shadow-lg {viewerState.showAnnotations
|
|
81
|
+
? 'btn-error'
|
|
82
|
+
: 'btn-neutral'}"
|
|
83
|
+
onclick={() => viewerState.toggleAnnotations()}
|
|
84
|
+
>
|
|
85
|
+
<ChatCenteredText size={28} weight="bold" />
|
|
86
|
+
</button>
|
|
87
|
+
{/snippet}
|
|
88
|
+
|
|
89
|
+
{#snippet infoBtn()}
|
|
90
|
+
<button
|
|
91
|
+
aria-label={m.toggle_metadata()}
|
|
92
|
+
class="btn btn-lg btn-circle shadow-lg {viewerState.showMetadataDialog
|
|
93
|
+
? 'btn-info'
|
|
94
|
+
: 'btn-neutral'}"
|
|
95
|
+
onclick={() => viewerState.toggleMetadataDialog()}
|
|
96
|
+
>
|
|
97
|
+
<Info size={28} weight="bold" />
|
|
98
|
+
</button>
|
|
99
|
+
{/snippet}
|
|
100
|
+
|
|
101
|
+
{#if activeButtons.length === 1}
|
|
102
|
+
<div class="absolute z-600 bottom-6 right-6">
|
|
103
|
+
{#if showSearch}
|
|
104
|
+
<div class="tooltip tooltip-left" data-tip={m.search()}>
|
|
105
|
+
{@render searchBtn()}
|
|
106
|
+
</div>
|
|
107
|
+
{/if}
|
|
108
|
+
{#if showGallery}
|
|
109
|
+
<div
|
|
110
|
+
class="tooltip tooltip-left"
|
|
111
|
+
data-tip={viewerState.showThumbnailGallery
|
|
112
|
+
? m.hide_gallery()
|
|
113
|
+
: m.show_gallery()}
|
|
114
|
+
>
|
|
115
|
+
{@render galleryBtn()}
|
|
116
|
+
</div>
|
|
117
|
+
{/if}
|
|
118
|
+
{#if showFullscreen}
|
|
119
|
+
<div
|
|
120
|
+
class="tooltip tooltip-left"
|
|
121
|
+
data-tip={viewerState.isFullScreen
|
|
122
|
+
? m.exit_full_screen()
|
|
123
|
+
: m.enter_full_screen()}
|
|
124
|
+
>
|
|
125
|
+
{@render fullscreenBtn()}
|
|
126
|
+
</div>
|
|
127
|
+
{/if}
|
|
128
|
+
{#if showAnnotations}
|
|
129
|
+
<div
|
|
130
|
+
class="tooltip tooltip-top"
|
|
131
|
+
data-tip={viewerState.showAnnotations
|
|
132
|
+
? m.hide_annotations()
|
|
133
|
+
: m.show_annotations()}
|
|
134
|
+
>
|
|
135
|
+
{@render annotationsBtn()}
|
|
136
|
+
</div>
|
|
137
|
+
{/if}
|
|
138
|
+
{#if showInfo}
|
|
139
|
+
<div class="tooltip tooltip-top" data-tip={m.metadata()}>
|
|
140
|
+
{@render infoBtn()}
|
|
141
|
+
</div>
|
|
142
|
+
{/if}
|
|
143
|
+
</div>
|
|
144
|
+
{:else if activeButtons.length > 1}
|
|
145
|
+
<div
|
|
146
|
+
class="fab fab-flower fab-top-left absolute z-600 pointer-events-auto bottom-6 right-6"
|
|
147
|
+
>
|
|
148
|
+
<!-- Trigger button: focusable div (Safari bug workaround) - hidden when FAB is open -->
|
|
149
|
+
<div
|
|
150
|
+
tabindex="0"
|
|
151
|
+
role="button"
|
|
152
|
+
class="btn btn-lg btn-primary btn-circle shadow-xl"
|
|
153
|
+
>
|
|
154
|
+
<span class="sr-only">{m.menu()}</span>
|
|
155
|
+
<List size={32} weight="bold" />
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<!-- fab-main-action: replaces the trigger button when FAB is open -->
|
|
159
|
+
<div class="fab-main-action tooltip tooltip-top" data-tip={m.search()}>
|
|
160
|
+
{@render searchBtn()}
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<!-- buttons that show up when FAB is open (max 4 for flower layout) -->
|
|
164
|
+
|
|
165
|
+
<!-- Gallery Toggle -->
|
|
166
|
+
{#if showGallery}
|
|
167
|
+
<div
|
|
168
|
+
class="tooltip tooltip-left"
|
|
169
|
+
data-tip={viewerState.showThumbnailGallery
|
|
170
|
+
? m.hide_gallery()
|
|
171
|
+
: m.show_gallery()}
|
|
172
|
+
>
|
|
173
|
+
{@render galleryBtn()}
|
|
174
|
+
</div>
|
|
175
|
+
{/if}
|
|
176
|
+
|
|
177
|
+
<!-- Full Screen Toggle -->
|
|
178
|
+
{#if showFullscreen}
|
|
179
|
+
<div
|
|
180
|
+
class="tooltip tooltip-left"
|
|
181
|
+
data-tip={viewerState.isFullScreen
|
|
182
|
+
? m.exit_full_screen()
|
|
183
|
+
: m.enter_full_screen()}
|
|
184
|
+
>
|
|
185
|
+
{@render fullscreenBtn()}
|
|
186
|
+
</div>
|
|
187
|
+
{/if}
|
|
188
|
+
|
|
189
|
+
<!-- Annotations Toggle -->
|
|
190
|
+
{#if showAnnotations}
|
|
191
|
+
<div
|
|
192
|
+
class="tooltip tooltip-top"
|
|
193
|
+
data-tip={viewerState.showAnnotations
|
|
194
|
+
? m.hide_annotations()
|
|
195
|
+
: m.show_annotations()}
|
|
196
|
+
>
|
|
197
|
+
{@render annotationsBtn()}
|
|
198
|
+
</div>
|
|
199
|
+
{/if}
|
|
200
|
+
|
|
201
|
+
<!-- Metadata Toggle -->
|
|
202
|
+
{#if showInfo}
|
|
203
|
+
<div class="tooltip tooltip-top" data-tip={m.metadata()}>
|
|
204
|
+
{@render infoBtn()}
|
|
205
|
+
</div>
|
|
206
|
+
{/if}
|
|
207
|
+
</div>
|
|
208
|
+
{/if}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
|
+
import PuzzlePiece from 'phosphor-svelte/lib/PuzzlePiece';
|
|
4
|
+
import X from 'phosphor-svelte/lib/X';
|
|
5
|
+
import { VIEWER_STATE_KEY, type ViewerState } from '../state/viewer.svelte';
|
|
6
|
+
import { m } from '../state/i18n.svelte';
|
|
7
|
+
|
|
8
|
+
const viewerState = getContext<ViewerState>(VIEWER_STATE_KEY);
|
|
9
|
+
|
|
10
|
+
let sortedPluginButtons = $derived(
|
|
11
|
+
[...viewerState.pluginMenuButtons].sort(
|
|
12
|
+
(a, b) => (a.order ?? 100) - (b.order ?? 100),
|
|
13
|
+
),
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
let isOpen = $state(false);
|
|
17
|
+
|
|
18
|
+
function toggleOpen() {
|
|
19
|
+
isOpen = !isOpen;
|
|
20
|
+
}
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
{#if sortedPluginButtons.length > 0}
|
|
24
|
+
<div
|
|
25
|
+
class="absolute left-6 bottom-6 z-50 pointer-events-auto flex flex-col items-start transition-all duration-300"
|
|
26
|
+
>
|
|
27
|
+
<!-- Plugin Buttons Stack -->
|
|
28
|
+
<div
|
|
29
|
+
class="flex flex-col-reverse gap-3 mb-3 transition-all duration-300 origin-bottom {isOpen
|
|
30
|
+
? 'opacity-100 translate-y-0 scale-100'
|
|
31
|
+
: 'opacity-0 translate-y-4 scale-90 pointer-events-none'}"
|
|
32
|
+
>
|
|
33
|
+
{#each sortedPluginButtons as button (button.id)}
|
|
34
|
+
{@const Icon = button.icon}
|
|
35
|
+
<div class="tooltip tooltip-right" data-tip={button.tooltip}>
|
|
36
|
+
<button
|
|
37
|
+
aria-label={button.tooltip}
|
|
38
|
+
class="btn btn-lg btn-circle shadow-lg {button.isActive?.()
|
|
39
|
+
? (button.activeClass ?? 'btn-primary')
|
|
40
|
+
: 'btn-neutral'}"
|
|
41
|
+
onclick={() => {
|
|
42
|
+
button.onClick();
|
|
43
|
+
isOpen = false;
|
|
44
|
+
}}
|
|
45
|
+
>
|
|
46
|
+
<Icon size={28} weight="bold" />
|
|
47
|
+
</button>
|
|
48
|
+
</div>
|
|
49
|
+
{/each}
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<!-- Main Toggle Button -->
|
|
53
|
+
<div class="tooltip tooltip-right" data-tip={m.plugins_tooltip()}>
|
|
54
|
+
<button
|
|
55
|
+
class="btn btn-lg btn-secondary btn-circle shadow-xl transition-transform duration-300 {isOpen
|
|
56
|
+
? 'rotate-90'
|
|
57
|
+
: ''}"
|
|
58
|
+
aria-label={m.plugins_tooltip()}
|
|
59
|
+
onclick={toggleOpen}
|
|
60
|
+
>
|
|
61
|
+
{#if isOpen}
|
|
62
|
+
<X size={28} weight="bold" />
|
|
63
|
+
{:else}
|
|
64
|
+
<PuzzlePiece size={28} weight="bold" />
|
|
65
|
+
{/if}
|
|
66
|
+
</button>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
{/if}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
|
+
import { VIEWER_STATE_KEY, type ViewerState } from '../state/viewer.svelte';
|
|
4
|
+
import { m, language } from '../state/i18n.svelte';
|
|
5
|
+
|
|
6
|
+
const viewerState = getContext<ViewerState>(VIEWER_STATE_KEY);
|
|
7
|
+
|
|
8
|
+
let manifest = $derived(viewerState.manifest);
|
|
9
|
+
|
|
10
|
+
// Helper to extract metadata
|
|
11
|
+
let metadata = $derived.by(() => {
|
|
12
|
+
const currentLang = language.current;
|
|
13
|
+
if (!manifest) return [];
|
|
14
|
+
|
|
15
|
+
// Manifesto's getMetadata() returns an array of { label: ..., value: ... } objects
|
|
16
|
+
// The values might be strings or arrays of objects (LanguageMap)
|
|
17
|
+
const rawMetadata = manifest.getMetadata();
|
|
18
|
+
|
|
19
|
+
if (!rawMetadata) return [];
|
|
20
|
+
|
|
21
|
+
return rawMetadata.map((item: any) => {
|
|
22
|
+
let label = '';
|
|
23
|
+
let value = '';
|
|
24
|
+
|
|
25
|
+
// Helper to pick best language
|
|
26
|
+
const pickLang = (val: string | any[]) => {
|
|
27
|
+
if (typeof val === 'string') return val;
|
|
28
|
+
if (Array.isArray(val)) {
|
|
29
|
+
// exact match
|
|
30
|
+
let match = val.find(
|
|
31
|
+
(x: any) =>
|
|
32
|
+
x.locale === currentLang ||
|
|
33
|
+
x.language === currentLang,
|
|
34
|
+
);
|
|
35
|
+
// fallback to en
|
|
36
|
+
if (!match)
|
|
37
|
+
match = val.find(
|
|
38
|
+
(x: any) =>
|
|
39
|
+
x.locale === 'en' || x.language === 'en',
|
|
40
|
+
);
|
|
41
|
+
// fallback to none/null
|
|
42
|
+
if (!match)
|
|
43
|
+
match = val.find((x: any) => !x.locale && !x.language);
|
|
44
|
+
// fallback to first
|
|
45
|
+
if (!match) match = val[0];
|
|
46
|
+
|
|
47
|
+
return match ? match.value : '';
|
|
48
|
+
}
|
|
49
|
+
return String(val);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Handle Label
|
|
53
|
+
if (item.getLabel) {
|
|
54
|
+
label = pickLang(item.getLabel());
|
|
55
|
+
} else if (item.label) {
|
|
56
|
+
label = pickLang(item.label);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Handle Value
|
|
60
|
+
if (item.getValue) {
|
|
61
|
+
value = pickLang(item.getValue());
|
|
62
|
+
} else if (item.value) {
|
|
63
|
+
value = pickLang(item.value);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { label, value };
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Also get top-level description, attribution, license/rights
|
|
71
|
+
let description = $derived(manifest ? manifest.getDescription() : '');
|
|
72
|
+
let attribution = $derived(
|
|
73
|
+
manifest ? manifest.getRequiredStatement()?.getValue() : '',
|
|
74
|
+
);
|
|
75
|
+
// getRequiredStatement usually returns { label: ..., value: ... } or similar structure that supports getValue()
|
|
76
|
+
|
|
77
|
+
let license = $derived(manifest ? manifest.getLicense() : '');
|
|
78
|
+
</script>
|
|
79
|
+
|
|
80
|
+
<!-- Modal -->
|
|
81
|
+
<dialog class="modal" open={viewerState.showMetadataDialog}>
|
|
82
|
+
<div class="modal-box w-11/12 max-w-5xl">
|
|
83
|
+
<form method="dialog">
|
|
84
|
+
<button
|
|
85
|
+
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
|
|
86
|
+
onclick={() => viewerState.toggleMetadataDialog()}>✕</button
|
|
87
|
+
>
|
|
88
|
+
</form>
|
|
89
|
+
|
|
90
|
+
<h3 class="font-bold text-lg mb-4">
|
|
91
|
+
{manifest
|
|
92
|
+
? manifest.getLabel().length && manifest.getLabel()[0]
|
|
93
|
+
? manifest.getLabel()[0].value
|
|
94
|
+
: m.metadata_label_fallback()
|
|
95
|
+
: m.loading()}
|
|
96
|
+
</h3>
|
|
97
|
+
|
|
98
|
+
<div class="py-4 overflow-y-auto max-h-[70vh]">
|
|
99
|
+
{#if description}
|
|
100
|
+
<div class="mb-6 prose">
|
|
101
|
+
<p>{@html description}</p>
|
|
102
|
+
</div>
|
|
103
|
+
{/if}
|
|
104
|
+
|
|
105
|
+
<dl class="grid grid-cols-1 md:grid-cols-[200px_1fr]">
|
|
106
|
+
{#if attribution}
|
|
107
|
+
<dt class="font-bold text-lg opacity-70 mt-6">
|
|
108
|
+
{m.attribution()}
|
|
109
|
+
</dt>
|
|
110
|
+
<dd class="text-sm ps-2">{@html attribution}</dd>
|
|
111
|
+
{/if}
|
|
112
|
+
|
|
113
|
+
{#if license}
|
|
114
|
+
<dt class="font-bold text-lg opacity-70 mt-6">
|
|
115
|
+
{m.license()}
|
|
116
|
+
</dt>
|
|
117
|
+
<dd class="text-sm ps-2">
|
|
118
|
+
<a
|
|
119
|
+
href={license}
|
|
120
|
+
target="_blank"
|
|
121
|
+
rel="noreferrer"
|
|
122
|
+
class="link link-primary break-all">{license}</a
|
|
123
|
+
>
|
|
124
|
+
</dd>
|
|
125
|
+
{/if}
|
|
126
|
+
|
|
127
|
+
{#each metadata as item}
|
|
128
|
+
<dt class="font-bold text-lg opacity-70 mt-6">
|
|
129
|
+
{item.label}
|
|
130
|
+
</dt>
|
|
131
|
+
<dd class="text-sm ps-2">{@html item.value}</dd>
|
|
132
|
+
{/each}
|
|
133
|
+
</dl>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<div class="modal-action">
|
|
137
|
+
<form method="dialog">
|
|
138
|
+
<button
|
|
139
|
+
class="btn"
|
|
140
|
+
onclick={() => viewerState.toggleMetadataDialog()}
|
|
141
|
+
>{m.close()}</button
|
|
142
|
+
>
|
|
143
|
+
</form>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
<form method="dialog" class="modal-backdrop">
|
|
147
|
+
<button onclick={() => viewerState.toggleMetadataDialog()}
|
|
148
|
+
>{m.close()}</button
|
|
149
|
+
>
|
|
150
|
+
</form>
|
|
151
|
+
</dialog>
|