From f4a7a7bcc73d7f1af6de0bc6d3dec8f02057f62e Mon Sep 17 00:00:00 2001 From: jay Date: Sun, 28 Jun 2026 14:11:06 -0400 Subject: [PATCH] news relaunch step 1: extract the feed into NewsFeed.svelte, mount at / and /news MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pure refactor, no visible/behavioral change. The ~1065-line feed moves verbatim from routes/+page.svelte into lib/components/NewsFeed.svelte; both routes/+page and the new routes/news render it. Link generation is base-aware (feedBase()): on `/` it builds `/?…` exactly as today (bug-for-bug parity); on `/news` it builds `/news?…` so /news is self-coherent. At cutover, `/` becomes the hub and the feed lives only at /news. /news is kept hidden during the transition (noindex, follow) so we never publish a duplicate indexable feed: route + a Caddy @newsHidden X-Robots-Tag (follow, so link equity flows). Removed at cutover, when /news enters the sitemap. 27/27 frontend tests green; build clean; /news.html prerenders. Co-Authored-By: Claude Opus 4.8 --- data/wotd_audio/renewal.mp3 | Bin 0 -> 22985 bytes frontend/src/lib/components/NewsFeed.svelte | 1070 +++++++++++++++++++ frontend/src/routes/+page.svelte | 1067 +----------------- frontend/src/routes/news/+page.svelte | 12 + 4 files changed, 1087 insertions(+), 1062 deletions(-) create mode 100644 data/wotd_audio/renewal.mp3 create mode 100644 frontend/src/lib/components/NewsFeed.svelte create mode 100644 frontend/src/routes/news/+page.svelte diff --git a/data/wotd_audio/renewal.mp3 b/data/wotd_audio/renewal.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..008789df8085ed6856c7e4672eb0ca88c3c81020 GIT binary patch literal 22985 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 literal 0 HcmV?d00001 diff --git a/frontend/src/lib/components/NewsFeed.svelte b/frontend/src/lib/components/NewsFeed.svelte new file mode 100644 index 0000000..363632f --- /dev/null +++ b/frontend/src/lib/components/NewsFeed.svelte @@ -0,0 +1,1070 @@ + + +
(showSaved = true)} onaccount={openAccount} user={auth.user} boundariesActive={filtersOn} /> + +{#if showSignIn} { showSignIn = false; if (!auth.user) pendingDigestOptIn = false; }} />{/if} +{#if showSaved && auth.user} (showSaved = false)} />{/if} +{#if showLanes && lanePool} + (showLanes = false)} /> +{/if} + +
+ {#if navLanes.length} + (showLanes = true)} /> + {/if} + + {#if notice}

{notice}

{/if} + + {#if loading} +

Gathering the good news…

+ {:else if error} +

{error}

+ {:else} + {#key selected} +
+
+

{viewLabel}

+ {#if viewSubtitle}

{viewSubtitle}

{/if} +
+
+ + {#if auth.user && followTarget} + + {/if} + {#if selected !== 'today'} + + {/if} +
+
+ + {#if searchOpen || selected === 'search'} + + {/if} + + {#if selected === 'today'} + {#if sinceCount > 0 && !sinceDismissed} +
+

+ Since you were last here, {sinceCount} new calm read{sinceCount === 1 ? '' : 's'} came in. + {#if !sinceOpen}{/if} +

+ +
+ {#if sinceOpen && sinceItems.length} +
+

New since your last visit

+
+ {#each sinceItems as a (a.id)} + drill('tag:' + t)} onsource={(id, name) => drill('source:' + id, { id, name })} onview={record} /> + {/each} +
+
+ {/if} + {/if} + {#if brief?.items?.length} + {#if homeEditing || (!homeValue && !homePromptDismissed)} +
+ {#if !homeValue}

Want your good news closer to home?

{/if} +
+ + {#if pickCountry === 'US'} + + {/if} + + {#if homeValue} + {:else}{/if} +
+
+ {:else if homeValue} +
+ Good news closest first +
+ {#each scopeStops as [s, label] (s)} + + {/each} +
+ + + + +
+ {/if} +
+ drill('tag:' + t)} onsource={(id, name) => drill('source:' + id, { id, name })} onview={record} onimageerror={heroImageFailed} /> + {#if restArticles.length} +
+ {#each restArticles as a, i (a.id)} + {#if a.section && a.section !== restArticles[i - 1]?.section && a.section !== heroArticle?.section} +

{sectionLabel(a.section)}

+ {/if} + drill('tag:' + t)} onsource={(id, name) => drill('source:' + id, { id, name })} onview={record} /> + {/each} +
+ {/if} +
+
+

✦ that's the good news for today ✦

+

You're caught up for now.

+ {#if ritual.total} +
+

Today's calm set

+
    + {#each ritual.items as it (it.key)} +
  • + {#if it.done || it.key === 'brief'}{it.label}{:else}{it.label}{/if} +
  • + {/each} +
+

+ {ritual.count === ritual.total ? `All ${ritual.total} enjoyed today` : `${ritual.count} of ${ritual.total} enjoyed today`} · fresh set tomorrow · make it yours +

+
+ {/if} + {#if auth.user?.digest_enabled} +

Tomorrow's brief is headed to your inbox ☕

+ {:else} + + {/if} +
+ {:else} +

No highlights yet today — try a calmer filter, or check back soon.

+ {/if} + {:else if feed.length} + {#if selected === 'latest'} + {#if homeEditing || (!homeValue && !homePromptDismissed)} +
+

Want good news closer to home?

+
+ + {#if pickCountry === 'US'} + + {/if} + + {#if homeValue} + + {:else} + + {/if} +
+
+ {:else if homeValue} +
📍 Showing local first · ·
+ {/if} + {/if} +
+ {#each feed as a, i (a.id)} + {#if a.section && a.section !== feed[i - 1]?.section} +

{sectionLabel(a.section)}

+ {/if} + drill('tag:' + t)} onsource={(id, name) => drill('source:' + id, { id, name })} onview={record} /> + {/each} +
+ {#if !feedDone} +
+ +
+ {:else} +

✦ you're all caught up ✦

+ {/if} + {:else if selected === 'search'} +

No articles found for “{searchQuery}”. Try a different word, or a source name like “Nature”.

+ {:else if selected === 'following'} +

+ {#if auth.user}Nothing here yet — open a source or a grouping and tap Follow to fill this lane with what you care about. + {:else}Sign in and follow a few sources or topics, and this becomes your own calm lane.{/if} +

+ {:else} +

Nothing here right now — try another, or ease a boundary.

+ {/if} + {/key} + + {#if families.length} +
+

Explore Upbeat Bytes

+
+ {#each families as f (f.name)} + {@const tags = f.tags.filter((t) => t.count > 0)} + {#if tags.length} +
+

{f.name}

+

{f.description}

+
+ {#each tags as t (t.key)} + + {/each} +
+
+ {/if} + {/each} +
+
+ {/if} + + {#if !pwa.isStandalone && !pwa.dismissed && (pwa.canInstall || pwa.isIOS)} + + {/if} + {/if} +
+ + navigate('today')} onLatest={() => navigate('latest')} onPlay={() => goto('/play')} onYou={openAccount} user={auth.user} /> + + diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index ecfedb3..4a63172 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -1,1065 +1,8 @@ -
(showSaved = true)} onaccount={openAccount} user={auth.user} boundariesActive={filtersOn} /> - -{#if showSignIn} { showSignIn = false; if (!auth.user) pendingDigestOptIn = false; }} />{/if} -{#if showSaved && auth.user} (showSaved = false)} />{/if} -{#if showLanes && lanePool} - (showLanes = false)} /> -{/if} - -
- {#if navLanes.length} - (showLanes = true)} /> - {/if} - - {#if notice}

{notice}

{/if} - - {#if loading} -

Gathering the good news…

- {:else if error} -

{error}

- {:else} - {#key selected} -
-
-

{viewLabel}

- {#if viewSubtitle}

{viewSubtitle}

{/if} -
-
- - {#if auth.user && followTarget} - - {/if} - {#if selected !== 'today'} - - {/if} -
-
- - {#if searchOpen || selected === 'search'} - - {/if} - - {#if selected === 'today'} - {#if sinceCount > 0 && !sinceDismissed} -
-

- Since you were last here, {sinceCount} new calm read{sinceCount === 1 ? '' : 's'} came in. - {#if !sinceOpen}{/if} -

- -
- {#if sinceOpen && sinceItems.length} -
-

New since your last visit

-
- {#each sinceItems as a (a.id)} - drill('tag:' + t)} onsource={(id, name) => drill('source:' + id, { id, name })} onview={record} /> - {/each} -
-
- {/if} - {/if} - {#if brief?.items?.length} - {#if homeEditing || (!homeValue && !homePromptDismissed)} -
- {#if !homeValue}

Want your good news closer to home?

{/if} -
- - {#if pickCountry === 'US'} - - {/if} - - {#if homeValue} - {:else}{/if} -
-
- {:else if homeValue} -
- Good news closest first -
- {#each scopeStops as [s, label] (s)} - - {/each} -
- - - - -
- {/if} -
- drill('tag:' + t)} onsource={(id, name) => drill('source:' + id, { id, name })} onview={record} onimageerror={heroImageFailed} /> - {#if restArticles.length} -
- {#each restArticles as a, i (a.id)} - {#if a.section && a.section !== restArticles[i - 1]?.section && a.section !== heroArticle?.section} -

{sectionLabel(a.section)}

- {/if} - drill('tag:' + t)} onsource={(id, name) => drill('source:' + id, { id, name })} onview={record} /> - {/each} -
- {/if} -
-
-

✦ that's the good news for today ✦

-

You're caught up for now.

- {#if ritual.total} -
-

Today's calm set

-
    - {#each ritual.items as it (it.key)} -
  • - {#if it.done || it.key === 'brief'}{it.label}{:else}{it.label}{/if} -
  • - {/each} -
-

- {ritual.count === ritual.total ? `All ${ritual.total} enjoyed today` : `${ritual.count} of ${ritual.total} enjoyed today`} · fresh set tomorrow · make it yours -

-
- {/if} - {#if auth.user?.digest_enabled} -

Tomorrow's brief is headed to your inbox ☕

- {:else} - - {/if} -
- {:else} -

No highlights yet today — try a calmer filter, or check back soon.

- {/if} - {:else if feed.length} - {#if selected === 'latest'} - {#if homeEditing || (!homeValue && !homePromptDismissed)} -
-

Want good news closer to home?

-
- - {#if pickCountry === 'US'} - - {/if} - - {#if homeValue} - - {:else} - - {/if} -
-
- {:else if homeValue} -
📍 Showing local first · ·
- {/if} - {/if} -
- {#each feed as a, i (a.id)} - {#if a.section && a.section !== feed[i - 1]?.section} -

{sectionLabel(a.section)}

- {/if} - drill('tag:' + t)} onsource={(id, name) => drill('source:' + id, { id, name })} onview={record} /> - {/each} -
- {#if !feedDone} -
- -
- {:else} -

✦ you're all caught up ✦

- {/if} - {:else if selected === 'search'} -

No articles found for “{searchQuery}”. Try a different word, or a source name like “Nature”.

- {:else if selected === 'following'} -

- {#if auth.user}Nothing here yet — open a source or a grouping and tap Follow to fill this lane with what you care about. - {:else}Sign in and follow a few sources or topics, and this becomes your own calm lane.{/if} -

- {:else} -

Nothing here right now — try another, or ease a boundary.

- {/if} - {/key} - - {#if families.length} -
-

Explore Upbeat Bytes

-
- {#each families as f (f.name)} - {@const tags = f.tags.filter((t) => t.count > 0)} - {#if tags.length} -
-

{f.name}

-

{f.description}

-
- {#each tags as t (t.key)} - - {/each} -
-
- {/if} - {/each} -
-
- {/if} - - {#if !pwa.isStandalone && !pwa.dismissed && (pwa.canInstall || pwa.isIOS)} - - {/if} - {/if} -
- - navigate('today')} onLatest={() => navigate('latest')} onPlay={() => goto('/play')} onYou={openAccount} user={auth.user} /> - - + diff --git a/frontend/src/routes/news/+page.svelte b/frontend/src/routes/news/+page.svelte new file mode 100644 index 0000000..556224b --- /dev/null +++ b/frontend/src/routes/news/+page.svelte @@ -0,0 +1,12 @@ + + + + + + +