Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 29 additions & 28 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ on:
branches: [main]
paths:
- "packages/ui/**"
- "CHANGELOG.md"
- "cliff.toml"
- "pnpm-lock.yaml"
- ".github/workflows/publish.yml"
workflow_dispatch: {}
Expand Down Expand Up @@ -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<<CHANGELOG_EOF"

FEATS=$(git log "$RANGE" --pretty=format:"%s" --grep="^feat" 2>/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]"
Expand Down
222 changes: 202 additions & 20 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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/<name>.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/<name>.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/<name>.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/<name>.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
66 changes: 66 additions & 0 deletions apps/registry/app/atom.xml/route.ts
Original file line number Diff line number Diff line change
@@ -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("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&apos;");
}

function buildAtomDate(value?: string): string {
return new Date(value ?? Date.now()).toISOString();
}

async function buildAtomXml(): Promise<string> {
const releases = await getReleaseRecords();
const entries = releases
.map((release) => {
const link = releasePageUrl(release);
return [
" <entry>",
` <title>${escapeXml(release.title)}</title>`,
` <id>${escapeXml(link)}</id>`,
` <link href="${escapeXml(link)}" />`,
` <updated>${escapeXml(buildAtomDate(release.date))}</updated>`,
` <summary>${escapeXml(release.summary)}</summary>`,
" </entry>",
].join("\n");
})
.join("\n");

return [
'<?xml version="1.0" encoding="UTF-8"?>',
'<feed xmlns="http://www.w3.org/2005/Atom">',
" <title>VLLNT UI Releases</title>",
` <id>${SITE_URL}/releases</id>`,
` <link href="${SITE_URL}/releases" />`,
` <link rel="self" href="${SITE_URL}/atom.xml" />`,
` <updated>${escapeXml(feedUpdatedAt(releases))}</updated>`,
entries,
"</feed>",
].join("\n");
}

export const revalidate = 3600;

async function getAtomXml(): Promise<Response> {
const body = await buildAtomXml();
return new Response(body, { headers: ATOM_HEADERS });
}

export { getAtomXml as GET };
Loading
Loading