{
  "markdown": "# fenestra\n\n[![CI](https://github.com/richer-richard/fenestra/actions/workflows/ci.yml/badge.svg)](https://github.com/richer-richard/fenestra/actions/workflows/ci.yml)\n[![Crates.io](https://img.shields.io/crates/v/fenestra.svg)](https://crates.io/crates/fenestra)\n[![docs.rs](https://img.shields.io/docsrs/fenestra)](https://docs.rs/fenestra)\n[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#license)\n\n**A UI stack built for the agent loop.** Describe a UI as JSON, render it\nnatively, and check it in CI. There's no compile step in that loop, and no\nflaky screenshots.\n\nfenestra is a pure-Rust GUI framework. Its headless renderer is\ndeterministic, which means people and AI coding agents can both look at\nwhat they built and prove it is right. It also speaks\n[A2UI](https://a2ui.org), the open Agent-to-UI standard — this is its first\nnative Rust renderer ([`fenestra-a2ui`](fenestra-a2ui)). The widget kit and\ndesign system below are what that loop can produce.\n\n**[▶ Try the live demo](https://richer-richard.github.io/fenestra/)** — the\ndashboard and widget galleries, running in your browser over WebGPU. No\nDOM and no CSS: every pixel is vello on wgpu, from the same code as the\nnative window. There's also\n**[the book](https://richer-richard.github.io/fenestra/book/)** if you want\nthe guided tour.\n\n| Light | Dark |\n| --- | --- |\n| ![agent-session dashboard, light theme](https://raw.githubusercontent.com/richer-richard/fenestra/main/gallery/agent_dashboard_light.png) | ![agent-session dashboard, dark theme](https://raw.githubusercontent.com/richer-richard/fenestra/main/gallery/agent_dashboard_dark.png) |\n\n*That hero shot is a real tool, not a mockup: `examples/agent_dashboard.rs`\nis a live dashboard over an AI coding session, with a virtualized feed,\ncharts, and a live tail through the effect layer. The SaaS-style widget\nshowcase lives on as `examples/dashboard.rs`:*\n\n| Light | Dark |\n| --- | --- |\n| ![dashboard, light theme](https://raw.githubusercontent.com/richer-richard/fenestra/main/gallery/dashboard_light.png) | ![dashboard, dark theme](https://raw.githubusercontent.com/richer-richard/fenestra/main/gallery/dashboard_dark.png) |\n\nThere's no browser here, no webview, and no HTML or CSS parser. fenestra\ndraws everything itself with [vello] on wgpu. It lays out with [taffy]\n(flexbox and grid) and shapes text with [parley].\n\nOn top of that sits a themed widget kit that looks like a polished modern\nweb app: soft layered shadows, OKLCH color ramps, a real typographic\nhierarchy, transitions on hover and focus, and light and dark themes that\nboth get first-class treatment.\n\n[vello]: https://github.com/linebender/vello\n[taffy]: https://github.com/DioxusLabs/taffy\n[parley]: https://github.com/linebender/parley\n\n## Quickstart\n\n```rust\nuse fenestra::prelude::*;\n\nstruct Counter { n: i64 }\n\n#[derive(Clone)]\nenum Msg { Inc, Dec }\n\nimpl App for Counter {\n    type Msg = Msg;\n\n    fn update(&mut self, msg: Msg) {\n        match msg { Msg::Inc => self.n += 1, Msg::Dec => self.n -= 1 }\n    }\n\n    fn view(&self) -> Element<Msg> {\n        col().p(SP6).gap(SP4).items_center().children([\n            text(self.n.to_string()).size(TextSize::Xl2).weight(Weight::Semibold),\n            row().gap(SP3).children([\n                button(\"Decrement\").variant(ButtonVariant::Secondary).on_click(Msg::Dec),\n                button(\"Increment\").on_click(Msg::Inc),\n            ]),\n        ])\n    }\n}\n\nfn main() { fenestra::run(Counter { n: 0 }, WindowOptions::titled(\"Counter\")) }\n```\n\nRun `cargo add fenestra`, paste that in, then `cargo run`. If you'd rather\nstart from a template, `cargo generate richer-richard/fenestra-template`\ngives you one with a headless UI test and CI already set up.\n\nThe whole view is rebuilt, laid out and repainted on every redraw. There's\nno diffing and there are no macros, so everything autocompletes.\n\n## Agents can see what they build\n\nRendering `(element tree, theme, size)` to pixels is a pure function, and it\nruns without a window or display server:\n\n```rust\nuse fenestra::shell::{SyntheticEvent, render_app, render_element};\n\n// A picture of any element tree:\nlet image = render_element(my_view(), &Theme::dark(), (800, 600));\nimage.save(\"preview.png\")?;\n\n// Or drive a full app with scripted input and look at the result:\nlet image = render_app(\n    &mut app,\n    &[\n        SyntheticEvent::MouseMove { x: 50.0, y: 34.0 },\n        SyntheticEvent::MouseDown,\n        SyntheticEvent::MouseUp,\n        SyntheticEvent::Text(\"hello\".into()),\n    ],\n    (800, 600),\n    &Theme::light(),\n);\nassert_eq!(app.value, \"hello\");\n```\n\nHeadless rendering is deterministic (embedded fonts, fixed scale, reduced\nmotion), which makes pixel-exact golden tests practical — fenestra's own\nwidget kit is tested this way, on CI, with no GPU display attached.\n\nThe same pipeline backs a JSON authoring format called `fenestra/1`, for\nagents and tools that would rather not compile Rust. You describe a UI in\nJSON and [`fenestra-describe`](fenestra-describe) parses it into the same\n`Element` tree the builders produce. The format covers the whole kit: data\ntables, trees, popovers, command palettes, the OKLCH color picker, images,\ncharts, and markdown.\n\nFrom there you have a few ways in. `fenestra render` writes a PNG.\n`fenestra preview <file>` opens a window that re-renders every time you\nsave. And the [`fenestra-mcp`](fenestra-mcp) server hands an agent the whole\nloop — render, query, interact, verify — as fourteen MCP tools, including\n`render_a2ui`. You can watch motion too, not just single frames:\n`Harness::film` (also `fenestra film`, also the MCP `film_ui` tool) captures\na sequence with real motion turned on and composes it into one captioned\nfilmstrip.\n\n**What a headless render does and doesn't cover.** It renders a deliberate\nsubset of what a live window shows. That subset is exactly what makes it\ndeterministic, so it's worth knowing where the edges are.\n\nText uses the embedded fonts, so Latin comes out exact. The real monospace,\nCJK, emoji and RTL faces come from the OS, and those only show up in a real\nwindow. Motion is always forced to reduced. Pixels are referenced against\nmacOS/Metal, with Linux/lavapipe allowed a wider tolerance. Scale isn't\npinned — `render_element_scaled` runs the same two-pass pipeline at any\ndevice scale, so you can catch retina-only regressions like hairlines and\nblur radii headlessly as well.\n\nTwo things look different outside that path. The full Liquid-Glass optics\n(backdrop blur, edge lensing, adaptive vibrancy) only render in the\nheadless golden path; a live single-pass window gives you the translucent\ntint plus the specular rim and sheen. On the web, copying out reaches the\nsystem clipboard but pasting in from other apps stays inside the app, glass\nmatches the native live window, and AccessKit is still waiting on an\nupstream web adapter.\n\nSo headless is the right thing to trust for layout, semantics, color, and\nthe large majority of pixels. Check non-Latin text, monospace, and full\nglass in a real window. ARCHITECTURE.md keeps the precise ledger.\n\n**Working with an AI agent?** [AGENTS.md](AGENTS.md) is the manual for the\nbuild → render → look → verify loop (and [llms.txt](llms.txt) for\ncontext loaders).\n\n## Philosophy: web aesthetics without the web platform\n\nThe way the web *looks* — soft elevation, tinted neutrals, OKLCH ramps,\nspacing on a 4px grid, focus rings, easing in the 120–300ms range — is the\nbest-tested visual language we have in software. The web *platform* is a\nheavy way to get hold of it.\n\nSo fenestra encodes that language as typed Rust values instead. A `Theme`\nis generated from a single accent hue. Spacing, radius, shadow and motion\ncome from tokens. The builder vocabulary (`row()`, `.p(SP4)`,\n`.rounded(R_MD)`, `.shadow(ShadowToken::Sm)`) is small enough to memorize\nand regular enough that rust-analyzer — or a language model — can\nautocomplete it. Every widget routes every color through the theme, so\nflipping one `Mode` turns the whole app dark.\n\n## The kit\n\nEvery widget below ships in every state, in both themes.\n\n**Controls.** Button, IconButton, Checkbox, Switch, Radio, Slider,\nSegmentedControl, Select, and a Color Picker with an OKLCH\nlightness×chroma pad, hue and alpha strips, and forgiving hex entry.\n\n**Text entry.** TextInput (parley editing, clipboard, IME) and TextArea\n(multiline, auto-growing).\n\n**Surfaces and feedback.** Tooltip, Modal (focus trap and backdrop),\nToasts, Tabs, Card, StatCard, Badge, Avatar, StatusIndicator with a live\npulse, Kbd key-caps, Skeleton loaders, Divider, Spinner, Table, Callout,\nand Progress — including a Material-3 Expressive wavy bar. Plus a vendored\nsubset of Lucide icons.\n\n| | |\n| --- | --- |\n| ![controls, light](https://raw.githubusercontent.com/richer-richard/fenestra/main/gallery/controls_light.png) | ![controls, dark](https://raw.githubusercontent.com/richer-richard/fenestra/main/gallery/controls_dark.png) |\n| ![display widgets, light](https://raw.githubusercontent.com/richer-richard/fenestra/main/gallery/display_light.png) | ![display widgets, dark](https://raw.githubusercontent.com/richer-richard/fenestra/main/gallery/display_dark.png) |\n| ![segmented control, status, skeletons, key-caps, wavy progress — light](https://raw.githubusercontent.com/richer-richard/fenestra/main/gallery/feedback_light.png) | ![segmented control, status, skeletons, key-caps, wavy progress — dark](https://raw.githubusercontent.com/richer-richard/fenestra/main/gallery/feedback_dark.png) |\n\nRegenerate this corpus any time with `cargo run --example gallery` — it\nrenders headlessly.\n\n## Motion\n\n`fenestra-motion` renders frame-pure compositions headlessly, with no live\nwindow and no screen recorder involved. The same pipeline is what `fenestra\nfilm` and the MCP `film_ui` tool use to let an agent watch a transition\nplay.\n\nBelow is a `fenestra-charts` bar chart, rebuilt every frame from\nrank-sorted, track-interpolated data:\n\n![chart race motion demo](https://raw.githubusercontent.com/richer-richard/fenestra/main/gallery/chart_race_demo.gif)\n\n`cargo run -p fenestra-motion --example chart_race -- --mp4` renders this\nexact sequence — the lead changes hands partway through, verified\nstructurally in the example itself, not just eyeballed. Two more shipped\ndemos render the same way: a broadcast lower-third\n(`examples/lower_third.rs`) and a per-word title stagger\n(`examples/title_stagger.rs`).\n\n## Workspace\n\n| Crate | Role |\n| --- | --- |\n| `fenestra` | Facade: prelude, `run()`, examples |\n| `fenestra-core` | Element IR, theme/tokens, layout, text, paint, input, transitions |\n| `fenestra-shell` | winit + wgpu window runner and the headless renderer |\n| `fenestra-kit` | The themed widget kit, built only on core's public API |\n| `fenestra-charts` | Sparklines, line and bar charts — the reference third-party widget crate |\n| `fenestra-markdown` | CommonMark rendered as native `fenestra` elements |\n| `fenestra-looks` | Six ready-made design languages (product, editorial, terminal, console, warm-editorial, playful), applied in one call |\n| `fenestra-describe` | Parses `fenestra/1` JSON into the same `Element` tree the builders produce |\n| `fenestra-a2ui` | A native Rust renderer for [A2UI](https://a2ui.org) v0.9 — the open Agent-to-UI standard |\n| `fenestra-render` | The `fenestra` CLI: render, preview, film, query, verify, lint — from the command line |\n| `fenestra-mcp` | MCP server exposing render, query, interact, and verify as fourteen tools to AI agents |\n| `fenestra-motion` | Frame-pure motion graphics: timelines, headless frame/video rendering, temporal lints, the `motion` CLI |\n| `fenestra-anim` | Keyframe animation math — easing, springs, an exact rational timebase |\n\n`fenestra-anim` is versioned on its own (0.1.x). It's a standalone leaf\ncrate that depends on no fenestra crate at all, and not on wgpu, vello,\nparley, taffy or winit either. It was pulled out of `fenestra-core` and\n`fenestra-motion` so that anything sampling by frame or tick — in this\nworkspace or well outside it — can depend on just the animation math.\n`fenestra-mcp` is versioned separately too, so the MCP server can ship on\nits own schedule.\n\nSee [ARCHITECTURE.md](ARCHITECTURE.md) for how the pipeline, widget\nidentity, transitions, and overlays work — recorded decision-by-decision as\nthe framework was built — and [BENCHMARKS.md](BENCHMARKS.md) for honest\nframe-cost numbers (a full screen rebuilds, lays out, and paints in ~0.3 ms;\n100k-row lists virtualize to ~0.09 ms).\n\n## Design range\n\nSame framework, same tokens, a different design language.\n\nThe `fenestra-looks` crate bundles six ready-made voices — product,\neditorial, terminal, console, warm-editorial and playful — and you can\nenumerate them with `all()`. Past that, single knobs re-skin the whole kit.\n`Theme::with_radius(RadiusScale::sharp())` gives you un-rounded tech chrome.\n`Theme::with_elevation(Elevation::Flat)` draws surfaces with borders instead\nof shadows. `Theme::duotone` swaps neutral grays for atmospheric fields. If\nyou want your own display faces, register them under font roles with\n`Fonts::register`.\n\nBelow is the opposite end of the range from the soft default dashboard\nabove: a sharp, hairline-ruled **console** in slate, with one lime accent\nand mono numerals. Rendered headlessly and golden-tested, like everything\nelse here.\n\n| Light | Dark |\n| --- | --- |\n| ![sharp console, light theme](https://raw.githubusercontent.com/richer-richard/fenestra/main/gallery/console_light.png) | ![sharp console, dark theme](https://raw.githubusercontent.com/richer-richard/fenestra/main/gallery/console_dark.png) |\n\n## Composition, commands, accessibility\n\nComponents written around their own message type compose with\n`Element::map`.\n\nBackground work comes in through `App::init`, which hands the app a\ncloneable `Proxy<Msg>`. Spawn a thread, send messages back, and the window\nrepaints — `examples/clock.rs` and `examples/toasts.rs` both do this.\n\nEvery widget exposes its role, state and name in two directions: headlessly\nthrough `Frame::access_tree()`, so you can assert in CI that your UI is\nlabeled, and to real assistive technology through AccessKit in the windowed\nrunner.\n\nAmbient motion comes from looping `Keyframes` timelines, and images from\n`image_rgba8` (use `.rounded_full()` for round avatars).\n\n## Status\n\nfenestra is at 0.41.0. [ARCHITECTURE.md](ARCHITECTURE.md) records how it got\nthere, decision by decision.\n\nHere's what has shipped:\n\n- The interactive widget kit, in light and dark themes.\n- Six ready-made design languages (`fenestra-looks`) and a frosted-glass\n  material system.\n- Charts and markdown, as reference third-party widget crates.\n- The `fenestra/1` JSON format, which can author the entire kit. It's\n  parsed by `fenestra-describe`, then rendered and verified by the\n  `fenestra` CLI and the fourteen `fenestra-mcp` tools.\n- An A2UI v0.9 renderer (`fenestra-a2ui`).\n- An effect layer (`Cmd`/`Sub`) with a deterministic test harness.\n- Declarative native menus on macOS, hi-DPI headless rendering at any\n  scale, and a live-reload `fenestra preview` window.\n- `fenestra-motion`, for frame-pure motion graphics with temporal lints and\n  filmstrip capture.\n\nEvery change clears the same gate before it merges: `cargo fmt --check`,\n`clippy -D warnings`, the full test suite, and a headless golden-PNG\ncomparison on macOS/Metal and Linux/lavapipe. `cargo audit` and `cargo deny`\nrun on every push and once a week on top of that.\n\nOpen work is kept as a ranked list in ARCHITECTURE.md's \"Deferred\" notes.\nThe gaps worth knowing about up front: A2UI's `DateTimeInput` is an ISO text\nfield rather than a calendar, obscured text fields render unmasked (the\nrenderer reports this as a note rather than hiding it), and AccessKit on the\nweb is waiting on an upstream adapter.\n\n## License\n\nMIT or Apache-2.0, at your option. The embedded Inter font, the Playfair\nDisplay faces (poster and editorial looks), the Fraunces variable text serif\n(the `opsz`/optical-sizing serif in the warm-editorial look), and JetBrains\nMono (terminal look) are licensed under the SIL Open Font License 1.1; the\nvendored Lucide icon path data is ISC (see `fenestra-kit/LICENSE-LUCIDE.txt`).\n",
  "bytes": 16234,
  "sha": "d477bafe3ffd3c4dc1ddfce0f491c5f3b0b83d21d93b76c2c91df4231fe0bc93",
  "repo_slug": "richer-richard/fenestra",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_richer_richard_fenestra_mcp_dbba66e1/readme"
}