diff --git a/src/components/RoadmapInMotion.astro b/src/components/RoadmapInMotion.astro new file mode 100644 index 0000000..216c9a4 --- /dev/null +++ b/src/components/RoadmapInMotion.astro @@ -0,0 +1,69 @@ +--- +import type { RoadmapItemWithAttribution } from '../lib/db'; + +interface Props { + items: RoadmapItemWithAttribution[]; +} + +const { items } = Astro.props; + +/** First sentence of a description — naive but matches the user's needs. + * Returns '' for null/empty input. Falls back to a 200-char slice if no + * sentence-ending punctuation is found in a reasonable window. */ +function firstSentenceOf(text: string | null): string { + if (!text) return ''; + const trimmed = text.trim(); + const match = trimmed.match(/^[^.!?]*[.!?](?=\s|$)/); + return match ? match[0] : trimmed.slice(0, 200); +} + +// Most recent shipping item, in display_order (same selection rule as +// the .rr-current marker on the route). +let currentItem: RoadmapItemWithAttribution | null = null; +items.forEach((it) => { if (it.status === 'shipping') currentItem = it; }); + +const line = currentItem ? firstSentenceOf(currentItem.description) : ''; +const visible = !!currentItem && line.length > 0; +--- +{visible && ( +
+ In motion right now +

{line}

+
+)} + + diff --git a/src/components/admin/RoadmapTab.astro b/src/components/admin/RoadmapTab.astro index db42c6b..91f4872 100644 --- a/src/components/admin/RoadmapTab.astro +++ b/src/components/admin/RoadmapTab.astro @@ -64,6 +64,7 @@ const grouped: Record = {
+ For shipping items: the first sentence appears on /roadmap as the "In motion right now" line. Make it count.
diff --git a/src/pages/roadmap.astro b/src/pages/roadmap.astro index efdf520..771b7d3 100644 --- a/src/pages/roadmap.astro +++ b/src/pages/roadmap.astro @@ -1,6 +1,7 @@ --- import AppLayout from '../layouts/AppLayout.astro'; import LatestDispatchBanner from '../components/LatestDispatchBanner.astro'; +import RoadmapInMotion from '../components/RoadmapInMotion.astro'; import RoadmapRoute from '../components/RoadmapRoute.astro'; import { getAllRoadmapItems } from '../lib/db'; @@ -18,13 +19,15 @@ const items = getAllRoadmapItems()

What we are building.

A live picture of the work. What's in motion, what's queued, - what we're still thinking about. Hover any milestone for the - full story. + what we're still thinking about. Tap or hover any milestone + for the full story.

+ +
@@ -63,14 +66,14 @@ const items = getAllRoadmapItems() max-width: 540px; } - /* The banner already has its own bottom-margin via the layout in - the parent. Add the spec'd 56px below it before the route header. */ - .page :global(.banner) { margin-bottom: 56px; } + /* In-motion strip lives between banner and route; banner margin + tightens to 40px because the strip carries its own 36px below. */ + .page :global(.banner) { margin-bottom: 40px; } @media (max-width: 767px) { .page { padding: 32px 24px 64px; } .page-title { font-size: 36px; } .page-header { margin-bottom: 28px; } - .page :global(.banner) { margin-bottom: 36px; } + .page :global(.banner) { margin-bottom: 28px; } }