From feb336fc5bc1f40325c5faa32335a4592ec2d3a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lohrer?= Date: Wed, 15 Apr 2026 17:39:24 +0200 Subject: [PATCH] spa(phase 3, tasks 15-22): routing, komponenten, home, postview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 3 komplett: - Task 15: LoadingOrError-Komponente (loading/error-states, Habla-Fallback) - Task 16: app.html mit CSS-Variablen (light/dark), Base-Typography - Task 17: +layout.svelte mit Container + bootstrapReadRelays onMount - Task 18: ProfileCard-Komponente (Avatar, Name, About, NIP-05, Website) - Task 19: PostCard-Komponente (Thumbnail + Titel/Summary/Datum), responsive - Task 20: +page.svelte als Home (Profil + Liste, Promise.all für beides) - Task 21: PostView-Komponente (Titel, Meta, Cover, Summary, Markdown-Body) - Task 22: [...slug]/+page.ts+svelte — Catch-all-Route mit Legacy-301-Redirect Alle $props()-abhängigen Werte via $derived() (Svelte-5-Runes-Konformität). npm run check: 0 errors, 0 warnings, 592 files. npm run build grün. Co-Authored-By: Claude Opus 4.6 (1M context) --- app/src/app.html | 46 +++++- app/src/lib/components/LoadingOrError.svelte | 40 +++++ app/src/lib/components/PostCard.svelte | 94 ++++++++++++ app/src/lib/components/PostView.svelte | 148 +++++++++++++++++++ app/src/lib/components/ProfileCard.svelte | 82 ++++++++++ app/src/routes/+layout.svelte | 23 ++- app/src/routes/+page.svelte | 52 ++++++- app/src/routes/[...slug]/+page.svelte | 61 ++++++++ app/src/routes/[...slug]/+page.ts | 21 +++ 9 files changed, 562 insertions(+), 5 deletions(-) create mode 100644 app/src/lib/components/LoadingOrError.svelte create mode 100644 app/src/lib/components/PostCard.svelte create mode 100644 app/src/lib/components/PostView.svelte create mode 100644 app/src/lib/components/ProfileCard.svelte create mode 100644 app/src/routes/[...slug]/+page.svelte create mode 100644 app/src/routes/[...slug]/+page.ts diff --git a/app/src/app.html b/app/src/app.html index 6a2bb58..60b13e8 100644 --- a/app/src/app.html +++ b/app/src/app.html @@ -1,9 +1,51 @@ - + - + + Jörg Lohrer + %sveltekit.head% diff --git a/app/src/lib/components/LoadingOrError.svelte b/app/src/lib/components/LoadingOrError.svelte new file mode 100644 index 0000000..9d1226f --- /dev/null +++ b/app/src/lib/components/LoadingOrError.svelte @@ -0,0 +1,40 @@ + + +{#if loading && !error} +

Lade von Nostr-Relays …

+{:else if error} +

+ {error} + {#if hablaLink} +
+ In Habla.news öffnen + {/if} +

+{/if} + + diff --git a/app/src/lib/components/PostCard.svelte b/app/src/lib/components/PostCard.svelte new file mode 100644 index 0000000..9531ac7 --- /dev/null +++ b/app/src/lib/components/PostCard.svelte @@ -0,0 +1,94 @@ + + + + +
+
{date}
+

{title}

+ {#if summary}

{summary}

{/if} +
+
+ + diff --git a/app/src/lib/components/PostView.svelte b/app/src/lib/components/PostView.svelte new file mode 100644 index 0000000..0b06b2a --- /dev/null +++ b/app/src/lib/components/PostView.svelte @@ -0,0 +1,148 @@ + + +

{title}

+
+ Veröffentlicht am {date} + {#if tags.length > 0} +
+ {#each tags as t} + {t} + {/each} +
+ {/if} +
+ +{#if image} +

Cover-Bild

+{/if} + +{#if summary} +

{summary}

+{/if} + +
{@html bodyHtml}
+ + diff --git a/app/src/lib/components/ProfileCard.svelte b/app/src/lib/components/ProfileCard.svelte new file mode 100644 index 0000000..8d25ec0 --- /dev/null +++ b/app/src/lib/components/ProfileCard.svelte @@ -0,0 +1,82 @@ + + +{#if profile} +
+ {#if profile.picture} + {profile.display_name + {:else} +
+ {/if} +
+
{profile.display_name ?? profile.name ?? ''}
+ {#if profile.about} +
{profile.about}
+ {/if} + {#if profile.nip05 || profile.website} +
+ {#if profile.nip05}{profile.nip05}{/if} + {#if profile.nip05 && profile.website}·{/if} + {#if profile.website} + + {profile.website.replace(/^https?:\/\//, '')} + + {/if} +
+ {/if} +
+
+{/if} + + diff --git a/app/src/routes/+layout.svelte b/app/src/routes/+layout.svelte index 9cebde5..1b26365 100644 --- a/app/src/routes/+layout.svelte +++ b/app/src/routes/+layout.svelte @@ -1,11 +1,32 @@ -{@render children()} +
+ {@render children()} +
+ + diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte index 962cbde..13e33dd 100644 --- a/app/src/routes/+page.svelte +++ b/app/src/routes/+page.svelte @@ -1,2 +1,50 @@ -

SvelteKit-SPA bootet

-

Wird Stück für Stück mit Nostr-Funktionalität gefüllt.

+ + + + +

Beiträge

+ + + +{#each posts as post (post.id)} + +{/each} + + diff --git a/app/src/routes/[...slug]/+page.svelte b/app/src/routes/[...slug]/+page.svelte new file mode 100644 index 0000000..3b47ef6 --- /dev/null +++ b/app/src/routes/[...slug]/+page.svelte @@ -0,0 +1,61 @@ + + + + + + +{#if post} + +{/if} + + diff --git a/app/src/routes/[...slug]/+page.ts b/app/src/routes/[...slug]/+page.ts new file mode 100644 index 0000000..c7b2f51 --- /dev/null +++ b/app/src/routes/[...slug]/+page.ts @@ -0,0 +1,21 @@ +import { error, redirect } from '@sveltejs/kit'; +import { parseLegacyUrl, canonicalPostPath } from '$lib/url/legacy'; +import type { PageLoad } from './$types'; + +export const load: PageLoad = async ({ url }) => { + const pathname = url.pathname; + + // Legacy-Form /YYYY/MM/DD/.html/ → Redirect auf // + const legacyDtag = parseLegacyUrl(pathname); + if (legacyDtag) { + throw redirect(301, canonicalPostPath(legacyDtag)); + } + + // Kanonisch: // — erster Segment des Pfades. + const segments = pathname.replace(/^\/+|\/+$/g, '').split('/'); + if (segments.length !== 1 || !segments[0]) { + throw error(404, 'Seite nicht gefunden'); + } + + return { dtag: decodeURIComponent(segments[0]) }; +};