triiiceratops 0.10.1 → 0.10.3
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/{ArrowCounterClockwise-C6n_F9YZ.js → ArrowCounterClockwise-CvTUOlYp.js} +1 -1
- package/dist/{X-BUzsFa3u.js → X-Dgb3I7Ob.js} +227 -232
- package/dist/{annotation_tool_point-CCJi2I8J.js → annotation_tool_point-CKkqbVDq.js} +1 -1
- package/dist/components/DemoHeader.svelte +27 -0
- package/dist/components/SearchPanel.svelte +39 -24
- package/dist/components/ThumbnailGallery.svelte +23 -1
- package/dist/components/TriiiceratopsViewer.svelte +12 -2
- package/dist/{image_filters_reset-CWe7vTJU.js → image_filters_reset-DZrbHhqM.js} +1 -1
- package/dist/plugins/annotation-editor.js +3 -3
- package/dist/plugins/image-manipulation.js +3 -3
- package/dist/state/viewer.svelte.js +102 -82
- package/dist/triiiceratops-bundle.js +2264 -2231
- package/dist/triiiceratops-element.iife.js +26 -25
- package/dist/triiiceratops.css +1 -1
- package/package.json +1 -1
|
@@ -539,6 +539,33 @@
|
|
|
539
539
|
/>
|
|
540
540
|
</label>
|
|
541
541
|
</li>
|
|
542
|
+
<li>
|
|
543
|
+
<label class="label cursor-pointer py-1 gap-2">
|
|
544
|
+
<span class="label-text"
|
|
545
|
+
>{m.settings_select_dock_position()}</span
|
|
546
|
+
>
|
|
547
|
+
<select
|
|
548
|
+
class="select select-bordered select-xs w-24"
|
|
549
|
+
value={config.search?.position ??
|
|
550
|
+
'right'}
|
|
551
|
+
onchange={(e) => {
|
|
552
|
+
if (!config.search)
|
|
553
|
+
config.search = {};
|
|
554
|
+
config.search.position = (
|
|
555
|
+
e.currentTarget as HTMLSelectElement
|
|
556
|
+
).value;
|
|
557
|
+
}}
|
|
558
|
+
onclick={(e) => e.stopPropagation()}
|
|
559
|
+
>
|
|
560
|
+
<option value="right"
|
|
561
|
+
>{m.settings_position_right()}</option
|
|
562
|
+
>
|
|
563
|
+
<option value="left"
|
|
564
|
+
>{m.settings_position_left()}</option
|
|
565
|
+
>
|
|
566
|
+
</select>
|
|
567
|
+
</label>
|
|
568
|
+
</li>
|
|
542
569
|
<li>
|
|
543
570
|
<label class="label cursor-pointer py-1 gap-2">
|
|
544
571
|
<span class="label-text"
|
|
@@ -33,13 +33,14 @@
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
function navigate(
|
|
37
|
-
const canvas = viewerState.canvases[
|
|
36
|
+
function navigate(canvasIndex: number) {
|
|
37
|
+
const canvas = viewerState.canvases[canvasIndex];
|
|
38
38
|
if (canvas) {
|
|
39
39
|
viewerState.setCanvas(canvas.id);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
let width = $derived(viewerState.config.search?.width ?? '320px');
|
|
43
|
+
let position = $derived(viewerState.config.search?.position ?? 'right');
|
|
43
44
|
</script>
|
|
44
45
|
|
|
45
46
|
<!-- Drawer / Panel -->
|
|
@@ -48,7 +49,9 @@
|
|
|
48
49
|
class="h-full bg-base-200 shadow-2xl z-100 flex flex-col transition-[width] duration-200 {viewerState
|
|
49
50
|
.config.transparentBackground
|
|
50
51
|
? ''
|
|
51
|
-
:
|
|
52
|
+
: position === 'left'
|
|
53
|
+
? 'border-r border-base-300'
|
|
54
|
+
: 'border-l border-base-300'}"
|
|
52
55
|
style="width: {width}"
|
|
53
56
|
role="dialog"
|
|
54
57
|
aria-label={m.search_panel_title()}
|
|
@@ -121,32 +124,44 @@
|
|
|
121
124
|
})}
|
|
122
125
|
</div>
|
|
123
126
|
|
|
124
|
-
{#each viewerState.searchResults as
|
|
127
|
+
{#each viewerState.searchResults as group}
|
|
125
128
|
<button
|
|
126
|
-
class="w-full text-left
|
|
127
|
-
onclick={() => navigate(
|
|
129
|
+
class="w-full text-left bg-base-100 shadow-sm border border-base-200 rounded-box cursor-pointer hover:shadow-md transition-all block p-0 select-none"
|
|
130
|
+
onclick={() => navigate(group.canvasIndex)}
|
|
128
131
|
>
|
|
129
|
-
<div
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
132
|
+
<div
|
|
133
|
+
class="text-sm font-bold opacity-80 bg-base-200/50 flex items-center justify-between py-2 px-3 border-b border-base-200"
|
|
134
|
+
>
|
|
135
|
+
<span>{group.canvasLabel}</span>
|
|
136
|
+
<span class="badge badge-sm badge-ghost"
|
|
137
|
+
>{group.hits.length}
|
|
138
|
+
{group.hits.length === 1
|
|
139
|
+
? 'match'
|
|
140
|
+
: 'matches'}</span
|
|
133
141
|
>
|
|
134
142
|
</div>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
<span
|
|
140
|
-
class="bg-yellow-200 text-yellow-900 font-bold px-0.5 rounded"
|
|
141
|
-
>{@html result.match}</span
|
|
143
|
+
<div class="p-0">
|
|
144
|
+
{#each group.hits.slice(0, 1) as result}
|
|
145
|
+
<div
|
|
146
|
+
class="p-3 text-sm border-b border-base-200 last:border-none hover:bg-base-200/30 transition-colors"
|
|
142
147
|
>
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
{#if result.type === 'hit'}
|
|
149
|
+
<div class="leading-relaxed">
|
|
150
|
+
<span>{@html result.before}</span>
|
|
151
|
+
<span
|
|
152
|
+
class="bg-yellow-200 text-yellow-900 font-bold px-0.5 rounded"
|
|
153
|
+
>{@html result.match}</span
|
|
154
|
+
>
|
|
155
|
+
<span>{@html result.after}</span>
|
|
156
|
+
</div>
|
|
157
|
+
{:else}
|
|
158
|
+
<div class="leading-relaxed">
|
|
159
|
+
{@html result.match}
|
|
160
|
+
</div>
|
|
161
|
+
{/if}
|
|
162
|
+
</div>
|
|
163
|
+
{/each}
|
|
164
|
+
</div>
|
|
150
165
|
</button>
|
|
151
166
|
{/each}
|
|
152
167
|
{/if}
|
|
@@ -356,6 +356,27 @@
|
|
|
356
356
|
}
|
|
357
357
|
});
|
|
358
358
|
|
|
359
|
+
// Auto-scroll active thumbnail into view
|
|
360
|
+
$effect(() => {
|
|
361
|
+
if (!galleryElement || !viewerState.canvasId) return;
|
|
362
|
+
// Use a slight timeout to ensure DOM is ready/layout is stable if needed,
|
|
363
|
+
// though usually effect runs after render.
|
|
364
|
+
const id = viewerState.canvasId;
|
|
365
|
+
|
|
366
|
+
// requestAnimationFrame to ensure we are in a good painting frame?
|
|
367
|
+
// Or just direct. Svelte 5 effects are post-dom-update.
|
|
368
|
+
const activeEl = galleryElement.querySelector(
|
|
369
|
+
`[data-id="${CSS.escape(id)}"]`,
|
|
370
|
+
);
|
|
371
|
+
if (activeEl) {
|
|
372
|
+
activeEl.scrollIntoView({
|
|
373
|
+
behavior: 'smooth',
|
|
374
|
+
block: 'nearest',
|
|
375
|
+
inline: 'center',
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
|
|
359
380
|
// Switch to horizontal layout if height is small or docked to top/bottom
|
|
360
381
|
let isHorizontal = $derived(
|
|
361
382
|
dockSide === 'top' ||
|
|
@@ -483,7 +504,7 @@
|
|
|
483
504
|
|
|
484
505
|
<!-- Content (Grid or Horizontal Scroll) -->
|
|
485
506
|
<div
|
|
486
|
-
class="flex-1
|
|
507
|
+
class="flex-1 p-1 bg-base-100 {isHorizontal
|
|
487
508
|
? 'overflow-x-auto overflow-y-hidden h-full'
|
|
488
509
|
: 'overflow-y-auto overflow-x-hidden'}"
|
|
489
510
|
>
|
|
@@ -503,6 +524,7 @@
|
|
|
503
524
|
? 'ring-2 ring-primary bg-primary/5'
|
|
504
525
|
: ''}"
|
|
505
526
|
onclick={() => selectCanvas(thumb.id)}
|
|
527
|
+
data-id={thumb.id}
|
|
506
528
|
aria-label="Select canvas {thumb.label}"
|
|
507
529
|
>
|
|
508
530
|
<div
|
|
@@ -162,13 +162,16 @@
|
|
|
162
162
|
let isLeftSidebarVisible = $derived(
|
|
163
163
|
(internalViewerState.showThumbnailGallery &&
|
|
164
164
|
internalViewerState.dockSide === 'left') ||
|
|
165
|
+
(internalViewerState.showSearchPanel &&
|
|
166
|
+
internalViewerState.config.search?.position === 'left') ||
|
|
165
167
|
internalViewerState.pluginPanels.some(
|
|
166
168
|
(p) => p.position === 'left' && p.isVisible(),
|
|
167
169
|
),
|
|
168
170
|
);
|
|
169
171
|
|
|
170
172
|
let isRightSidebarVisible = $derived(
|
|
171
|
-
internalViewerState.showSearchPanel
|
|
173
|
+
(internalViewerState.showSearchPanel &&
|
|
174
|
+
internalViewerState.config.search?.position !== 'left') ||
|
|
172
175
|
(internalViewerState.showThumbnailGallery &&
|
|
173
176
|
internalViewerState.dockSide === 'right') ||
|
|
174
177
|
internalViewerState.pluginPanels.some(
|
|
@@ -373,6 +376,13 @@
|
|
|
373
376
|
? ''
|
|
374
377
|
: 'bg-base-200 border-r border-base-300'}"
|
|
375
378
|
>
|
|
379
|
+
<!-- Search Panel (when configured left) -->
|
|
380
|
+
{#if internalViewerState.showSearchPanel && internalViewerState.config.search?.position === 'left'}
|
|
381
|
+
<div class="h-full relative pointer-events-auto">
|
|
382
|
+
<SearchPanel />
|
|
383
|
+
</div>
|
|
384
|
+
{/if}
|
|
385
|
+
|
|
376
386
|
<!-- Gallery (when docked left) -->
|
|
377
387
|
{#if internalViewerState.showThumbnailGallery && internalViewerState.dockSide === 'left'}
|
|
378
388
|
<div class="h-full w-[140px] pointer-events-auto relative">
|
|
@@ -507,7 +517,7 @@
|
|
|
507
517
|
: 'bg-base-200 border-l border-base-300'}"
|
|
508
518
|
>
|
|
509
519
|
<!-- Search Panel -->
|
|
510
|
-
{#if internalViewerState.showSearchPanel}
|
|
520
|
+
{#if internalViewerState.showSearchPanel && internalViewerState.config.search?.position !== 'left'}
|
|
511
521
|
<div class="h-full relative pointer-events-auto">
|
|
512
522
|
<SearchPanel />
|
|
513
523
|
</div>
|
|
@@ -4,10 +4,10 @@ var nt = (s, e, t) => Mf(s, typeof e != "symbol" ? e + "" : e, t);
|
|
|
4
4
|
import "svelte/internal/disclose-version";
|
|
5
5
|
import * as d from "svelte/internal/client";
|
|
6
6
|
import { getContext as Rf, onMount as If, onDestroy as Cf } from "svelte";
|
|
7
|
-
import { m as xl, g as as, l as Pf, s as Df, X as bl, c as Lf, V as Of } from "../X-
|
|
7
|
+
import { m as xl, g as as, l as Pf, s as Df, X as bl, c as Lf, V as Of } from "../X-Dgb3I7Ob.js";
|
|
8
8
|
import Hs from "openseadragon";
|
|
9
|
-
import { A as Bf } from "../ArrowCounterClockwise-
|
|
10
|
-
import { q as Nf, h as Ff, c as kf, j as Uf, k as $f, t as Gf, u as Hf, v as Vf, r as Xf, s as zf, m as jf, i as Yf, g as Wf, a as Zf, n as qf, f as Kf, e as Jf, b as Qf, d as ep, o as tp, l as sp, p as ip } from "../annotation_tool_point-
|
|
9
|
+
import { A as Bf } from "../ArrowCounterClockwise-CvTUOlYp.js";
|
|
10
|
+
import { q as Nf, h as Ff, c as kf, j as Uf, k as $f, t as Gf, u as Hf, v as Vf, r as Xf, s as zf, m as jf, i as Yf, g as Wf, a as Zf, n as qf, f as Kf, e as Jf, b as Qf, d as ep, o as tp, l as sp, p as ip } from "../annotation_tool_point-CKkqbVDq.js";
|
|
11
11
|
import "manifesto.js";
|
|
12
12
|
var np = Object.defineProperty, rp = (s, e, t) => e in s ? np(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t, El = (s, e, t) => rp(s, typeof e != "symbol" ? e + "" : e, t), wl = Object.prototype.hasOwnProperty;
|
|
13
13
|
function Vs(s, e) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import "svelte/internal/disclose-version";
|
|
2
2
|
import * as e from "svelte/internal/client";
|
|
3
3
|
import { getContext as s0 } from "svelte";
|
|
4
|
-
import { l as l0, s as i0, X as n0, c as o0, V as c0, g as v0 } from "../X-
|
|
5
|
-
import { A as d0 } from "../ArrowCounterClockwise-
|
|
6
|
-
import { i as _0, a as g0, b as f0, g as u0, c as h0, e as m0, d as p0, f as b0 } from "../image_filters_reset-
|
|
4
|
+
import { l as l0, s as i0, X as n0, c as o0, V as c0, g as v0 } from "../X-Dgb3I7Ob.js";
|
|
5
|
+
import { A as d0 } from "../ArrowCounterClockwise-CvTUOlYp.js";
|
|
6
|
+
import { i as _0, a as g0, b as f0, g as u0, c as h0, e as m0, d as p0, f as b0 } from "../image_filters_reset-DZrbHhqM.js";
|
|
7
7
|
const G = {
|
|
8
8
|
brightness: 100,
|
|
9
9
|
contrast: 100,
|
|
@@ -292,7 +292,8 @@ export class ViewerState {
|
|
|
292
292
|
throw new Error('Search request failed');
|
|
293
293
|
const data = await response.json();
|
|
294
294
|
const resources = data.resources || [];
|
|
295
|
-
|
|
295
|
+
// Group results by canvas index
|
|
296
|
+
const resultsByCanvas = new Map();
|
|
296
297
|
// Helper to parse xywh
|
|
297
298
|
const parseSelector = (onVal) => {
|
|
298
299
|
const val = typeof onVal === 'string'
|
|
@@ -308,73 +309,84 @@ export class ViewerState {
|
|
|
308
309
|
return coords; // [x, y, w, h]
|
|
309
310
|
return null;
|
|
310
311
|
};
|
|
312
|
+
// Helper to unescape mark tags
|
|
313
|
+
const decodeMark = (str) => {
|
|
314
|
+
if (!str)
|
|
315
|
+
return '';
|
|
316
|
+
return str
|
|
317
|
+
.replace(/<mark>/g, '<mark>')
|
|
318
|
+
.replace(/<\/mark>/g, '</mark>');
|
|
319
|
+
};
|
|
311
320
|
if (data.hits) {
|
|
312
321
|
for (const hit of data.hits) {
|
|
313
322
|
// hits have property 'annotations' which is array of ids
|
|
314
|
-
// Collapse all annotations for this hit into a single result per canvas
|
|
315
323
|
const annotations = hit.annotations || [];
|
|
316
|
-
|
|
324
|
+
// We need to determine which canvas this hit belongs to.
|
|
325
|
+
// A hit might technically span annotations on multiple canvases (unlikely for IIIF Content Search),
|
|
326
|
+
// but usually it's associated with specific annotations on one canvas.
|
|
327
|
+
// We will take the first valid canvas we find for the annotations.
|
|
328
|
+
let canvasIndex = -1;
|
|
329
|
+
let bounds = null;
|
|
330
|
+
let allBounds = [];
|
|
317
331
|
for (const annoId of annotations) {
|
|
318
332
|
const annotation = resources.find((r) => r['@id'] === annoId || r.id === annoId);
|
|
319
333
|
if (annotation && annotation.on) {
|
|
320
|
-
// annotation.on can be "canvas-id" or "canvas-id#xywh=..."
|
|
321
334
|
const onVal = typeof annotation.on === 'string'
|
|
322
335
|
? annotation.on
|
|
323
336
|
: annotation.on['@id'] || annotation.on.id;
|
|
324
337
|
const cleanOn = onVal.split('#')[0];
|
|
325
|
-
const
|
|
326
|
-
const
|
|
327
|
-
if (
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
let label = 'Canvas ' + (canvasIndex + 1);
|
|
332
|
-
try {
|
|
333
|
-
if (canvas.getLabel) {
|
|
334
|
-
const l = canvas.getLabel();
|
|
335
|
-
if (Array.isArray(l) &&
|
|
336
|
-
l.length > 0)
|
|
337
|
-
label = l[0].value;
|
|
338
|
-
else if (typeof l === 'string')
|
|
339
|
-
label = l;
|
|
340
|
-
}
|
|
341
|
-
else if (canvas.label) {
|
|
342
|
-
// Fallback if raw object
|
|
343
|
-
if (typeof canvas.label === 'string')
|
|
344
|
-
label = canvas.label;
|
|
345
|
-
else if (Array.isArray(canvas.label))
|
|
346
|
-
label = canvas.label[0]?.value;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
catch (e) {
|
|
350
|
-
/* ignore */
|
|
351
|
-
}
|
|
352
|
-
hitBoundsByCanvas.set(canvasIndex, {
|
|
353
|
-
label: String(label),
|
|
354
|
-
bounds: [],
|
|
355
|
-
});
|
|
338
|
+
const b = parseSelector(onVal);
|
|
339
|
+
const cIndex = this.canvases.findIndex((c) => c.id === cleanOn);
|
|
340
|
+
if (cIndex >= 0) {
|
|
341
|
+
// If we haven't set a canvas yet, set it
|
|
342
|
+
if (canvasIndex === -1) {
|
|
343
|
+
canvasIndex = cIndex;
|
|
356
344
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
345
|
+
// If we found bounds, add them
|
|
346
|
+
if (b) {
|
|
347
|
+
allBounds.push(b);
|
|
348
|
+
if (!bounds)
|
|
349
|
+
bounds = b;
|
|
361
350
|
}
|
|
362
351
|
}
|
|
363
352
|
}
|
|
364
353
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
354
|
+
if (canvasIndex >= 0) {
|
|
355
|
+
if (!resultsByCanvas.has(canvasIndex)) {
|
|
356
|
+
const canvas = this.canvases[canvasIndex];
|
|
357
|
+
let label = 'Canvas ' + (canvasIndex + 1);
|
|
358
|
+
try {
|
|
359
|
+
if (canvas.getLabel) {
|
|
360
|
+
const l = canvas.getLabel();
|
|
361
|
+
if (Array.isArray(l) && l.length > 0)
|
|
362
|
+
label = l[0].value;
|
|
363
|
+
else if (typeof l === 'string')
|
|
364
|
+
label = l;
|
|
365
|
+
}
|
|
366
|
+
else if (canvas.label) {
|
|
367
|
+
// Fallback if raw object
|
|
368
|
+
if (typeof canvas.label === 'string')
|
|
369
|
+
label = canvas.label;
|
|
370
|
+
else if (Array.isArray(canvas.label))
|
|
371
|
+
label = canvas.label[0]?.value;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
catch (e) {
|
|
375
|
+
/* ignore */
|
|
376
|
+
}
|
|
377
|
+
resultsByCanvas.set(canvasIndex, {
|
|
378
|
+
canvasIndex,
|
|
379
|
+
canvasLabel: String(label),
|
|
380
|
+
hits: [],
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
resultsByCanvas.get(canvasIndex).hits.push({
|
|
368
384
|
type: 'hit',
|
|
369
|
-
before: hit.before,
|
|
370
|
-
match: hit.match,
|
|
371
|
-
after: hit.after,
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
// Store all bounds for this hit on this canvas
|
|
375
|
-
allBounds: data.bounds,
|
|
376
|
-
// Keep first bounds for backwards compatibility
|
|
377
|
-
bounds: data.bounds.length > 0 ? data.bounds[0] : null,
|
|
385
|
+
before: decodeMark(hit.before),
|
|
386
|
+
match: decodeMark(hit.match),
|
|
387
|
+
after: decodeMark(hit.after),
|
|
388
|
+
bounds,
|
|
389
|
+
allBounds,
|
|
378
390
|
});
|
|
379
391
|
}
|
|
380
392
|
}
|
|
@@ -410,46 +422,54 @@ export class ViewerState {
|
|
|
410
422
|
catch (e) {
|
|
411
423
|
/* ignore */
|
|
412
424
|
}
|
|
413
|
-
|
|
425
|
+
if (!resultsByCanvas.has(canvasIndex)) {
|
|
426
|
+
resultsByCanvas.set(canvasIndex, {
|
|
427
|
+
canvasIndex,
|
|
428
|
+
canvasLabel: String(label),
|
|
429
|
+
hits: [],
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
resultsByCanvas.get(canvasIndex).hits.push({
|
|
414
433
|
type: 'resource',
|
|
415
|
-
match: res.resource && res.resource.chars
|
|
434
|
+
match: decodeMark(res.resource && res.resource.chars
|
|
416
435
|
? res.resource.chars
|
|
417
|
-
: res.chars || '',
|
|
418
|
-
canvasIndex,
|
|
419
|
-
canvasLabel: String(label),
|
|
436
|
+
: res.chars || ''),
|
|
420
437
|
bounds,
|
|
438
|
+
allBounds: bounds ? [bounds] : [],
|
|
421
439
|
});
|
|
422
440
|
}
|
|
423
441
|
}
|
|
424
442
|
}
|
|
425
|
-
|
|
443
|
+
// Convert Map to Array and Sort
|
|
444
|
+
this.searchResults = Array.from(resultsByCanvas.values()).sort((a, b) => a.canvasIndex - b.canvasIndex);
|
|
426
445
|
// Generate ephemeral search annotations
|
|
427
|
-
//
|
|
446
|
+
// We need to flatten our grouped structure to generate the annotations list
|
|
428
447
|
let annotationIndex = 0;
|
|
429
|
-
this.searchAnnotations =
|
|
430
|
-
const canvas = this.canvases[
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
448
|
+
this.searchAnnotations = this.searchResults.flatMap((group) => {
|
|
449
|
+
const canvas = this.canvases[group.canvasIndex];
|
|
450
|
+
return group.hits.flatMap((hit) => {
|
|
451
|
+
const boundsArray = hit.allBounds && hit.allBounds.length > 0
|
|
452
|
+
? hit.allBounds
|
|
453
|
+
: hit.bounds
|
|
454
|
+
? [hit.bounds]
|
|
455
|
+
: [];
|
|
456
|
+
return boundsArray.map((bounds) => {
|
|
457
|
+
const on = `${canvas.id}#xywh=${bounds.join(',')}`;
|
|
458
|
+
return {
|
|
459
|
+
'@id': `urn:search-hit:${annotationIndex++}`,
|
|
460
|
+
'@type': 'oa:Annotation',
|
|
461
|
+
motivation: 'sc:painting',
|
|
462
|
+
on: on,
|
|
463
|
+
canvasId: canvas.id,
|
|
464
|
+
resource: {
|
|
465
|
+
'@type': 'cnt:ContentAsText',
|
|
466
|
+
chars: hit.match,
|
|
467
|
+
},
|
|
468
|
+
// Flag to identify styling in Overlay?
|
|
469
|
+
// Or just standard rendering.
|
|
470
|
+
isSearchHit: true,
|
|
471
|
+
};
|
|
472
|
+
});
|
|
453
473
|
});
|
|
454
474
|
});
|
|
455
475
|
}
|