From 3e9fadcf795e26d50c8c287184d54a320b89eaad Mon Sep 17 00:00:00 2001 From: Jonathan Hvid Date: Mon, 11 May 2026 15:58:29 +0200 Subject: [PATCH] =?UTF-8?q?feat(component):=20RecentlyFromTheCouncil=20?= =?UTF-8?q?=E2=80=94=20pulls=20the=20most=20recent=20contribution?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit White-card surface. Header row with secondary 'Recently from the council' eyebrow and a terracotta 'All contributions →' link. Body is one card: 22px id-pigment avatar, full name, relative time, contribution-type pill (idea→copper, inspiration→ochre, question→indigo), then the body rendered as a serif italic pull-quote with a 40%-opacity terracotta left border, clamped to 3 lines via -webkit-line-clamp with markdown stripped before trimming. Footer shows the reaction count. Hidden entirely when the contributions table has no visible rows — no empty-state placeholder. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/RecentlyFromTheCouncil.astro | 123 ++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 src/components/RecentlyFromTheCouncil.astro diff --git a/src/components/RecentlyFromTheCouncil.astro b/src/components/RecentlyFromTheCouncil.astro new file mode 100644 index 0000000..16b1294 --- /dev/null +++ b/src/components/RecentlyFromTheCouncil.astro @@ -0,0 +1,123 @@ +--- +import Avatar from './Avatar.astro'; +import { getContributions } from '../lib/db'; +import { relativeTime, stripMarkdownLight } from '../lib/format'; + +const [latest] = getContributions({ sort: 'newest' }); + +const TYPE_LABEL = { idea: 'Idea', inspiration: 'Inspiration', question: 'Question' } as const; +const TYPE_PIGMENT = { + idea: 'var(--pigment-copper)', + inspiration: 'var(--pigment-ochre)', + question: 'var(--pigment-indigo)', +} as const; + +function trimQuote(body: string, max = 220): string { + const stripped = stripMarkdownLight(body); + if (stripped.length <= max) return stripped; + const cut = stripped.slice(0, max); + const last = Math.max(cut.lastIndexOf(' '), cut.lastIndexOf('.')); + return (last > max - 40 ? cut.slice(0, last) : cut).trim() + '…'; +} +--- +{latest && ( +
+
+

Recently from the council

+ All contributions → +
+ + + +
{trimQuote(latest.body_md)}
+ +

+ {latest.reaction_count === 0 + ? 'No reactions yet' + : `${latest.reaction_count} reaction${latest.reaction_count === 1 ? '' : 's'} from the council`} +

+
+)} + +