From ba6b47832084c9252d0d25fe0bfccb65b143713a Mon Sep 17 00:00:00 2001
From: Michael Freno
Date: Mon, 5 Oct 2026 09:27:30 -0400
Subject: [PATCH] nook: collapse changelog history into a fixed-height scroller
A long appcast made the page grow with every release and pushed
historical versions far below the fold. Versions now collapse to a
header row inside a fixed-height vertical panel (max-h 70vh): expanding
one never shifts the page, and history is browsed by scrolling inside
it. The newest release starts open; notes stay mounted and toggle via
hidden rather than unmounting, so crawlers still see every entry.
Tests: vinxi build clean; tsc clean.
Issue: FRE-13
---
src/routes/nook/changelog.tsx | 46 +++++++++++++++++++++++++++--------
1 file changed, 36 insertions(+), 10 deletions(-)
diff --git a/src/routes/nook/changelog.tsx b/src/routes/nook/changelog.tsx
index 4dd4867..9ee190e 100644
--- a/src/routes/nook/changelog.tsx
+++ b/src/routes/nook/changelog.tsx
@@ -5,8 +5,13 @@
* launch, so this page renders exactly what Sparkle publishes: one source
* of truth, no second place to write release notes. Doubles as the raw
* material for the release-announcement job (marketing/README.md).
+ *
+ * Layout: the version list lives in a fixed-height vertical scroller, so
+ * expanding a release never shifts the page — history is browsed inside
+ * the panel. Rows collapse to their header; notes stay in the DOM
+ * (visibility toggled, not unmounted) so crawlers still see every entry.
*/
-import { For, Show } from "solid-js";
+import { For, Show, createSignal } from "solid-js";
import { query, createAsync } from "@solidjs/router";
import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
@@ -20,6 +25,11 @@ const getReleases = query(async () => {
export default function NookChangelogPage() {
const releases = createAsync(() => getReleases());
+ // Newest release starts open; every other row starts collapsed.
+ const [openVersion, setOpenVersion] = createSignal(null);
+
+ const isOpen = (version: string, index: number) =>
+ openVersion() === null ? index === 0 : openVersion() === version;
return (
<>
@@ -47,12 +57,24 @@ export default function NookChangelogPage() {
}
>
-
+
- {(release) => (
-
-
-
+ {(release, i) => (
+
+
+
{/* Notes are the publisher's own HTML, written to the same
- S3 appcast the Sparkle updater consumes. */}
+ S3 appcast the Sparkle updater consumes. Kept mounted
+ and toggled via `hidden` so the content stays crawlable. */}