Repository navigation
Conversation
captureSpans() output → a palette-indexed, per-line delta format that the site replays; round trips are exact and the gzip size is measurable against the budget.
The myapp stack on a developer's Mac: the api serves requests while its memory creeps up, the worker crashes on a real-looking bug at tick 12, Kestrel retries it and it is ready again by tick 18. A fixed clock and seeded data make every run identical.
bun run website:generate renders the real App headlessly (the bench-ui pattern), presses Tab and v like a user, and captures 30 delta frames (6.8 KB gzipped) plus compact, wide and NO_COLOR frames; it also writes the key bindings, real CLI output and the palette as CSS variables. Runs in UTC so every machine produces identical files.
Tailwind 4 themed from the generated tokens.css, self-hosted monospace subsets with braille and box drawing (Cascadia Mono and DejaVu, renamed as their licences require, 28 KB total), security headers with a CSP that allows only this origin, the nav, footer and layout. Exact dependency versions, no install scripts, no analytics.
npx @devclubnst/ui@2.0.1 add for code-block, toast, animated-button, glowing-badge, dither, spotlight-card, bento-grid, animated-counter, scales, mac-switch, mac-slider, task-list, file-tree, dotted-accordion, spotlight-search and reveal-sheet. They are our source now; adapted to strict TypeScript and the lint rules (a keyboard-inaccessible click handler removed from the accordion, a guarded uniform setter in dither). Their dependencies pinned exactly; no network, storage, eval or HTML injection in any of them.
MacSwitch and MacSlider gain an xs size and a label (aria-label); the slider also takes valueText, so it announces 'standard' rather than 1. AnimatedButton gets an AnimatedLink twin, since a button inside a link is invalid. Dark variants follow the site's dark class.
Frame 1 is server-rendered; the 30 frames load as a separate chunk and replay at 1 Hz, pausing off-screen, in a hidden tab and under reduced motion. Live, Colour (Kestrel's NO_COLOR mode) and Width (compact, standard, wide) switch between real captures. The window tilts towards the cursor, the headline rises word by word, and npm, npx and curl install tabs copy with a toast.
The frame where the worker is retrying stays pinned while five steps light up CPU, memory, the managed stack, logs and ports. The spotlight is positioned in terminal cells inside the frame, so it lands on Kestrel's own boxes at any size; the steps are buttons without pin.
… numbers htop and pm2 slide together into Kestrel. Each feature card holds a small working demo: core meters, dependency-order start, a searchable logs panel, and the type-the-name confirmation Kestrel uses for another user's process (it kills nothing). Counters roll up to the measured figures.
The first run is typed out with Kestrel's real, captured output and ticked off in a task list. The kestrel.json example explains each key on hover or focus and lights up its process in the dependency graph, next to a project tree with .kestrel/logs.
Copyable verify commands for provenance and attestations, the platform matrix linking each npm package, answers to the questions people ask, and a last install command.
Section extraction that fails when an expected heading disappears, links rewritten to site routes or GitHub, heading anchors and a table of contents, wide tables that scroll, and build-time Shiki highlighting in Kestrel's palette. Raw HTML in markdown is dropped; tests prove script and event-handler markup never reach a page.
…ter labels onSelect passes the whole item (so a result can navigate), filterLabels renames the four filters, and autoFocus serves a search dialog.
/docs (the npm README from Install on), /docs/config (docs/CONFIG.md), /docs/commands (the real kestrel --help), /docs/keys (the app's key map: press a key and its rows light up) and /changelog as a timeline. A docs shell with an animated sidebar, an 'on this page' list that follows your reading, copy buttons on code, prev/next and edit-on-GitHub.
Cmd-K or / searches every page, section, command and key (DevClub spotlight search in a reveal sheet), ? lists the shortcuts, and g h / g d / g c move around. Keys are ignored while typing; the nav has a search button too.
'kestrel: no such page (exit 2)' with the pages people usually want; titled and noindex.
The Open Graph image is drawn with next/og: the wordmark, the headline, the install command and a CPU skyline in the app's meter colours.
The task list's live region was an li with role=status inside the ul (a non-item child); it is now a p after the list. The accordion's index numbers meet contrast on dark.
…inline links Small muted text used overlay0 (4.3:1 on black); it now uses k-muted (overlay1, 5.6:1), and overlay0 is decoration only, as the plan said. Code that scrolls takes focus, one-line commands wrap instead of scrolling, the tour's inactive steps keep their contrast, and the inline security link is underlined.
Every route: one h1, no console errors, nothing loaded from another origin, and no serious axe violations (the captured terminal frames are excluded: they are the product's own output, aria-hidden with a text alternative). Plus the hero replay and reduced motion, the colour and width controls, tabs and copy, Cmd-K search, the ? sheet, the keys page and 404.
website/ changes (and the documents the site renders) run only the website job, not the product suite; UI, --help, version and capture-script changes run it as well. Pinned by tests.
The job checks the committed captures are current (bun run website:generate + git diff), then lints, type-checks, unit-tests, builds and runs the Playwright + axe suite. The font script is shellchecked. vercel.json builds with Bun and skips deploys when nothing the site depends on changed.
| /** A link from a repository document, rewritten for the site: its own pages locally, other files on GitHub. */ | ||
| export function rewriteHref(href: string, sourceFile: string): string { | ||
| if (href.startsWith('#') || /^(mailto|tel):/.test(href)) return href; | ||
| let target = href; |
bun test treats bare arguments as substring filters, so tests/unit also matched website/tests/unit, whose Vitest files need the site's own dependencies. ./tests/... names the directories themselves.
Defence in depth from the security review: rewriteHref drops javascript:, vbscript: and data: links outright instead of relying on them being rewritten into a GitHub URL.
From the code review: a '## Heading' on the last line with no newline lost its last character (so excluding it by name stopped matching), and a document that begins with a '## ' section dropped it. Both pinned by tests. Also uses import.meta.dirname in the Vitest config and ignores the agent notes Next's dev server writes into website/.
The vendored github-activity generated random activity when it had no data. It now draws only the days it is given, from pure, tested helpers (src/lib/heatmap.ts): Sunday-first weeks, GitHub-style quarter levels, month labels, and a text summary that is the grid's accessible name. Kestrel's green scale; no spring or scale-up under reduced motion.
Added from DevClub UI: noise, gooey-nav, profile-menu, hook-sidebar, proximity-sidebar, pixel-card, sparkle-button and orb. New pinned deps: lenis 1.3.26, thinking-orbs 0.3.2, voice-glow 0.2.1. Adaptations: - profile-menu: no theme switcher or localStorage, no demo sections, remote avatar, email or links; a real button with aria-expanded and aria-haspopup, menu/menuitem roles, a plain-key shortcut (m) that ignores typing and never collides with Cmd/Ctrl+K - gooey-nav: a named tab list (not a nav landmark), aria-controls, and arrow keys move focus with the selection - spotlight-search: the combobox pattern (listbox, options, aria-activedescendant, a live result count), instant under reduced motion - proximity-sidebar: dashes are the tab stops, arrow keys walk them, a visible focus ring - sparkle-button, dither: reduced-motion guards; orb: display only - pixel-card: not a tab stop itself; lights up when a control inside it is focused - shadcn-style theme tokens (border, muted-foreground…) mapped to the Kestrel palette
- useMotionGate: one site-wide answer to how much motion a page may make (reduced motion, an effects switch, low-power devices); off on the server so hydration always matches - SmoothScroll: Lenis on GSAP's ticker, synced to ScrollTrigger; dialogs scroll natively and a modal freezes the page behind it - Scramble and LineReveal (ScrambleText, SplitText) for section titles and leads, once on entry; the final text is in the markup from the start - Magnetic hover for a fine pointer; a scroll meter drawn as a Kestrel meter - the search E2E targets the combobox by name The full-page canvas grain is dropped (and the vendored noise with it): it recomposited the whole page 12 times a second and made the E2E suite time out under parallel load. Without it the suite is stable and twice as fast.
Self-hosted from the pinned, checksummed 2.7.1 release (SIL OFL, no Reserved Font Name): the variable font with every weight, subset to Latin, 23 KB, preloaded with the HTML. Kestrel Mono stays for terminal frames. The font build is now reproducible: the rename step no longer stamps the build time into each font, so rebuilding gives byte-identical files (the mono fonts change once, by that timestamp only).
The hero is now one signature visual: KESTREL set in a 43×9 grid of meter cells, coloured by Kestrel's own meter bands (a unit test checks them against ui/logic/charts.js). Pure SVG and CSS: it fills in column by column from the first paint, a few empty cells flicker, the pointer leaves a trail; no JavaScript, still under reduced motion. Below it: one line, the install command, a white pill and a ghost pill, then the live dashboard. A quiet radial glow replaces the WebGL light field (dither, ogl, HeroBackdrop and RiseText removed).
Following AlgoLedger's structure: - the hero is the first screen and only the name: KESTREL in meter cells above Kestrel's CPU history graph drawn in the same cells, coloured by height like the product's graph (a unit test checks both colour rules against ui/) and scrolling once a second; KES / TREL on phones; a caption and a scroll cue - a stat strip of three checkable facts (the measured ~3% CPU, 1 app, 0 install scripts) - how it works: four tabs in one terminal, pinned and walked by scroll on desktop: install, kestrel init (captured output), the live dashboard, and the captured crash and recovery with 'Crash it again' Removed what this replaces: the hero player and its colour and width controls, the quick start (which showed npm output that was never captured), the dashboard tour, and the task-list, mac-switch and mac-slider components. The terminal window is flat; the scenario's frame numbers live with the frame data (server-safe).
Replaces the vendored spotlight search (a full-width sheet, a serif title, filter bubbles for its demo's Apps/Folders/Workspaces, results clipped by the sheet, titles only) with a centred, keyboard-first palette: a › prompt, results grouped as Docs / Configuration / Commands / Keys / Changelog, the best group first, and a ↑↓ ↵ esc footer. The combobox pattern, as before. Sections now carry the start of their text, so a query finds what a section says: 'crash' finds Restarts. The index is a static /search.json fetched when the palette first opens, instead of being embedded in every page. The ? shortcuts list uses the same panel; reveal-sheet and spotlight-search are gone.
- the footer is the closing screen: 'One command, then kestrel.' with the install command and pills, link columns, and the name once more in the hero's meter cells, dimmed until the pointer passes over it; it replaces the separate closing CTA - section headers follow the new language: centred, a pill bar, a letter-spaced eyebrow, a bold title - removed Why one app, and the Numbers grid (the stat strip under the hero states the same figures); the platforms section is 'Packages' - MergeCards, Numbers, ClosingCta, animated-counter and animated-button are gone
… search) The DevClub spotlight search is back, used the way macOS does it: a floating bar over a lightly blurred page (no sheet, no title box), the filter bubbles parting from the bar as it opens, results below in the same glass. Adapted: the filters are the site's own groups (Docs, Config, Commands, Keys), ranking comes from lib/search.ts (so 'crash' still finds Restarts), the geometry scales with width and height, and narrow screens keep the full-width bar without bubbles.
The name, the tagline and the one install command (with copy), three short link columns, and a single mono line: year, licence, version, no telemetry, back to top. The big headline and the giant meter-cell mark are gone.
The nav is the name on the left and two pills on the right: Search (⌘K) and Menu. Menu opens the site's few places over the whole screen, big and numbered (Home, How it works, Features, Docs, Changelog), with the project links, the install command and Star on GitHub beside them. A Radix dialog: focus stays inside, Esc and Close return.
…used - press q anywhere (not while typing, not on the keys page) and Kestrel's own quit dialog appears: captured by the generator, which now presses q with the stack running (hero-quit.json). y 'quits' the page for a moment, Esc cancels - removed vendored components nothing uses any more (code-block, gooey-nav, hook-sidebar, orb, pixel-card, profile-menu, proximity-sidebar, scales, sparkle-button, toast, button) and their dependencies (thinking-orbs, voice-glow, @radix-ui/react-icons, @radix-ui/react-slot, class-variance-authority, next-themes) - github-activity and lib/heatmap stay for the Built in the open section, which waits until the repository has a few weeks of history
The website job is removed: CI covers the package, and Vercel builds the site. A change under website/ now runs no job (only the final CI result check), like a docs change. The change detector drops its website output, and shellcheck no longer covers website/scripts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
The Kestrel website in
website/: a Next.js 16 static site (landing page and docs), built from the product itself.The rule it follows: everything on it that looks like Kestrel is Kestrel. These are all captured from the app by
bun run website:generate, and CI fails if they're stale:--helpand thekestrel initoutput;The landing page (minimal, modelled on AlgoLedger)
kestrel init, the live dashboard, the captured crash and its recovery), pinned and walked by scrolling on desktop./opens DevClub's spotlight search, shown the way macOS shows Spotlight. It matches titles and section text, from a static/search.json.qfor Kestrel's real quit dialog.Docs
Guide, Configuration, Commands, Keys and Changelog, rendered at build time from the repository's own Markdown and captured CLI data.
Guarantees
'unsafe-inline'for scripts, which is documented), HSTS and no framing.Test plan
bun run website:generateis deterministic, and the CI freshness check covershero-quit.json.website/: lint, typecheck, unit tests (49) and the build pass locally.websiteand analytics off, then check the live headers and Lighthouse.