feat(export): single-file offline keepsake viewer + guest download

Rebuild the guest "keepsake" (offline HTML gallery) so it renders when the
extracted index.html is opened over file://. Browsers block external ES-module
scripts and fetch() at origin null, so a normal multi-file SvelteKit build shows
a blank window. Build the viewer as one self-contained index.html (inlined
JS/CSS via vite-plugin-singlefile, standalone non-SvelteKit entry) and inject
the data as window.__EXPORT_DATA__; the backend embeds the built viewer via
include_dir! and writes the data global into index.html when zipping.

Also surface the guest-facing download: a feed banner and the BottomNav Export
tab, both shown once the host releases the export.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-18 17:28:54 +02:00
parent f243bfe89a
commit 3654aca18b
26 changed files with 271 additions and 92 deletions

View File

@@ -166,16 +166,10 @@
Tipp: Am besten im WLAN herunterladen — die Datei kann mehrere GB groß sein.
</p>
<div class="flex gap-2">
<button
onclick={() => (showHtmlGuide = false)}
class="flex-1 rounded-lg border border-gray-300 py-2 text-sm text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:text-gray-300 dark:hover:bg-gray-800"
>
<button onclick={() => (showHtmlGuide = false)} class="btn btn-secondary flex-1">
Abbrechen
</button>
<button
onclick={confirmHtmlDownload}
class="flex-1 rounded-lg bg-blue-600 py-2 text-sm font-medium text-white hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-400"
>
<button onclick={confirmHtmlDownload} class="btn btn-primary flex-1">
Herunterladen
</button>
</div>
@@ -206,9 +200,7 @@
{#if loading}
<div class="py-16 text-center text-gray-400 dark:text-gray-500">Laden…</div>
{:else if !status?.released}
<div
class="rounded-xl border border-gray-200 bg-white p-6 text-center dark:border-gray-700 dark:bg-gray-800"
>
<div class="card p-6 text-center">
<svg
class="mx-auto mb-3 h-12 w-12 text-gray-300 dark:text-gray-600"
fill="none"
@@ -231,12 +223,10 @@
<p class="text-sm text-gray-500 dark:text-gray-400">Wähle dein bevorzugtes Format:</p>
<!-- ZIP card -->
<div
class="rounded-xl border border-gray-200 bg-white p-5 dark:border-gray-700 dark:bg-gray-800"
>
<div class="card p-5">
<div class="flex items-start justify-between gap-4">
<div class="min-w-0">
<h2 class="font-semibold text-gray-900 dark:text-gray-100">ZIP-Archiv</h2>
<h2 class="section-title">ZIP-Archiv</h2>
<p class="mt-0.5 text-sm text-gray-500 dark:text-gray-400">
Alle Original-Fotos und Videos in strukturierten Ordnern.
</p>
@@ -253,9 +243,7 @@
<button
onclick={downloadZip}
disabled={status.zip.status !== 'done'}
class="shrink-0 rounded-lg px-4 py-2 text-sm font-medium {status.zip.status === 'done'
? 'bg-blue-600 text-white hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-400'
: 'bg-gray-100 text-gray-400 cursor-not-allowed dark:bg-gray-700 dark:text-gray-500'}"
class="btn btn-primary btn-sm shrink-0"
>
Download
</button>
@@ -273,12 +261,10 @@
</div>
<!-- HTML card -->
<div
class="rounded-xl border border-gray-200 bg-white p-5 dark:border-gray-700 dark:bg-gray-800"
>
<div class="card p-5">
<div class="flex items-start justify-between gap-4">
<div class="min-w-0">
<h2 class="font-semibold text-gray-900 dark:text-gray-100">HTML-Viewer</h2>
<h2 class="section-title">HTML-Viewer</h2>
<p class="mt-0.5 text-sm text-gray-500 dark:text-gray-400">
Schöne Offline-Galerie mit Filterung, Kommentaren und Likes — kein Internet nötig.
</p>
@@ -295,9 +281,7 @@
<button
onclick={downloadHtml}
disabled={status.html.status !== 'done'}
class="shrink-0 rounded-lg px-4 py-2 text-sm font-medium {status.html.status === 'done'
? 'bg-blue-600 text-white hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-400'
: 'bg-gray-100 text-gray-400 cursor-not-allowed dark:bg-gray-700 dark:text-gray-500'}"
class="btn btn-primary btn-sm shrink-0"
>
Download
</button>