From 3d18139fd34db0084608dfad094bc457dc020bc1 Mon Sep 17 00:00:00 2001 From: bntvllnt Date: Tue, 12 May 2026 20:26:44 +0200 Subject: [PATCH 1/2] feat(releases): add changelog and release feeds --- .github/workflows/publish.yml | 57 +-- CHANGELOG.md | 222 ++++++++++- apps/registry/app/atom.xml/route.ts | 66 ++++ apps/registry/app/changelog/page.tsx | 235 ++++++++++++ apps/registry/app/docs/changelog/page.tsx | 5 + apps/registry/app/layout.tsx | 12 + apps/registry/app/llms-full.txt/route.ts | 195 ++++++---- apps/registry/app/llms.txt/route.ts | 182 +++++---- apps/registry/app/releases/page.tsx | 213 +++++++++++ apps/registry/app/rss.xml/route.ts | 68 ++++ apps/registry/app/sitemap.ts | 113 +++--- apps/registry/components/footer/footer.tsx | 15 +- apps/registry/components/header/header.tsx | 34 +- apps/registry/components/landing/landing.tsx | 86 ++++- apps/registry/lib/changelog.ts | 378 +++++++++++++++++++ cliff.toml | 45 +++ docs/RELEASING.md | 6 +- packages/ui/CHANGELOG.md | 1 + 18 files changed, 1666 insertions(+), 267 deletions(-) create mode 100644 apps/registry/app/atom.xml/route.ts create mode 100644 apps/registry/app/changelog/page.tsx create mode 100644 apps/registry/app/docs/changelog/page.tsx create mode 100644 apps/registry/app/releases/page.tsx create mode 100644 apps/registry/app/rss.xml/route.ts create mode 100644 apps/registry/lib/changelog.ts create mode 100644 cliff.toml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 913ef548..c37dcdb5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,6 +5,8 @@ on: branches: [main] paths: - "packages/ui/**" + - "CHANGELOG.md" + - "cliff.toml" - "pnpm-lock.yaml" - ".github/workflows/publish.yml" workflow_dispatch: {} @@ -118,43 +120,42 @@ jobs: exit 1 fi - - name: Generate changelog + - name: Read changelog release notes id: changelog + env: + VERSION: ${{ steps.version.outputs.version }} run: | - LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") - if [ -z "$LAST_TAG" ]; then - RANGE="HEAD" - else - RANGE="${LAST_TAG}..HEAD" + awk -v version="$VERSION" ' + /^## / { + if (capture) exit + if ($0 ~ "^## \\[?" version "\\]?") { + capture = 1 + next + } + } + capture { print } + ' CHANGELOG.md > /tmp/release-notes.md + + if [ ! -s /tmp/release-notes.md ]; then + echo "::error::CHANGELOG.md must include a ${VERSION} release section before dispatch." + exit 1 fi { echo "body</dev/null || true) - if [ -n "$FEATS" ]; then - echo "### Features" - echo "$FEATS" | sed 's/^/- /' - echo "" - fi - - FIXES=$(git log "$RANGE" --pretty=format:"%s" --grep="^fix" 2>/dev/null || true) - if [ -n "$FIXES" ]; then - echo "### Bug Fixes" - echo "$FIXES" | sed 's/^/- /' - echo "" - fi - - OTHERS=$(git log "$RANGE" --pretty=format:"%s" --invert-grep --grep="^feat" --grep="^fix" 2>/dev/null || true) - if [ -n "$OTHERS" ]; then - echo "### Other Changes" - echo "$OTHERS" | sed 's/^/- /' - echo "" - fi - + cat /tmp/release-notes.md echo "CHANGELOG_EOF" } >> "$GITHUB_OUTPUT" + - name: Confirm changelog entry exists + env: + VERSION: ${{ steps.version.outputs.version }} + run: | + if ! grep -Eq "^## \\[?${VERSION}\\]?|^## \\[?v${VERSION}\\]?" CHANGELOG.md packages/ui/CHANGELOG.md; then + echo "::error::CHANGELOG.md or packages/ui/CHANGELOG.md must include a ${VERSION} release section before dispatch." + exit 1 + fi + - name: Tag release run: | git config user.name "github-actions[bot]" diff --git a/CHANGELOG.md b/CHANGELOG.md index 315cc69d..d8a35fa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,32 +1,214 @@ # Changelog -The canonical changelog for the published `@vllnt/ui` package lives at [`packages/ui/CHANGELOG.md`](./packages/ui/CHANGELOG.md). It follows [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/) and [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +All notable changes to `@vllnt/ui` and the public registry site are documented +in this file. -This root file is a **pointer** — it tracks repo-wide events that don't ship in a published package (CI workflows, registry build pipeline, ROADMAP changes, agent-surface routes like `/llms.txt`, `/r/.json` schema additions). +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +Release automation can regenerate this file from Conventional Commits with +`git-cliff`. -## Where to look +## [Unreleased] -| Looking for… | File / source | -|---|---| -| Published package changes (`@vllnt/ui`) | [`packages/ui/CHANGELOG.md`](./packages/ui/CHANGELOG.md) | -| Roadmap toward the next release | [`ROADMAP.md`](./ROADMAP.md) | -| Per-release notes | [GitHub Releases](https://github.com/vllnt/ui/releases) | -| Live registry index | [`/r/registry.json`](https://ui.vllnt.ai/r/registry.json) (carries `version` + `generatedAt`) | -| Per-component descriptors | [`/r/.json`](https://ui.vllnt.ai/r/registry.json) (each carries `version` + `stability`) | +> Target version: **`0.3.0`** - canary only; not yet published. See +> [ROADMAP.md](./ROADMAP.md) for shipping criteria. -## [Unreleased] — repo-wide +### Added -> Target version: `0.3.0`. Tracks ROADMAP.md, see [#252](https://github.com/vllnt/ui/issues/252). +- **Release intelligence surface** - `/changelog`, `/releases`, `/rss.xml`, and + `/atom.xml` expose one changelog source through HTML, GitHub release cards, and + feed readers. `/docs/changelog` redirects to `/changelog`. +- **Agent-first surface** - `/robots.txt` allowing 11 AI crawlers, + `/sitemap.xml`, `/llms.txt`, `/llms-full.txt`, JSON-LD on every page, PWA + manifest, breadcrumbs, custom 404, canonical URLs per route, expanded root + metadata (keywords, robots tuning, OG/Twitter site-level config). +- **Codebase health gate** - `react-doctor` CI workflow with PR annotations and + score artifact, `pnpm doctor*` script suite, type-enforcement workflow on every + issue. +- **Registry-item richness** - every `/r/.json` now carries `version`, + `stability`, optional `a11y` schema, optional inline `examples`. Top-level + `/r/registry.json` carries `version` and `generatedAt`. +- **Site UX** - Header GitHub link, `/request-component` and `/report` pages with + prefilled-GitHub-issue forms, per-component "Report a bug" button. +- **Brand** - `DESIGN.md` at repo root with the canonical brand and design + guideline (color tokens, typography, motion, anti-patterns). +- **Hooks + utility primitives** - `CopyButton` with `useCopyToClipboard`, + `Banner` with `BannerAction`, `Kbd`, `EmptyState`, `DocumentSiblingNav`. +- **Pricing + identity cards** - `PricingTable`, `PricingPlan`, + `HistoricalFigureCard`, `CivilizationCard`, and `CivilizationComparison`. +- **Newsletter** - `NewsletterSignup` with a state-machine driven submit flow. +- **AI compound families** - `ModelComparison`, `PromptTemplates`, + `AgentActivity`, `AISidebar`, and `AIArtifact`. +- **Era / financial** - `EraComparison` and `TransactionList`. +- **Forms** - `Form`, `MultiSelect`, `SegmentedControl`, `TagsInput`, and + `AutoReload`. +- **Educational** - `KnowledgeCheck` inline question runner. +- **Charts + timelines** - `GanttChart`, `ParallelTimeline`, `Timeline`, + `TimelineItem`, `HistoricTimeline`, `InteractiveTimeline`, + `ChronologicalTimeline`, and `TreeView`. +- **Document + reading** - `PrimarySourceViewer` compound family. +- **Maps + geography** - `Map2D`, `ChoroplethMap`, `RouteMap`, `HeatMapOverlay`, + `GeographyQuizMap`, `MapTimeline`, `StoryMap`, and `Globe3D`. +- **Canvas foundation** - `CanvasShell`, `CanvasView`, `InfinitePlane`, + `ViewportBookmarks`, `WorldBreadcrumbs`, `TopBar`, `LeftRail`, `RightDock`, + `ZoomHud`, `MiniMapPanel`, and `WorkspaceSwitcher`. +- **Canvas selection + manipulation** - `SelectionHalo`, `SnapGuides`, + `FloatingToolbar`, `MultiSelectLasso`, `FollowMode`, and `HandoffBeacon`. +- **Canvas spatial objects** - `ObjectCard`, `ObjectHandle`, `AnchorPort`, + `ConnectorEdge`, `EdgeLabel`, and `GroupHull`. +- **Canvas collaboration primitives** - `LiveCursor`, `CommentPin`, + `PresenceSyncIndicator`, `PresenceStack`, `SelectionPresence`, and + `ThreadBubble`. +- **Inspector + dock panels** - `PropertySection`, `ObjectInspector`, + `RelationshipInspector`, `RuntimeOverviewPanel`, `RoutingAssignmentPanel`, and + `PolicyDeliveryPanel`. +- **Runtime overlays** - `AlertPulse`, `ThresholdRing`, `StickyMetric`, + `HeatOverlay`, `StateBadgeOverlay`, `MetricCluster`, `JarvisDock`, and + `ContextLens`. +- **Time navigation** - `TimelineScrubber`, `PlaybackGhost`, + `BottomActivityStrip`, and `RunTimeline`. +- Total component count: **225** (up from 140). -### Added +### Changed -- **Agent-first surface** — `/robots.txt` allowing 11 AI crawlers, `/sitemap.xml`, `/llms.txt`, `/llms-full.txt`, JSON-LD on every page, PWA manifest, breadcrumbs, custom 404, canonical URLs per route, expanded root metadata (keywords, robots tuning, OG/Twitter site-level config). -- **Codebase health gate** — `react-doctor` CI workflow with PR annotations + score artifact, `pnpm doctor*` script suite, type-enforcement workflow on every issue. -- **Registry-item richness** — every `/r/.json` now carries `version`, `stability`, optional `a11y` schema, optional inline `examples`. Top-level `/r/registry.json` carries `version` + `generatedAt`. -- **Site UX** — Header GitHub icon, `/request-component` and `/report` pages with prefilled-GitHub-issue forms, per-component "Report a bug" button. -- **Brand** — `DESIGN.md` at repo root with the canonical brand + design guideline (color tokens, typography, motion, anti-patterns). +- Registry installs use a **hybrid CLI strategy** - leaf component source is + inlined, sibling registry items resolve via `@vllnt/ui` to keep installs + minimal and dedupe shared primitives. ### Notes -- Site routes `/changelog`, `/releases`, and `/rss.xml` ship in a follow-up — this PR scopes #260 to the root pointer file. -- Once `0.3.0` is cut, the `Unreleased` heading flips to `0.3.0` and this section moves into [`packages/ui/CHANGELOG.md`](./packages/ui/CHANGELOG.md). +- `ROADMAP.md` tracks the open work gating the `0.3.0` cut. +- `0.3.0` requires the codebase-health gate (`react-doctor` CI + 0 errors), the + agent-first surface (`/llms.txt`, `/mcp`, sitemap, JSON-LD), and the rebuilt + landing page before shipping. + +## [0.2.1] - 2026-04-21 + +### Fixed + +- **Public API:** `ProgressCard` now re-exported from `@vllnt/ui` - it shipped + in `0.2.0`'s tarball but was missing from the barrel, so + `import { ProgressCard } from "@vllnt/ui"` resolved to `undefined`. + +### Docs + +- Correct casing for the AI family (`AIChatInput`, `AIMessageBubble`, + `AISourceCitation`, `AIStreamingText`, `AIToolCallDisplay`) and `SocialFAB` + across README, package README, CHANGELOG, and `llms-full.txt`. + +## [0.2.0] - 2026-04-21 + +### Added + +- **AI family** - `AIChatInput`, `AIMessageBubble`, `AISourceCitation`, + `AIStreamingText`, `AIToolCallDisplay`, `ThinkingBlock`, `ModelSelector`. +- **Financial family** - `CandlestickChart`, `MarketTreemap`, `OrderBook`, + `TickerTape`, `SparklineGrid`, `WalletCard`, `Watchlist`. +- **Ops / Status family** - `StatusBoard`, `StatusIndicator`, `LiveFeed`, + `WorldClockBar`, `SeverityBadge`, `RoleBadge`, `ScopeSelector`. +- **Billing & Plans family** - `SubscriptionCard`, `PlanBadge`, `CreditBadge`, + `UsageBreakdown`. +- **Animation family** - `AnimatedText`, `BorderBeam`, `Marquee`, + `NumberTicker`, and a standalone spinner library exported via `Spinner`. +- **Educational family** - `Flashcard`, `Stepper`, `Tour`, `Annotation`, + `CompletionDialog`, `TruncatedText`, `TableOfContentsPanel`. +- **Form additions** - `DatePicker`, `FileUpload`, `NumberInput`, + `PasswordInput`, `InlineInput`, `Combobox`, `Rating`. +- **Data / metric additions** - `DataTable`, `DataList`, `StatCard`, + `MetricGauge`, `ActivityHeatmap`, `ActivityLog`. +- **App shell additions** - `CategoryFilter`, `FilterBar`, `CookieConsent`, + `Slideshow`, `CountdownTimer`, `AvatarGroup`, `FloatingActionButton`, + `SocialFab`, `KeyboardShortcutsHelp`, `ShareDialog`, `HorizontalScrollRow`, + `ViewSwitcher`. +- Total component count: **144** (up from 93). +- Full Storybook integration with E2E smoke tests in CI. +- OG image generation for every registry page. +- Signed provenance attestations on every published artifact (sigstore). + +### Changed + +- Documentation domain moved to `ui.vllnt.ai` / `storybook.vllnt.ai`. +- Default package registry is the public npm registry (no longer GitHub + Packages). +- README component tables expanded to cover all 144 components organized by + family. + +### Fixed + +- Publish workflow now uses `npx --yes npm@latest publish` so OIDC trusted + publishing survives GitHub runners that ship pre-11.5.1 npm. + +## [0.1.11] - 2026-04 + +### Fixed + +- Re-extract tarball path from `pnpm pack` output for publish step. + +## [0.1.10] + +### Changed + +- Switched to `pnpm pack` and `npm publish` flow to support `publishConfig` with + OIDC auth. + +## [0.1.9] + +### Fixed + +- Use `pnpm publish` and repair release creation step in CI. + +## [0.1.8] + +### Fixed + +- Annotated tags and `--notes-file` for release creation. + +## [0.1.7] + +### Added + +- `HorizontalScrollRow`, `ViewSwitcher`, and `useHorizontalScroll` hook. + +### Fixed + +- `Comparison`: defensive prop validation to prevent runtime crash on malformed + input. + +## [0.1.6] + +### Fixed + +- Bundle-free build to preserve per-file `"use client"` directives in published + chunks. + +## [0.1.5] + +### Fixed + +- Add `"use client"` to all dist JS including `index.js`. + +## [0.1.4] + +### Fixed + +- Exclude `index.js` from `"use client"` banner. + +## [0.1.3] + +### Fixed + +- Add `"use client"` directive to published dist chunks. + +## [0.1.2] + +### Added + +- Initial public publish to the public npm registry. + +[0.2.1]: https://github.com/vllnt/ui/releases/tag/v0.2.1 +[0.2.0]: https://github.com/vllnt/ui/releases/tag/v0.2.0 +[0.1.11]: https://github.com/vllnt/ui/releases/tag/v0.1.11 +[0.1.10]: https://github.com/vllnt/ui/releases/tag/v0.1.10 +[0.1.9]: https://github.com/vllnt/ui/releases/tag/v0.1.9 +[0.1.8]: https://github.com/vllnt/ui/releases/tag/v0.1.8 +[0.1.2]: https://github.com/vllnt/ui/releases/tag/v0.1.2 diff --git a/apps/registry/app/atom.xml/route.ts b/apps/registry/app/atom.xml/route.ts new file mode 100644 index 00000000..e55cd706 --- /dev/null +++ b/apps/registry/app/atom.xml/route.ts @@ -0,0 +1,66 @@ +import { + feedUpdatedAt, + getReleaseRecords, + releasePageUrl, +} from "@/lib/changelog"; + +const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? "https://ui.vllnt.ai"; +const ATOM_HEADERS = new Headers([ + [ + "Cache-Control", + "public, max-age=0, s-maxage=3600, stale-while-revalidate=86400", + ], + ["Content-Type", "application/atom+xml; charset=utf-8"], +]); + +function escapeXml(value: string): string { + return value + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """) + .replaceAll("'", "'"); +} + +function buildAtomDate(value?: string): string { + return new Date(value ?? Date.now()).toISOString(); +} + +async function buildAtomXml(): Promise { + const releases = await getReleaseRecords(); + const entries = releases + .map((release) => { + const link = releasePageUrl(release); + return [ + " ", + ` ${escapeXml(release.title)}`, + ` ${escapeXml(link)}`, + ` `, + ` ${escapeXml(buildAtomDate(release.date))}`, + ` ${escapeXml(release.summary)}`, + " ", + ].join("\n"); + }) + .join("\n"); + + return [ + '', + '', + " VLLNT UI Releases", + ` ${SITE_URL}/releases`, + ` `, + ` `, + ` ${escapeXml(feedUpdatedAt(releases))}`, + entries, + "", + ].join("\n"); +} + +export const revalidate = 3600; + +async function getAtomXml(): Promise { + const body = await buildAtomXml(); + return new Response(body, { headers: ATOM_HEADERS }); +} + +export { getAtomXml as GET }; diff --git a/apps/registry/app/changelog/page.tsx b/apps/registry/app/changelog/page.tsx new file mode 100644 index 00000000..d79f9a34 --- /dev/null +++ b/apps/registry/app/changelog/page.tsx @@ -0,0 +1,235 @@ +import { Badge, Breadcrumb, Button, MDXContent, Sidebar } from "@vllnt/ui"; +import type { Metadata } from "next"; +import Link from "next/link"; + +import { type ChangelogTypeFilter, getChangelogEntries } from "@/lib/changelog"; +import { breadcrumbLd, jsonLdScript } from "@/lib/jsonld"; +import { generateOGMetadata, generateTwitterMetadata } from "@/lib/og"; +import { canonical } from "@/lib/seo"; +import { getSidebarSections } from "@/lib/sidebar-sections"; + +const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? "https://ui.vllnt.ai"; + +type SearchParameters = { + readonly from?: string; + readonly to?: string; + readonly type?: string; +}; + +const CHANGELOG_TYPES: readonly { + readonly label: string; + readonly value: ChangelogTypeFilter; +}[] = [ + { label: "All changes", value: "all" }, + { label: "Features", value: "feat" }, + { label: "Fixes", value: "fix" }, + { label: "Breaking", value: "breaking" }, +]; + +const DESCRIPTION = + "Browse VLLNT UI release notes from the canonical Keep a Changelog source, with filters for features, fixes, breaking changes, and release date."; + +export const metadata: Metadata = { + alternates: { canonical: canonical("/changelog") }, + description: DESCRIPTION, + openGraph: generateOGMetadata({ + description: DESCRIPTION, + title: "Changelog · VLLNT UI", + type: "docs", + }), + title: "Changelog · VLLNT UI", + twitter: generateTwitterMetadata({ + description: DESCRIPTION, + title: "Changelog · VLLNT UI", + type: "docs", + }), +}; + +function normalizeType(value?: string): ChangelogTypeFilter { + if (value === "breaking" || value === "feat" || value === "fix") { + return value; + } + return "all"; +} + +function FilterControls({ + from, + to, + type, +}: { + readonly from?: string; + readonly to?: string; + readonly type: ChangelogTypeFilter; +}) { + return ( +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ ); +} + +export default async function ChangelogPage({ + searchParams, +}: { + readonly searchParams: Promise; +}) { + const parameters = await searchParams; + const type = normalizeType(parameters.type); + const entries = await getChangelogEntries({ + from: parameters.from, + to: parameters.to, + type, + }); + + return ( + <> +