diff options
| -rw-r--r-- | .rules/default/project.md | 1 | ||||
| -rw-r--r-- | src/App.svelte | 2 | ||||
| -rw-r--r-- | src/lib/components/ImagePreview.svelte | 57 |
3 files changed, 48 insertions, 12 deletions
diff --git a/.rules/default/project.md b/.rules/default/project.md new file mode 100644 index 0000000..96b6913 --- /dev/null +++ b/.rules/default/project.md @@ -0,0 +1 @@ +Look at the readme to see the project goals. The flashair API is documented in reference/. diff --git a/src/App.svelte b/src/App.svelte index ea161bf..b9050dc 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -37,7 +37,7 @@ <div class="flex h-screen bg-base-200"> <!-- Left: Image preview --> - <div class="flex-1 min-w-0 h-full"> + <div class="flex-1 min-w-0 h-full overflow-hidden"> {#if loading} <div class="flex items-center justify-center h-full"> <span class="loading loading-spinner loading-lg"></span> diff --git a/src/lib/components/ImagePreview.svelte b/src/lib/components/ImagePreview.svelte index d811c4f..a574df1 100644 --- a/src/lib/components/ImagePreview.svelte +++ b/src/lib/components/ImagePreview.svelte @@ -8,9 +8,8 @@ let { file }: Props = $props(); - let thumbnailUrl = $derived( - file !== undefined ? flashair.thumbnailUrl(file.path) : undefined, - ); + let thumbnailBlobUrl = $state<string | undefined>(undefined); + let imageAspectRatio = $state<string>('3 / 2'); let fullObjectUrl = $state<string | undefined>(undefined); let progress = $state(0); @@ -25,6 +24,7 @@ * add it as a tracked dependency and cause an infinite loop). */ let rawObjectUrl: string | undefined; + let rawThumbnailUrl: string | undefined; $effect(() => { const currentFile = file; @@ -33,6 +33,7 @@ return; } + loadThumbnail(currentFile); loadFullImage(currentFile); return () => { @@ -48,14 +49,44 @@ URL.revokeObjectURL(rawObjectUrl); rawObjectUrl = undefined; } + if (rawThumbnailUrl !== undefined) { + URL.revokeObjectURL(rawThumbnailUrl); + rawThumbnailUrl = undefined; + } fullObjectUrl = undefined; + thumbnailBlobUrl = undefined; + imageAspectRatio = '3 / 2'; progress = 0; downloading = false; loadError = undefined; } + async function loadThumbnail(entry: FlashAirFileEntry) { + const url = flashair.thumbnailUrl(entry.path); + if (url === undefined) return; + + try { + const { blob, meta } = await flashair.fetchThumbnail(entry.path); + const blobUrl = URL.createObjectURL(blob); + rawThumbnailUrl = blobUrl; + thumbnailBlobUrl = blobUrl; + + if (meta.width !== undefined && meta.height !== undefined && meta.width > 0 && meta.height > 0) { + imageAspectRatio = `${String(meta.width)} / ${String(meta.height)}`; + } + } catch { + // Thumbnail fetch failed — not critical, full image will load + } + } + async function loadFullImage(entry: FlashAirFileEntry) { - cleanup(); + if (rawObjectUrl !== undefined) { + URL.revokeObjectURL(rawObjectUrl); + rawObjectUrl = undefined; + } + fullObjectUrl = undefined; + progress = 0; + loadError = undefined; if (currentAbort !== undefined) { currentAbort.abort(); @@ -117,7 +148,7 @@ } let progressPercent = $derived(Math.round(progress * 100)); - let showThumbnail = $derived(fullObjectUrl === undefined && thumbnailUrl !== undefined); + let showThumbnail = $derived(fullObjectUrl === undefined && thumbnailBlobUrl !== undefined); </script> <div class="h-full flex items-center justify-center bg-base-300 relative"> @@ -150,12 +181,16 @@ /> {/key} {:else if showThumbnail} - <img - src={thumbnailUrl} - alt={file.filename} - class="max-w-full max-h-full object-contain image-rendering-pixelated" - style="image-rendering: pixelated;" - /> + <div + class="w-full max-h-full overflow-hidden" + style:aspect-ratio={imageAspectRatio} + > + <img + src={thumbnailBlobUrl} + alt={file.filename} + class="w-full h-full object-cover blur-lg" + /> + </div> {:else} <span class="loading loading-spinner loading-lg"></span> {/if} |
