@anton-gustafsson/snapshot-core 0.0.7 → 0.4.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/cached-snapshot-storage.d.ts +79 -0
- package/dist/cached-snapshot-storage.js +176 -0
- package/dist/encode.d.ts +19 -0
- package/dist/encode.js +56 -0
- package/dist/errors.d.ts +30 -0
- package/dist/errors.js +44 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/snapshot-nav-list.d.ts +45 -8
- package/dist/snapshot-nav-list.js +105 -44
- package/dist/snapshot-service.d.ts +87 -12
- package/dist/snapshot-service.js +216 -39
- package/dist/snapshot-storage.d.ts +46 -7
- package/dist/snapshot-storage.js +54 -18
- package/package.json +7 -3
|
@@ -23,8 +23,11 @@ export class SnapshotNavList extends LitElement {
|
|
|
23
23
|
constructor() {
|
|
24
24
|
super(...arguments);
|
|
25
25
|
this.items = [];
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
/** `card` by default — a framed preview with title/description underneath. `tile` is the compact contact-sheet grid, `list` a sidebar row. */
|
|
27
|
+
this.variant = 'card';
|
|
28
|
+
/** Lets the host itself scroll (see `--snapshot-nav-list-max-height`) instead of growing unbounded. */
|
|
29
|
+
this.scrollable = false;
|
|
30
|
+
/** tile overlay: tint behind the title so it stays legible over any image. Transparent by default — opt into a scrim explicitly. */
|
|
28
31
|
this.overlayTint = 'none';
|
|
29
32
|
/** caption background tint strength, 0-1 */
|
|
30
33
|
this.textOverlayOpacity = 0.35;
|
|
@@ -32,11 +35,11 @@ export class SnapshotNavList extends LitElement {
|
|
|
32
35
|
this.imageOverlayOpacity = 0;
|
|
33
36
|
/** backdrop blur behind the title, in px */
|
|
34
37
|
this.overlayBlur = 0;
|
|
35
|
-
/**
|
|
38
|
+
/** tile only: 'bottom' is the caption strip (default), 'center' centers a larger title. */
|
|
36
39
|
this.labelPosition = 'bottom';
|
|
37
40
|
/** Defaults to the shared singleton — set your own instance (e.g. a namespaced or custom-storage SnapshotService) per <snapshot-nav-list> if needed. */
|
|
38
41
|
this.snapshotService = defaultSnapshotService;
|
|
39
|
-
/** Shows an edit button per card. Off by default — clicking it fires `nav-edit` instead of `nav-select`; the host decides what "edit" means (e.g. open its own dialog component). */
|
|
42
|
+
/** Shows an edit button per card. Off by default — clicking it fires `nav-edit` instead of `nav-select`; the host decides what "edit" means (e.g. open its own dialog component). Overridable per row via `NavItem.editable`. */
|
|
40
43
|
this.editable = false;
|
|
41
44
|
/** Where the edit button sits: `overlay` (default) floats it over the thumbnail; `meta` pins it to the right edge of the title row, with the description below. Ignored by the icon-only variant, whose caption is itself an overlay. */
|
|
42
45
|
this.editButtonPosition = 'overlay';
|
|
@@ -44,7 +47,14 @@ export class SnapshotNavList extends LitElement {
|
|
|
44
47
|
this.editIcon = DEFAULT_EDIT_ICON;
|
|
45
48
|
this.thumbs = new Map();
|
|
46
49
|
this.loadingIds = new Set();
|
|
47
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* In-flight dedup guard, separate from `loadingIds` (which is only for
|
|
52
|
+
* spinner display) so a repeat `loadThumb` call for an id already being
|
|
53
|
+
* fetched is a no-op. Keyed by `${variant}\0${id}` — not just `id` — so a
|
|
54
|
+
* `variantKey` switch mid-flight doesn't have the new variant's fetch
|
|
55
|
+
* silently dropped because the *previous* variant's id is still marked
|
|
56
|
+
* in-flight.
|
|
57
|
+
*/
|
|
48
58
|
this.fetchingIds = new Set();
|
|
49
59
|
}
|
|
50
60
|
static { this.styles = css `
|
|
@@ -52,6 +62,16 @@ export class SnapshotNavList extends LitElement {
|
|
|
52
62
|
display: block;
|
|
53
63
|
font-family: var(--snapshot-nav-list-font, inherit);
|
|
54
64
|
--frame-accent: var(--snapshot-nav-list-accent, #ff5a1f);
|
|
65
|
+
max-height: var(--snapshot-nav-list-max-height, none);
|
|
66
|
+
}
|
|
67
|
+
/* scrollable: the host owns the scroll, so a long list in a flex sidebar
|
|
68
|
+
doesn't push its siblings off-screen and the consumer doesn't have to
|
|
69
|
+
reach in with its own overflow/min-height CSS. */
|
|
70
|
+
:host([scrollable]) {
|
|
71
|
+
flex: 1 1 auto;
|
|
72
|
+
min-height: 0;
|
|
73
|
+
overflow-y: auto;
|
|
74
|
+
overscroll-behavior: contain;
|
|
55
75
|
}
|
|
56
76
|
ul {
|
|
57
77
|
list-style: none;
|
|
@@ -187,10 +207,10 @@ export class SnapshotNavList extends LitElement {
|
|
|
187
207
|
-webkit-backdrop-filter: blur(var(--overlay-blur, 0px));
|
|
188
208
|
pointer-events: none;
|
|
189
209
|
}
|
|
190
|
-
/* the overlay exists so
|
|
210
|
+
/* the overlay exists so a tile's overlaid title stays legible — list
|
|
191
211
|
variant shows the label beside the thumb, not on top of it, so the
|
|
192
212
|
tint has nothing to do there. */
|
|
193
|
-
:host(:not([variant='
|
|
213
|
+
:host(:not([variant='tile'])) .image-overlay {
|
|
194
214
|
display: none;
|
|
195
215
|
}
|
|
196
216
|
|
|
@@ -299,25 +319,25 @@ export class SnapshotNavList extends LitElement {
|
|
|
299
319
|
height: 68px;
|
|
300
320
|
}
|
|
301
321
|
|
|
302
|
-
/*
|
|
303
|
-
:host([variant='
|
|
322
|
+
/* tile: contact-sheet grid, caption strip pinned to the bottom of each frame */
|
|
323
|
+
:host([variant='tile']) ul {
|
|
304
324
|
flex-direction: row;
|
|
305
325
|
flex-wrap: wrap;
|
|
306
326
|
gap: 0.6rem;
|
|
307
327
|
}
|
|
308
|
-
:host([variant='
|
|
328
|
+
:host([variant='tile']) li {
|
|
309
329
|
position: relative;
|
|
310
330
|
width: var(--snapshot-nav-list-tile-width, 160px);
|
|
311
331
|
height: var(--snapshot-nav-list-tile-height, 100px);
|
|
312
332
|
padding: 0;
|
|
313
333
|
overflow: hidden;
|
|
314
334
|
}
|
|
315
|
-
:host([variant='
|
|
335
|
+
:host([variant='tile']) .thumb-wrap {
|
|
316
336
|
width: 100%;
|
|
317
337
|
height: 100%;
|
|
318
338
|
border-radius: var(--snapshot-nav-list-radius, 10px);
|
|
319
339
|
}
|
|
320
|
-
:host([variant='
|
|
340
|
+
:host([variant='tile']) .meta {
|
|
321
341
|
position: absolute;
|
|
322
342
|
inset: auto 0 0 0;
|
|
323
343
|
margin: var(--snapshot-nav-list-overlay-margin, 0);
|
|
@@ -330,28 +350,28 @@ export class SnapshotNavList extends LitElement {
|
|
|
330
350
|
backdrop-filter: blur(var(--overlay-blur, 0px));
|
|
331
351
|
-webkit-backdrop-filter: blur(var(--overlay-blur, 0px));
|
|
332
352
|
}
|
|
333
|
-
:host([variant='
|
|
353
|
+
:host([variant='tile']) .label {
|
|
334
354
|
white-space: normal;
|
|
335
355
|
}
|
|
336
|
-
:host([variant='
|
|
356
|
+
:host([variant='tile']) .description {
|
|
337
357
|
color: color-mix(in srgb, var(--overlay-text, #fff) 75%, transparent);
|
|
338
358
|
}
|
|
339
359
|
|
|
340
360
|
/* label-position="center": title big and centered */
|
|
341
|
-
:host([variant='
|
|
361
|
+
:host([variant='tile'][label-position='center']) .meta {
|
|
342
362
|
inset: 0;
|
|
343
363
|
align-items: center;
|
|
344
364
|
justify-content: center;
|
|
345
365
|
padding: 0.6rem;
|
|
346
366
|
}
|
|
347
|
-
:host([variant='
|
|
367
|
+
:host([variant='tile'][label-position='center']) .label {
|
|
348
368
|
font-size: 1.15rem;
|
|
349
369
|
font-weight: 600;
|
|
350
370
|
text-align: center;
|
|
351
371
|
}
|
|
352
372
|
|
|
353
373
|
/* card: a contained (never cropped) preview above real body text below it —
|
|
354
|
-
text never sits on top of the image, so unlike
|
|
374
|
+
text never sits on top of the image, so unlike a tile it needs no
|
|
355
375
|
overlay tint to stay legible. Modeled on a typical "preview card"
|
|
356
376
|
pattern: framed shot, title + description underneath, shadow on hover. */
|
|
357
377
|
:host([variant='card']) ul {
|
|
@@ -416,15 +436,25 @@ export class SnapshotNavList extends LitElement {
|
|
|
416
436
|
font-size: 0.8125rem;
|
|
417
437
|
}
|
|
418
438
|
`; }
|
|
439
|
+
fetchKey(variant, id) {
|
|
440
|
+
return `${variant ?? ''}\0${id}`;
|
|
441
|
+
}
|
|
419
442
|
subscribeToService() {
|
|
420
443
|
this.unsubscribe?.();
|
|
421
|
-
this.unsubscribe = this.snapshotService.subscribe((id, url) => {
|
|
444
|
+
this.unsubscribe = this.snapshotService.subscribe((id, url, variant) => {
|
|
422
445
|
// Ignore captures for ids this list isn't showing — the service is
|
|
423
446
|
// often a shared singleton, so without this guard `thumbs` would grow
|
|
424
447
|
// forever with urls for every snapshot captured anywhere on the page,
|
|
425
448
|
// not just this list's own items.
|
|
426
449
|
if (!this.items.some((item) => item.id === id))
|
|
427
450
|
return;
|
|
451
|
+
// Same reason, one dimension over: a dark-theme capture must not
|
|
452
|
+
// overwrite the light-theme tile this list is currently showing. Folds
|
|
453
|
+
// '' and undefined together on both sides — SnapshotService.keyOf()
|
|
454
|
+
// does the same, so a caller that sets variantKey to '' instead of
|
|
455
|
+
// leaving it undefined must still match a plain (no-variant) capture.
|
|
456
|
+
if ((variant || undefined) !== (this.variantKey || undefined))
|
|
457
|
+
return;
|
|
428
458
|
if (url === null) {
|
|
429
459
|
this.thumbs.delete(id);
|
|
430
460
|
}
|
|
@@ -449,14 +479,23 @@ export class SnapshotNavList extends LitElement {
|
|
|
449
479
|
// update instead of triggering Lit's "update scheduled from updated()"
|
|
450
480
|
// warning that came from doing this same flip inside updated().
|
|
451
481
|
willUpdate(changed) {
|
|
452
|
-
|
|
482
|
+
// 'icon-only' is the pre-0.3 name for 'tile'. Normalising here (rather
|
|
483
|
+
// than in a setter) keeps the reflected attribute — and therefore every
|
|
484
|
+
// CSS selector — on the one canonical value.
|
|
485
|
+
if (this.variant === 'icon-only')
|
|
486
|
+
this.variant = 'tile';
|
|
487
|
+
// A variant-key switch (e.g. light -> dark) invalidates every thumbnail:
|
|
488
|
+
// they're separate snapshots under separate keys.
|
|
489
|
+
if (changed.has('variantKey'))
|
|
490
|
+
this.thumbs.clear();
|
|
491
|
+
if (changed.has('items') || changed.has('variantKey')) {
|
|
453
492
|
const ids = new Set(this.items.map((item) => item.id));
|
|
454
493
|
for (const id of this.thumbs.keys()) {
|
|
455
494
|
if (!ids.has(id))
|
|
456
495
|
this.thumbs.delete(id);
|
|
457
496
|
}
|
|
458
497
|
for (const item of this.items) {
|
|
459
|
-
if (!this.thumbs.has(item.id) && !this.fetchingIds.has(item.id)) {
|
|
498
|
+
if (!this.thumbs.has(item.id) && !this.fetchingIds.has(this.fetchKey(this.variantKey, item.id))) {
|
|
460
499
|
this.loadingIds.add(item.id);
|
|
461
500
|
}
|
|
462
501
|
}
|
|
@@ -466,25 +505,42 @@ export class SnapshotNavList extends LitElement {
|
|
|
466
505
|
if (changed.has('snapshotService')) {
|
|
467
506
|
this.subscribeToService();
|
|
468
507
|
}
|
|
469
|
-
if (changed.has('items')) {
|
|
470
|
-
|
|
508
|
+
if (changed.has('items') || changed.has('variantKey')) {
|
|
509
|
+
void this.loadThumbs();
|
|
471
510
|
}
|
|
472
511
|
}
|
|
473
|
-
|
|
474
|
-
|
|
512
|
+
/**
|
|
513
|
+
* One batched read for the whole list — `getMany()` collapses to a single
|
|
514
|
+
* query/round-trip on a storage that implements `loadMany`, instead of one
|
|
515
|
+
* per row. `fetchingIds` still guards per id, so an `items` reassignment
|
|
516
|
+
* mid-flight doesn't re-request what's already coming.
|
|
517
|
+
*/
|
|
518
|
+
async loadThumbs() {
|
|
519
|
+
const variant = this.variantKey;
|
|
520
|
+
const wanted = this.items.filter((item) => !this.thumbs.has(item.id) && !this.fetchingIds.has(this.fetchKey(variant, item.id)));
|
|
521
|
+
if (wanted.length === 0)
|
|
475
522
|
return;
|
|
476
|
-
|
|
523
|
+
const ids = wanted.map((item) => item.id);
|
|
524
|
+
ids.forEach((id) => this.fetchingIds.add(this.fetchKey(variant, id)));
|
|
477
525
|
try {
|
|
478
|
-
const
|
|
479
|
-
if
|
|
480
|
-
|
|
526
|
+
const urls = await this.snapshotService.getMany(ids, { variant });
|
|
527
|
+
// Dropped if the variant changed while the read was in flight — those
|
|
528
|
+
// urls belong to the previous theme.
|
|
529
|
+
if (variant !== this.variantKey)
|
|
530
|
+
return;
|
|
531
|
+
for (const [id, url] of urls) {
|
|
532
|
+
if (url)
|
|
533
|
+
this.thumbs.set(id, url);
|
|
534
|
+
}
|
|
481
535
|
}
|
|
482
536
|
catch (err) {
|
|
483
|
-
console.error(
|
|
537
|
+
console.error('snapshot-nav-list: failed to load thumbnails', err);
|
|
484
538
|
}
|
|
485
539
|
finally {
|
|
486
|
-
|
|
487
|
-
|
|
540
|
+
ids.forEach((id) => {
|
|
541
|
+
this.fetchingIds.delete(this.fetchKey(variant, id));
|
|
542
|
+
this.loadingIds.delete(id);
|
|
543
|
+
});
|
|
488
544
|
this.requestUpdate();
|
|
489
545
|
}
|
|
490
546
|
}
|
|
@@ -509,20 +565,17 @@ export class SnapshotNavList extends LitElement {
|
|
|
509
565
|
const text = this.overlayTint === 'light' ? '#111' : '#fff';
|
|
510
566
|
return { '--overlay-bg': bg, '--overlay-text': text, '--overlay-blur': blur };
|
|
511
567
|
}
|
|
568
|
+
// Both events carry the whole item — including `data` — so a handler never
|
|
569
|
+
// has to look the item back up by id.
|
|
512
570
|
select(item) {
|
|
513
|
-
this.dispatchEvent(new CustomEvent('nav-select', {
|
|
514
|
-
detail: { id: item.id, route: item.route },
|
|
515
|
-
bubbles: true,
|
|
516
|
-
composed: true,
|
|
517
|
-
}));
|
|
571
|
+
this.dispatchEvent(new CustomEvent('nav-select', { detail: item, bubbles: true, composed: true }));
|
|
518
572
|
}
|
|
519
573
|
edit(e, item) {
|
|
520
574
|
e.stopPropagation();
|
|
521
|
-
this.dispatchEvent(new CustomEvent('nav-edit', {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
}));
|
|
575
|
+
this.dispatchEvent(new CustomEvent('nav-edit', { detail: item, bubbles: true, composed: true }));
|
|
576
|
+
}
|
|
577
|
+
isEditable(item) {
|
|
578
|
+
return item.editable ?? this.editable;
|
|
526
579
|
}
|
|
527
580
|
renderEditButton(item) {
|
|
528
581
|
const icon = this.editIcon || DEFAULT_EDIT_ICON;
|
|
@@ -565,12 +618,12 @@ export class SnapshotNavList extends LitElement {
|
|
|
565
618
|
>
|
|
566
619
|
</div>`}
|
|
567
620
|
<div class="image-overlay" part="overlay" style=${styleMap(imageOverlayStyle)}></div>
|
|
568
|
-
${this.
|
|
621
|
+
${this.isEditable(item) && !editInMeta ? this.renderEditButton(item) : ''}
|
|
569
622
|
</div>
|
|
570
623
|
<div class="meta" part="meta" style=${styleMap(metaStyle)}>
|
|
571
624
|
<div class="label-row" part="label-row">
|
|
572
625
|
<span class="label" part="label">${item.label}</span>
|
|
573
|
-
${this.
|
|
626
|
+
${this.isEditable(item) && editInMeta ? this.renderEditButton(item) : ''}
|
|
574
627
|
</div>
|
|
575
628
|
${item.description ? html `<span class="description" part="description">${item.description}</span>` : ''}
|
|
576
629
|
</div>
|
|
@@ -586,6 +639,12 @@ __decorate([
|
|
|
586
639
|
__decorate([
|
|
587
640
|
property({ reflect: true })
|
|
588
641
|
], SnapshotNavList.prototype, "variant", void 0);
|
|
642
|
+
__decorate([
|
|
643
|
+
property({ attribute: 'variant-key' })
|
|
644
|
+
], SnapshotNavList.prototype, "variantKey", void 0);
|
|
645
|
+
__decorate([
|
|
646
|
+
property({ type: Boolean, reflect: true })
|
|
647
|
+
], SnapshotNavList.prototype, "scrollable", void 0);
|
|
589
648
|
__decorate([
|
|
590
649
|
property({ attribute: 'overlay-tint' })
|
|
591
650
|
], SnapshotNavList.prototype, "overlayTint", void 0);
|
|
@@ -619,6 +678,8 @@ __decorate([
|
|
|
619
678
|
__decorate([
|
|
620
679
|
state()
|
|
621
680
|
], SnapshotNavList.prototype, "loadingIds", void 0);
|
|
622
|
-
|
|
681
|
+
// Guarded so importing this package in a DOM-less process (SSR, Node, a test
|
|
682
|
+
// runner without jsdom) is a no-op instead of a crash.
|
|
683
|
+
if (typeof customElements !== 'undefined' && !customElements.get('snapshot-nav-list')) {
|
|
623
684
|
customElements.define('snapshot-nav-list', SnapshotNavList);
|
|
624
685
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { EncodeOptions } from './encode';
|
|
2
|
+
import type { SnapshotKey, SnapshotStorage } from './snapshot-storage';
|
|
2
3
|
export interface SnapshotServiceConfig {
|
|
3
4
|
/** Provide your own backend-backed implementation to persist across devices. */
|
|
4
5
|
storage?: SnapshotStorage;
|
|
@@ -11,21 +12,85 @@ export interface SnapshotServiceConfig {
|
|
|
11
12
|
* their keys and live-update notifications can't collide.
|
|
12
13
|
*/
|
|
13
14
|
keyPrefix?: string;
|
|
15
|
+
/** Re-encode applied to every capture before it's stored. Defaults to none (the raw html2canvas PNG). */
|
|
16
|
+
encode?: EncodeOptions;
|
|
17
|
+
/**
|
|
18
|
+
* Escape hatch for an existing store whose keys don't follow
|
|
19
|
+
* `keyPrefix + id [+ '@' + variant]` — return the key to use. Note that
|
|
20
|
+
* `parseKey()` (and therefore `prune()`) can't reverse a custom shape.
|
|
21
|
+
*/
|
|
22
|
+
keyFor?: (key: SnapshotKey) => string;
|
|
23
|
+
}
|
|
24
|
+
/** `url` is null when the id was removed (or invalidated) rather than (re)captured. */
|
|
25
|
+
type Listener = (id: string, url: string | null, variant?: string) => void;
|
|
26
|
+
export interface VariantOptions {
|
|
27
|
+
/**
|
|
28
|
+
* A second dimension on the id — a theme, a density, a locale. Stored under
|
|
29
|
+
* its own key, so `get(id, { variant: 'dark' })` never returns the light one.
|
|
30
|
+
*/
|
|
31
|
+
variant?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface CaptureOptions extends VariantOptions {
|
|
34
|
+
/** Per-call override of the instance `scale`. */
|
|
35
|
+
scale?: number;
|
|
36
|
+
/** Per-call override of the instance `encode`. */
|
|
37
|
+
encode?: EncodeOptions;
|
|
14
38
|
}
|
|
15
|
-
/** `url` is null when the id was removed rather than (re)captured. */
|
|
16
|
-
type Listener = (id: string, url: string | null) => void;
|
|
17
39
|
export declare class SnapshotService {
|
|
18
40
|
private storage;
|
|
19
41
|
private scale;
|
|
20
42
|
private keyPrefix;
|
|
21
|
-
private
|
|
43
|
+
private encode?;
|
|
44
|
+
private keyFor?;
|
|
45
|
+
/** Absent under Node/SSR (or any environment without BroadcastChannel) — same-tab notification still works. */
|
|
46
|
+
private channel?;
|
|
22
47
|
private listeners;
|
|
48
|
+
/** In-flight captures, keyed by storage key, so two callers racing on the same view do one render. */
|
|
49
|
+
private capturing;
|
|
23
50
|
constructor(config?: SnapshotServiceConfig);
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
|
|
51
|
+
/**
|
|
52
|
+
* The fully-qualified storage key for an id (+ variant) under this instance.
|
|
53
|
+
* `id`/`variant` are `encodeURIComponent`-escaped before joining, so a `@`
|
|
54
|
+
* (or any other character) inside either can never be mistaken for the
|
|
55
|
+
* separator itself — without escaping, `id: 'a@b'` and `id: 'a', variant: 'b'`
|
|
56
|
+
* would otherwise land on the exact same key.
|
|
57
|
+
*/
|
|
58
|
+
keyOf(id: string, opts?: VariantOptions): SnapshotKey;
|
|
59
|
+
/**
|
|
60
|
+
* Inverse of `keyOf()` for the default key shape — splits a stored key back
|
|
61
|
+
* into `{ id, variant }`. Returns `null` for a key belonging to a different
|
|
62
|
+
* `keyPrefix`, or for any key when a custom `keyFor` is configured (a custom
|
|
63
|
+
* shape isn't reversible).
|
|
64
|
+
*/
|
|
65
|
+
parseKey(key: string): SnapshotKey | null;
|
|
66
|
+
/**
|
|
67
|
+
* Capture works on any element — a snapshot-nav-list item is one convention,
|
|
68
|
+
* not a requirement. Concurrent calls for the same id/variant share one
|
|
69
|
+
* render instead of racing two.
|
|
70
|
+
*/
|
|
71
|
+
capture(el: HTMLElement, id: string, opts?: CaptureOptions): Promise<string>;
|
|
72
|
+
private runCapture;
|
|
73
|
+
get(id: string, opts?: VariantOptions): Promise<string | null>;
|
|
74
|
+
/**
|
|
75
|
+
* Batch read, keyed by bare id. Uses the storage's `loadMany()` when it has
|
|
76
|
+
* one (a single query/round-trip for a whole list) and falls back to parallel
|
|
77
|
+
* `load()`s when it doesn't.
|
|
78
|
+
*/
|
|
79
|
+
getMany(ids: string[], opts?: VariantOptions): Promise<Map<string, string | null>>;
|
|
27
80
|
/** Deletes a stored snapshot and notifies subscribers (this tab and others) that `id` is gone. */
|
|
28
|
-
remove(id: string): Promise<void>;
|
|
81
|
+
remove(id: string, opts?: VariantOptions): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Deletes every stored snapshot (all variants) whose id isn't in `keepIds`,
|
|
84
|
+
* so thumbnails don't outlive the entities they belong to. Requires a storage
|
|
85
|
+
* that implements `keys()`; returns the number of snapshots removed.
|
|
86
|
+
*/
|
|
87
|
+
prune(keepIds: Iterable<string>): Promise<number>;
|
|
88
|
+
/**
|
|
89
|
+
* Warms the storage for a list of ids ahead of render (one `loadMany()` where
|
|
90
|
+
* the storage supports it) and publishes whatever it finds, so any mounted
|
|
91
|
+
* `<snapshot-nav-list>` paints from the first frame instead of spinning.
|
|
92
|
+
*/
|
|
93
|
+
prefetch(ids: string[], opts?: VariantOptions): Promise<void>;
|
|
29
94
|
/**
|
|
30
95
|
* Announces a URL for `id` to subscribers (this tab and others) without
|
|
31
96
|
* touching storage. For a storage layer that resolves a fresher value
|
|
@@ -33,7 +98,7 @@ export declare class SnapshotService {
|
|
|
33
98
|
* local cache in front of a slower authoritative database — call this once
|
|
34
99
|
* the slow read settles so any mounted <snapshot-nav-list> updates live.
|
|
35
100
|
*/
|
|
36
|
-
publish(id: string, url: string): void;
|
|
101
|
+
publish(id: string, url: string, opts?: VariantOptions): void;
|
|
37
102
|
/**
|
|
38
103
|
* Tells subscribers to drop their locally cached thumbnail for `id` and
|
|
39
104
|
* treat it as not-yet-loaded — without deleting anything from storage.
|
|
@@ -41,12 +106,22 @@ export declare class SnapshotService {
|
|
|
41
106
|
* it again. Useful when the underlying data changed out from under the
|
|
42
107
|
* cache (or, for a demo, to replay a loading state on demand).
|
|
43
108
|
*/
|
|
44
|
-
invalidate(id: string): void;
|
|
45
|
-
subscribe(cb: Listener): () =>
|
|
109
|
+
invalidate(id: string, opts?: VariantOptions): void;
|
|
110
|
+
subscribe(cb: Listener): () => void;
|
|
46
111
|
/** Releases the cross-tab BroadcastChannel. Call when this instance (a non-default, namespaced one) is no longer needed. */
|
|
47
112
|
close(): void;
|
|
48
113
|
private notify;
|
|
49
114
|
}
|
|
50
|
-
/**
|
|
115
|
+
/**
|
|
116
|
+
* The shared IndexedDB-backed instance, created on first call — so importing
|
|
117
|
+
* this package never opens a BroadcastChannel or an IndexedDB connection an
|
|
118
|
+
* app that brings its own `SnapshotService` would never use.
|
|
119
|
+
*/
|
|
120
|
+
export declare function getDefaultSnapshotService(): SnapshotService;
|
|
121
|
+
/**
|
|
122
|
+
* @deprecated Use `getDefaultSnapshotService()`. A lazy stand-in for the
|
|
123
|
+
* default instance: it forwards every access to the real service, constructing
|
|
124
|
+
* it on first touch rather than at import time.
|
|
125
|
+
*/
|
|
51
126
|
export declare const snapshotService: SnapshotService;
|
|
52
127
|
export {};
|