GPUI Box GitHub

Coverage

What a general-purpose desktop UI library is expected to provide, what this one provides, and what is deliberately out of scope. GPUI Box is the application substrate: if a downstream desktop or browser-hosted product needs a surface to exist, the surface belongs here unless it is a host fact, a locale fact, a transport, or a platform chrome the OS already owns. docs/components.md describes the components themselves; this file exists so a gap is a recorded decision rather than an oversight.

Frozen visual retirement has a paint-only framework primitive: PaintRecording owns quads/gradients, paths, shadows, underlines, text/image sprites and their capture-visible clips/resources. A dynamic offscreen demonstrator lives in tools/headless-visual/src/paint_recording_tests.rs; it is infrastructure evidence, not a Kit component exhibit. Live registrations disappear at removal, even while the same visible pixels are replayed. Callers own exit timing, recording budgets, reinsertion and reduced-motion disposal.

The remaining generic gap is composited last-live snapshots for glass, native hosted content/platform surfaces, and deferred overlays. Paint recording refuses those capabilities explicitly; it cannot freeze a historical backdrop, recover clipped pixels, or replay an AnyElement inertly. Families must retire unsupported content immediately rather than present shell-only continuity.

Glass optics use a shared height field, Snell refraction, spectral indices and Fresnel reflection in all three shader backends. glass-optics isolates these parameters over a ruled fixture, including fused panes. The implementation is single-interface screen-space optics, not full volume transport: no second interface, internal bounces, caustics, hidden-background recovery or real environment reflection is claimed. Scattering remains a uniform Gaussian; background-busyness probes and fused-outline shadows remain deferred. See docs/compatibility.md for the material contract and platform evidence boundary.

A component counts as covered only when it has all four of: a public builder or view, a scene in gpui_kit::scenes, behaviour tests driven through simulated input, and an entry in docs/components.md.

Covered

The isolated JS host does not grant external-file or pasted-image read access from ExternalPaths or paste events. These require an explicit refusal, not host paths or apparently readable references. A future host capability must define consent, mount/revision scope, read-only operations, expiry/revocation, size limits, path privacy, and negative tests. Internal DragItem events are data-only identity/label/kind/built-in-icon values, not file capabilities.

FamilyComponents
ActionButton, IconButton, ButtonGroup, SplitButton, Toggle, ToggleGroup, CopyButton
Text entryTextInput, PasswordInput, OneTimeCodeInput, TextArea (including bounded autosize), MentionInput, RichTextEditor, NumberInput, TagInput, InlineEdit, SearchField, FindReplace, UploadList
ChoiceSelect, MultiSelect, TransferList, Cascader, Combobox, Checkbox, Radio, Switch, Slider (horizontal and vertical), SegmentedControl, ColorPicker, ColorSwatch
FormFormField, SettingsRow, SettingsSection
NavigationTabs, Accordion, Collapsible, Breadcrumb, Sidebar, AnchorList, Pagination, Wizard, UndoHistory, Carousel
DataList (virtualized), Flow (virtualized), Table, DataGrid (virtualized), TreeGrid (virtualized), BulkBar, Tree, KanbanBoard, DiagnosticsList, ImageList, Masonry
Date and timeCalendar, DateInput, RangePicker, TimeInput
ContentMarkdown, AgentDocument, MessageList, ImageViewer, CodeView, TransportBar, BrowserPanel (shell only), LogStream, DiffView, ArtifactPreview, Terminal
DisplayIcon, Badge, Tag, Avatar, AvatarGroup, Card, ListRow, Divider, ProgressBar, EmptyState, FailurePanel, StatusDot, StatusLine, Callout, Banner, StaleMark, PulseLoader, Skeleton, Spinner, BarLoader, LoadMore, RefreshVeil, ProgressCircle, StageProgress, StateView, OutcomePanel, DescriptionList, Timeline, HighlightedText, AnimatedNumber, MetricCard, Sparkline, PerformanceHud, MicroMark, Rating, Bubble, Plot, CandlestickChart, SankeyChart, LineChart, BarChart, AreaChart, ScatterChart, PieChart, StackedBarChart, RadarChart, GaugeChart, ChartLegend, Heatmap
OverlayOverlay, Frost, Glass, Dialog, Drawer, Popover, Menu, ContextMenu, Menubar, CommandPalette, Tooltip, HoverCard, Toast, ToastLayer, NotificationCenter, Kbd
LayoutDesktopTitlebar, SplitPane, SplitTree, ScrollArea, ScrollFade, Toolbar, AspectRatio, Responsive, Grid, Container
ShellDock, StatusBar
KeymapKeybindingRecorder, KeymapEditor
InteractionDropzone
FilteringFilterBar
Agent run and personaAgentAvatar, AgentActivityLine, AgentCard, AgentGroup, AgentRunIssues, ToolCall, ThinkingBlock, NodeGraph, GraphNode, NodeGroup, CanvasToolbar, Minimap, TraceView, SpanTimeline, AgentRoster, SubagentTree, AgentRunCanvas, PersonaPortrait, VoiceReactive, PersonaDialogue, FeedbackRating, PromptBuilder
Permission and costApprovalPrompt, PermissionMatrix, CostMeter, ContextGauge
Game experiencePartyRoster, ObjectiveTracker, AbilityBar, RewardReveal
Visual effectsEffectParticles, CinematicEffect
Structured dataJsonView, Outline, SchemaForm
ConnectionsServerList, OfferingCatalog
MediaAudioPlayer, AudioWaveform, VideoPlayer, ModelViewer

Tooltipped is an extension trait rather than a component: it attaches a Tooltip to any element, and is covered wherever that Tooltip is.

PasswordInput and OneTimeCodeInput cover only product-neutral sensitive entry. The auth-sign-in and auth-verification scenes show password, one-time verification, passkey, organization sign-on, and recovery actions as composition from generic primitives. Account models, provider policy, credential storage, validation, networking, RPC, and authentication outcomes remain with the caller.

BrowserPanel is listed with a qualification because it is one. It is the chrome and the states around an embedded web view and it renders no web content: that needs an engine, and a component library that pulled one into every binary would be charging every host for a feature almost none of them use. The host supplies the engine and the surface. What the panel does own is the part hosts otherwise each get wrong — a build with no engine says so instead of drawing a blank page, and Loading, Empty, Unavailable, Error, and Ready remain five distinct answers.

NodeGraph renders caller-owned positions. The caller may compute those with the optional layered_layout / dimension-aware layered_layout_sized helpers or a different layout policy; accepting a layout result never transfers topology or position ownership to the component. A node may carry a caller-rendered thumbnail, whose pixels the graph neither fetches nor decodes. GraphInteraction::Inspect permits only pan, zoom, and selection proposals; Arrange additionally permits movement; Edit adds deletion, connection, and disconnection. The caller remains authoritative for the selection, topology, positions, and viewport shown on the next frame.

CinematicEffect is covered without making an animation runtime mandatory. The cinematic-effects scene stages a resolved deterministic sample, explicit runtime and invalid-archive fallbacks, and a reduced-motion poster. Component tests drive exact sampling, frame ownership, RTL mirroring, typed semantics, and diagnostic redaction. The optional pure-Rust adapter is separately tested against archive and animation limits under --all-features; a default build retains identical recipes and always-available particle fallback behavior.

Systems, which span more than one component

A system is not a component. It has no builder of its own to place on a screen; it is a contract several families implement, so it is covered when the contract is documented, staged in a scene, and driven through simulated input against every surface that implements it.

SystemContractImplemented by
Drag and drop (gpui_kit::interaction::dnd)docs/interaction.mdList, Tree, Tabs, Dropzone, DataGrid (column headers), Dock/DockTree (panel headers, groups, split edges)

Drag and drop is covered: the contract is written down, the scenes drag-list, drag-tree, and dropzone stage it, and crates/gpui-kit/tests/it/dnd.rs drives a simulated pointer through every surface above. DataGrid reorders its column headers through the same system, driven in crates/gpui-kit/tests/it/grid.rs, and Dock moves panels between regions through it, driven in crates/gpui-kit/tests/it/shell.rs; DockTree additionally reports centre merges into preserved empty stacks and four-way recursive split placement, driven in crates/gpui-kit/tests/it/dock_tree.rs.

One resize implementation

SplitPane is two panes and a divider; SplitTree is however many of those the caller nests; Dock builds a SplitLayout from the regions that hold panels and hands it to a SplitTree; DockTree projects the caller's recursive DockTopology to that same layout. So every dock divider is the same divider a plain split gives, with the same minimums and published travel range, and there is one place where dragging a divider is implemented. Every dock stack header is a Tabs strip for the same reason: dragging a panel is the drag system, not a second one.

The Longbridge GPUI Kit 0.6 dock was reviewed here rather than copied. Its PaneTree, normalization reducer, panel entities, registry, and reconciliation cache solve ownership inside a retained DockArea; importing them would create a second layout authority and make an action mutate state the Box contract says the caller owns. DockTree therefore keeps its controlled record-and-intent boundary. Its tests pin the corresponding invariants: load/dump/load reaches a fixpoint while empty stacks persist, every malformed record shape is refused, selection and collapse do not move unrelated ratios, a nested divider names only its own split, one completed drop emits one move intent, and ratios remain dimensionless when the container changes size. The host still applies and normalizes accepted structural changes.

Table or DataGrid

Both are covered and neither replaces the other. Table takes materialized rows and lays all of them out; DataGrid takes a render closure and lays out only the rows the viewport holds, which is what buys it column resizing and reordering, a pinned group, selection over an incompletely loaded set, opened rows, and cell editing. docs/components.md has the guidance on which to reach for. A wide DataGrid uses one horizontal viewport for its header, virtualized body, and summary, while a pinned leading group remains frozen at the reading edge. Its remaining fit-to-content limit is stated rather than faked: a double click on a column edge reports the request and lets the host answer.

TreeGrid reuses that virtualized machinery for a caller-flattened hierarchy. Its tree-grid scene and crates/gpui-kit/tests/it/tree_grid.rs cover bounded materialization, TreeGrid/Row/GridCell semantics, row hierarchy metadata, and logical disclosure/parent keyboard intents. It deliberately adds no second flattening or cross-tree layout system; horizontal scrolling and a frozen hierarchy column are inherited from DataGrid rather than reimplemented.

Helpers, which the four-part rule does not reach

field_shell and FieldState draw the one border, background, and focus treatment every editable control wears. FocusTrap keeps the keyboard inside an open overlay. Neither renders on its own, so neither has a scene of its own; both are exercised through every control and overlay that uses them.

Out of scope, and why

  • The calendar itself. Calendar, DateInput, RangePicker, and TimeInput are covered; the calendar system, the time-zone database, the locale, and the notion of today underneath them are not, and never will be. Correctness there is calendar, time-zone and locale work, not UI work, and a library that shipped a half-correct calendar would be worse than one that shipped none. So the components own no date arithmetic at all and read every fact from a host-implemented DateAdapter; docs/datetime.md is the contract. The reference calendar the scenes and tests run on is behind the fixtures feature, off by default, so it cannot be mistaken for a default.
  • Time formatting. Timeline displays times and day headings, and formats neither: it takes strings the host has already put into words. The same reasoning that keeps the calendar out of this crate keeps “two minutes ago” out of it, and the seam is the adapter above the component.
  • Judging or applying a keybinding. KeybindingRecorder captures a keystroke and KeymapEditor coordinates it with caller-owned command and binding identities. Whether it clashes with something, what provenance it has, and whether add, remove, or reset should be accepted needs the keymap, which the host owns. Both components render the caller's answer rather than inventing or persisting one.
  • Persisting a layout. SplitLayout converts to and from plain records so a host can write it out, and this crate takes no serialization dependency to do it for them.
  • Language intelligence and document policy. Editor fixes the shared TextArea buffer and geometry to a no-wrap source projection, then accepts revision-tagged caller highlights and one synchronous caller-owned indentation replacement. RichTextEditor projects the same editing invariants through styled blocks, alignment, lists, diagnostics, semantics, and a formatting toolbar. Grammar/LSP facts, folding and multi-caret policy, persistence, collaboration, URL policy, and conversion to a product document format remain host work. Syntax colouring still stops at four built-in classes on the eight languages content::highlight knows. A host that has a grammar installs its own spans, and those facts win.
  • Doing what a document says. Markdown draws HTML as the characters somebody wrote, reports a link rather than opening it, and names an image rather than fetching it. There is no HTML renderer here, no URL policy, and no network; docs/content.md states why each of those is a refusal rather than a gap, and what a host has to supply instead.
  • Delivering a message. MessageList renders five delivery states and reports a retry. Sending anything, deciding what a resend means, and knowing whether a message was really read are the transport's, and this crate has no transport.
  • Capturing a voice or advancing dialogue. VoiceReactive maps a finite normalized host sample to a complete meter and PersonaDialogue owns the portrait, safe Markdown, streaming, and choice composition. Microphone access, recognition, synthesis, playback, expression inference, and choosing or applying the next turn remain host facts and capabilities.
  • Running a game or deciding an outcome. PartyRoster, ObjectiveTracker, AbilityBar, and RewardReveal own reusable character/game presentation, malformed-topology refusal, typed intents, RTL, reduced motion, semantics, and policy-resolved effects. Combat formulas, cooldown clocks, input maps, objective progression, reward eligibility, inventory mutation, persistence, networking, and asset fetching remain authoritative host systems. The UI never converts a click into a successful action or an item into an owned one.
  • Fetching or decoding an image. ImageViewer frames, zooms and pans an element the host hands it, and names the source when the host hands it nothing. There is no network here and no decoder, so the pixel size of a source is a caller input like every other fact this library cannot hold; a viewer given none says the size is unknown rather than reporting the box it drew.
  • Media policy. TransportBar reports play, pause, seek, volume, mute, speed and a track step, and applies none of them. AudioPlayer and VideoPlayer ask a MediaTransport; PlatformMediaTransport currently implements it with AVFoundation on macOS and Media Foundation on Windows. Linux GStreamer and Web HTML media adapters are planned work in docs/foundation-roadmap.md, not permanent no-backend policy. Playlist and queue ownership, URL/auth policy, DRM, subtitle/track policy, output-device policy, custom cache/retry and capture remain host responsibilities.
  • Reading a 3D model that is not glTF. ModelViewer reads the subset of glTF 2.0 stated in docs/components.md and refuses everything else, without a scene-graph dependency, a material system, or a texture pipeline. Other formats, materials, animation and skinning are not gaps to be filled here: a document that needs them is one an application converts before it arrives.
  • Owning application data or interpreting its business meaning. Charts read caller-owned observations and report caller-owned actions. Reusable scales, ticks, stacking, statistical layouts, reference marks and controlled range interactions are in scope; they do not require queries or persistence. Hosts still choose the data, domain policy, units, locale/calendar formatting and business transformations. Existing normalized charts retain their caller-prepared coordinates and exact text. Their keyed geometry, crosshair navigation and stale-data retention do not imply a complete raw-data engine. The visualization delivery plan records that expansion separately from delivered coverage. Query execution, stored reports, trading logic and data-source orchestration remain application work.
  • Owning a platform picker. Colour, file, and print dialogs that replace the operating system stay out. In-window colour wells, dropzones, and print-preview chrome that report a choice are in scope; they do not become the system dialog.
  • Menu bar and window chrome. Owned by the platform window, not by a component tree.
  • Product-specific marketing policy. Carousel, Rating, and Bubble are now neutral, caller-owned primitives. Product-specific autoplay, recommendation, delivery, and conversation policy remains outside the Kit.

Framework and platform limitations

The component coverage review above has no remaining MUI or shadcn component gap. The limitations below are intentionally kept separate: they are framework, renderer, platform, or host-policy boundaries, not missing public components. Out of scope above means "will not be built, and here is why"; these entries record capabilities that need a framework or platform owner without pretending that a component-local workaround is complete support.

Backdrop work regions preserve the established pixels, 2026-09-04

The WGPU frost and glass scenes were captured with Linux llvmpipe immediately before and after bounding their snapshot, blur, and composite passes. All four PNGs are byte-identical (AE = 0):

ImageSHA-256 before and after
frost-studio-dark.pngfd936b094521bc9fe91149f7fc6a0365fddd63554ebf13fe328c07d295ab1ec9
frost-studio-light.png11dd9814eddb4097c675f2c793b530017eeb59b8dc08620a52587b84aa743418
glass-studio-dark.png37387fe70f5a9a5f0e86908bf968867cea9d0513c18966df36d6cae02d686ad8
glass-studio-light.png604cdf8137acedb1fbb2dece495189f5310e7025273e37fa95a183a344a23ff1

The shared region calculation retains every Gaussian dependency, the maximum refracted and dispersed reach, and requested probe samples while leaving texture coordinates and material math unchanged. Direct3D's all-feature crate also cross-compiles for x86_64-pc-windows-gnu. Metal cannot run in the Linux orb; its check is the dispatched Platforms workflow. This is a pixel-equivalent renderer optimization, so no macOS or Windows baseline is recaptured or changed; their existing headless checks remain the acceptance authority.

Regular glass text contrast

Kit constrains the Regular material's shader wash to maintain the WCAG 4.5:1 normal-text ratio against the opposing neutral backdrop, including transmission gain and optical lift. This applies before any probe response as well: a mean luminance cannot guarantee contrast at every text position. It does not add an opaque or source-over face, change large-panel appearance, or affect Clear. Caller-supplied text colours and optical overrides still require caller review. Glass::protect_text_contrast(false) and the matching group option explicitly retain the material's wash instead; the caller then owns foreground legibility. This is used for native material calibration, not enabled silently on existing reading surfaces. Explicit tints compose into the RGB material wash (including fused bridges) before its rim light, rather than disappearing behind a zero-alpha fill or being converted to an achromatic pole by framework sanitization.

tools/liquid-glass-reference records native SwiftUI references and validates GPUI candidates with independent colour, rim, bridge and held-out-size scores. The persistent menu producer now resizes its real layout at simulated times, records measured geometry, and rejects unchanged menu pixels claiming a resize. This does not reproduce SwiftUI content crossfades or prove native dynamics. Foreground press scaling uses paired framework transforms without reflow; the material outline stays fixed, and reduced motion suppresses the scale. The 1.014 default is Kit policy informed by one native label specimen, not a universal native constant. Rounded foreground clipping and explicit window-overlay mask escape are implemented. Calibration trials are not accepted catalog baselines or proof of Apple's private implementation.

Application-wide Reduce transparency belongs in ThemeRegistry, alongside density: call set_reduce_transparency(reduce, cx) to update it and repaint all windows. ThemeRegistry::set_reduce_transparency and reduce_transparency provide the direct setter/getter. Activation, density changes, registration and counterpart resolution preserve this preference; an unchanged value does not rebuild the resolved theme. Scoped Theme::with_reduce_transparency remains available for individual exhibits; no root ThemeOverlay workaround is needed.

Rounded glass profile reach, 2026-09-08

The menu's 12 px corner and requested 36 px bevel exposed a second optical singularity, distinct from finite-difference medial-axis normals: the analytic arc normal still had nonzero dome slope when its parallel curve collapsed at the arc centre. Both Metal and Linux WGPU showed the resulting pointed highlight. BackdropGlass::optical_bevel now bounds the complete optical profile by positive corner radii and lobe half-extents before all three renderers pack it. The same depth across a union prevents seams; specular strength, hairline, wash and blur are unchanged. Square corners retain their intentional incident-face crease. CPU and native pixel regressions include the menu radius and sample both before and after the arc centre, while requiring a real rim highlight to remain.

Clear media reading

Clear is an appearance-independent on-media material: its subtree inherits color.onMediaForeground and color.onMediaHairline, never an adaptive flip. Reduced transparency keeps that light content on dark Frosted color.onMediaBackground, including in light themes. dimmed(true) applies the effect.glassDimming 35% backing only to Clear, and retains it when reduced; all other presets ignore dimming. This policy is inside Glass/GlassGroup, not a caller-installed theme override. Deliberately coloured child elements still own their explicit colours, as on other surfaces.

The current glass integration has Linux offscreen and native Metal evidence. Windows/WARP is unverified for this revision: no Windows runner was available; the existing Windows baseline is not claimed as acceptance of these changes.

Downsampled backdrop blur deliberately deferred, 2026-09-04

Metal, Direct3D, and WGPU keep full-resolution backdrop textures and preserve the established Gaussian and refraction samples while clipping work to the surface's conservative sampling region. Rendering the blur through a smaller intermediate texture could reduce bandwidth further, but resampling changes the material's pixels and may change the apparent radius, rim detail, and paint-order boundaries. It is therefore not hidden behind a renderer-local shortcut. A future downsampled path needs one shared quality contract, focused renderer tests, and reviewed replacement captures from both active baseline renderers (Metal on macOS and WARP on Windows).

Metal and WARP baselines outstanding for this wave, 2026-09-03

Three light-appearance fixes landed from a Linux orb, which can run neither active renderer. Twenty-two images across eleven scenes are therefore stale on macOS and Windows until someone captures them there:

ChangeScenes, in both Studio themes
Sprite corner coveragevisual-effects, cinematic-effects, game-ui
Washed panel surfacefailure-panel, outcome-panel, state-ladder, kanban, heatmap, audio-waveform, node-graph
Scrolling menu edge fadeform

Every claim recorded for them is a Linux llvmpipe claim and says so. The renderers were compared with a full catalog capture taken immediately before each change and re-checked after it, so the counts above are what those changes moved and not accumulated drift; snapshots/headless/linux itself stays as it was, retired and stale, rather than being refreshed for three scenes and left alone for the rest.

Nothing here asserts what Metal or WARP drew before the change or draws after it. Whoever captures on those platforms reviews the images against the entries below rather than accepting them unseen.

Sprite corner coverage reviewed on a retired renderer, 2026-09-03

The polychrome sprite corner mask's dependence on fwidth(distance) was found downstream in a light appearance and reproduced here in direct offscreen readback under Linux llvmpipe — no compositor took part, so it is a renderer defect rather than a capture artifact. visual-effects and cinematic-effects carried the dashed seam through their own sprite batches and particle recipes, and game-ui through its item art; the three scenes are the whole of the catalog's polychrome sprite coverage, and all six of their images move with the correction.

Whether Metal and WARP ever showed the same seam is not recorded, because neither renderer can be run here. Both carried the identical fwidth-sized mask, so both were exposed to the same class of failure; the correction removes the derivative rather than compensating for one backend, and reproduces the width a conforming fwidth reports. Whoever next captures on macOS and Windows should review those six images against this entry rather than accepting them unseen.

An overflowing menu with no active baseline, 2026-09-03

No catalog scene fills a menu past its own height. command-palette, choice, multi-select, cascader, and mention-input each stage a list that fits, which is why the boundary treatment every one of those menus lacked — a row sliced through its glyphs at the viewport edge, with the card's rounded corner cutting across the same row — was never in a reviewed picture.

A temporary llvmpipe exhibit rendered a CommandPalette holding fifteen commands in three sections at the same 480-pixel width and menuMaxHeight the scene uses, in both Studio themes, before and after the fade. Before, the eighth row was cut horizontally through its cap height with its shortcut chips halved; after, that row fades out over the band and the boundary reads as "there is more below". The exhibit was then removed, because Linux's baseline set is retired and cannot author the active Metal and WARP images.

Behavior coverage keeps the permanent invariant: overlay::popover's edge decision fades an end only while it hides a row, fades neither end for a list that fits, and does not mistake a sub-pixel resting offset for travel.

Two gaps stay open for whoever next captures on macOS and Windows. The first is the exhibit: a palette long enough to overflow belongs in the command-palette family so the boundary has a picture. The second is that CommandPalette still does not scroll its keyboard highlight into view the way Select and Combobox do, so arrowing past the fold moves a highlight nobody can see; the fade now says the rows are there, and the reveal is the separate change that lets the keyboard reach them.

Edge-faded crossing surfaces with no active baseline, 2026-09-03

A tall SettingsSection whose card crossed the bottom of a ScrollArea was rendered before and after the primitive-aware edge-fade correction in a temporary scroll-fade exhibit. Both Studio themes were inspected with Linux llvmpipe: before the correction the card shadow was absent while its visible rows remained; afterwards the shadow retained the card's visible grouping and the fill continued to fade over the band. The temporary exhibit was then removed because Linux's baseline set is retired and cannot author the active Metal and WARP images.

Framework behavior coverage keeps the permanent invariant: a shadow and a filled path taller than the band remain non-transparent when they cross the region edge, the path carries a transparent-to-opaque shader gradient, and a primitive smaller than the band retains nearest-edge alpha. Whoever next captures on macOS and Windows should add the crossing SettingsSection state to the scroll-fade family and review both active renderer images rather than accepting either unseen.

Three opt-in states with no active baseline, 2026-09-03

An InlineEdit reading a long multi-line value, a NodeGraph that keeps its dot grid but refuses the origin axes, and neutral controls declaring an overlay ground now have complete layout or paint behavior but no permanent catalog state. Existing scenes intentionally keep their defaults, so their Metal and WARP pixels do not move; Linux's llvmpipe baseline set is retired and cannot author either active renderer's new images.

The reading layout is covered by measured bounds, including the pen remaining inside a bounded single-line row. The graph's default-on axes and independent grid/ground settings are covered by behavior. Overlay-grounded Secondary, Default, refused, icon, and toggle paths share one resolver whose composited resting fill clears the 2 L* surface floor in both bundled themes, while the implicit panel ground resolves to the historical raised/active paints. A temporary llvmpipe exhibit put a Secondary button, an unpressed Toggle, and a Secondary IconButton on OverlaySurface::FLOATING; resting and hovered captures in both Studio themes were reviewed before the exhibit was removed.

Whoever next captures on macOS and Windows should add a long multi-line reading value to inline-edit, an axes-free state to node-graph, and the three grounded controls to the overlay or button family, then review both active renderer images rather than accepting them unseen.

Two colour settings with no scene of their own, 2026-09-02

TabItem::tint and NodeGraph::ground_light change what a strip and a canvas look like, and neither is drawn by any scene in the catalog. That is a deliberate deferral, not an oversight, and it is a renderer boundary rather than a component one: an active visual baseline can only be captured on a machine with that renderer, and the two active sets are Metal on macOS and WARP on Windows. A scene added without both is a headless check that fails for missing baselines on the two platforms that gate a release, so the picture would be added by breaking the thing that reviews pictures.

Both are covered by behaviour: a tinted strip publishes the same tree as an untinted one down to the measured bounds, a refused ground paints no gradient at all, and a canvas that has not asked is still lit. What is not covered is what they look like. Whoever next captures baselines on macOS and Windows should add a tinted strip beside the untinted one in the navigation family and a flat ground to the canvas family, and review the images rather than accept them.

Fit clearance with no scene of its own, 2026-09-02

NodeGraph::fit_clearance reserves caller-owned canvas edge bands when the graph frames its measured world. No gallery state was added from Linux: the active visual baselines are Metal on macOS and WARP on Windows, and adding a state without captures from both would leave the release gate incomplete.

The framing arithmetic instead covers zero, asymmetric, composed and oversized clearances, and a mounted graph with clearance publishes the same semantic tree as one without it. Whoever next captures baselines on macOS and Windows should add a fit-clearance state to the canvas family and review both active images.

Shared presentation tiers and semantic token authority, 2026-08-27

Theme::variant_colors resolves the seven shared tiers (Filled, Light, Outline, Subtle, Default, Transparent, White) against a palette group, a semantic role, or an explicit paint, and Button, IconButton, Badge, and Tag accept .variant(..) / .color(..) on top of their existing vocabularies without moving a pixel of the defaults. Other coloured surfaces — Callout, Banner, StatusDot, StatusLine, ProgressBar, and ProgressCircle — use the same semantic theme roles through their own state-oriented APIs. They intentionally do not expose arbitrary per-instance variant colours: tone is a fact, while variant tiers are a reusable choice for surfaces such as buttons, badges, and tags. This keeps all actual paint values in the theme token authority without creating a second override vocabulary.

A product backdrop that the bundled ramp still cannot 1:1, 2026-08-18

Surface::Backdrop and multi-layer elevation close the two kit-side holes a downstream prototype hit: there is now a plane darker than canvas for a card to sit on, and an elevation step can carry both a contact shadow and a wide one. The bundled Studio themes take the new roles without retuning the five existing surfaces, so the gallery does not move.

What is still not 1:1 is the light appearance. The prototype paints the page #c9c9d1, the well #dcdce2, and the card #ffffff. A white panel leaves raised nowhere to go — it would need L* ≥ 103, which is outside the colour space — and dropping the page to #c9c9d1 forces sunken darker still, at which point the light text and semantic rungs (tuned for a shallow well) fail the contrast floor in eleven pairs. Retuning that light foreground and semantic scale is a separate change; this one does not do it. The library still groups with colour rather than with a line, and a card still does not draw a border: lines stay reserved for focus, invalid, and drop.

Typography and visual polish audit, 2026-08-11

Before changing typography, eight independent reviewers inspected all 98 Linux headless scenes in both Studio themes: 196 images, each 1840×2000. Every image was checked for (1) type hierarchy and accidental GPUI-default text, (2) line height and icon/chip baselines, (3) token spacing rhythm, (4) dark-theme contrast, (5) visibly distinct Loading, Empty, Unavailable, Error, and Ready states, and (6) clipping, overflow, and unconstrained full-width content. This is the named pre-change inventory; clean means no defect was visible under that rubric, not that every possible interaction was exercised.

ScenesPre-change finding
accordionMedium: disabled title and detail are too faint in both themes.
actionsHigh: dark toolbar icons, shortcuts, menu outline, and separators nearly disappear.
agent-rosterClean.
agent-run-canvasClean.
anchor-listMedium: dark explanatory copy is nearly invisible.
animated-number, aspect-ratioClean.
approvalMedium: dark secondary action is weak; the focus ring and labelled divider need clearance.
audio-playerHigh: media-control baselines diverge and inactive rails/separators disappear, especially in dark.
auth-sign-in, auth-verificationMedium: dark banner, placeholder, link, and verification-cell boundaries are too weak.
badgeMedium: several dark semantic foreground/background pairs are hard to distinguish.
breadcrumbLow: the light-theme slash sits above the text baseline.
browser-panelMedium: the five states are distinct, but long Ready content is hard-clipped.
buttonMedium: control sizes rely on boxes more than a coherent type ladder; dark disabled labels are weak.
calendarMedium: dark adjacent/disabled dates and navigation are too faint; fixture explanation uses default-sized text.
cardMedium: a two-line card stretches across nearly the full canvas.
cascaderHigh: an unconstrained trigger and narrow popup disagree; dark disabled rows disappear.
choiceHigh: dark unchecked and disabled controls are almost invisible.
code-viewMedium: an intentionally long line clips without an equally visible horizontal-scroll affordance in both themes.
collapsibleHigh: the dark managed section is nearly invisible.
command-paletteMedium: dark section labels, secondary commands, and keycaps are too faint.
contentHigh: the states are truthful, but dark pinned/filter/empty affordances and progress rails disappear.
context-menuMedium: fixture copy falls back to large default text; dark metadata is weak.
conversationHigh: duplicated long message groups and an unconstrained reading column obscure the intended scroll states.
copy-buttonLow: success is repeated as adjacent Copied labels.
cost-meterHigh: dark labels, refusal reasons, and unknown values are nearly invisible.
data-gridMedium: chip baselines and compact type are inconsistent. Dark row hover/selected are now separate washes; managed rows no longer fade the whole row.
data-grid-editingMedium: editor and cell baselines differ and the focus edge collides with a grid line.
date-rangeMedium: disabled/range dates lack contrast and adjacent-month dates lack hierarchy.
date-timeMedium: light separators sit low and error copy touches the field.
detailHigh: labels, unknown/not-applicable values, and dark metadata are too faint.
diagnostics-listHigh: Hint and explanatory text nearly disappear; chip icon baselines diverge.
dialogLow: light is clean; only masked background copy is extremely faint in dark.
diff-viewLow: top clearance and the dark split divider are weak.
document-tabsMedium: light helper copy and dark inactive close controls are too faint.
drag-listClean.
drag-treeMedium: light is clean; dark status copy nearly disappears and preview content sits high.
drawerMedium: light is clean; the dark unchecked box is difficult to find.
dropzoneMedium: helper copy is too faint and states move vertically when a detail line is absent.
failure-panelMedium: top copy clips in light; secondary failure detail is weak and icon/text centring differs.
filter-barLow: light Add-filter/chip baselines differ; dark is clean.
find-replaceHigh: dark explanatory copy disappears, controls are over-wide, and count/arrows are crowded.
formMedium: the Region popup covers field help; dark unselected text is still faint. Errors now clear the field.
frostHigh: dark frost lacks enough edge/surface evidence; light needs bottom clearance and consistent stripes. The edge evidence a frosted surface is missing is what glass carries; Frost stays frosted deliberately, being the material every renderer can produce.
glassRegular Liquid separates reading surfaces with material-owned blur, saturation, achromatic wash, lensing and hairline. Rim refraction samples the scattered source; Clear stays sharp and media-only, with light content and optional 35% dimming. Reduced transparency resolves presets and overlays to Frosted. Small non-Clear surfaces may flip appearance through hysteresis; large and unmeasured surfaces keep the window appearance. Ring shadows use the five optical-source samples' mean and variance, retaining the last completed statistics; this is a sampled separation cue, not exhaustive backdrop analysis. Foreground press scale uses paired framework transforms without reflow and is suppressed under reduced motion. Caller content is rounded-clipped; window overlays explicitly escape ancestor masks. Fused-outline shadows still need a generic SDF shadow primitive. Over-budget surfaces/groups use opaque fallbacks. The exhibit covers text/media backdrops, compact controls, non-flipping surfaces and admission/lobe budgets. scroll-edge-effect owns the transcript-ramp exhibit. Native renderer evidence is recorded in compatibility.toml; no Apple optical/dynamic equivalence is claimed.
hover-cardMedium: helper and body copy are too faint, especially in dark.
ide-shellHigh: dark shell height leaves a large void and Empty/Unavailable claims conflict.
image-viewerHigh: metadata and disabled controls are too faint; the third viewer breaks the first two viewers' grid/container rhythm.
inline-editHigh: dark helper text disappears, errors touch fields, and the short value gets an over-wide editor.
inputHigh: read-only/disabled values are made unreadable rather than distinctly unavailable.
json-viewLow: comment columns and selected-row extent are inconsistent; dark null/comment values are weak.
kbdClean.
keybindingMedium: unbound/help/error text is faint and keycap/icon/row baselines vary.
keymap-editorHigh: Defaults and host-managed copy use GPUI's large default; chips, conflict/source text, and actions do not share a row grid.
listLight is clean; low in dark for title/list clearance and missing continuation affordance.
loadingClean. Every indicator paints from the neutral color.loader.* roles, the placeholder is held inside a loudness band by the token gate, and each of the six is a different picture rather than a different arrangement of the same one.
log-streamMedium: level/inline chips sit off baseline and small status copy is faint.
markdownMedium: the dark unfetched state disappears; truncated repetition and embedded-block widths are unclear.
menuLow: shortcut gaps/insets vary and the dark section label is weak.
menubarMedium: shortcuts, anchors, item gaps, and dividers do not share one baseline/rhythm.
model-viewerHigh: a dark labelled divider crosses its label; metadata and disabled controls are too faint.
motion-flipLow: light caption contrast/button padding and dark badge centring need correction.
motion-stateMedium: progress tracks disappear and the dark segmented strip stretches beyond its content.
node-graphHigh: edges cross labels, loose labels lack anchors, and dark node metadata is too faint.
notification-centerHigh: unread markers move between action/close slots and rows without detail collapse.
offering-catalogMedium: type badges and source columns are inconsistent; source/banner copy is faint.
overlayLow: dark secondary-button separation is weak.
paginationHigh: light previous/first controls look disabled away from the first page.
permission-matrixLow: wrapped policy detail breaks row rhythm and dark secondary copy is weak.
popoverLow: light unchecked-box contrast and optical centring are weak.
progress-circle, reading-directionClean.
schema-formMedium: dark fields and placeholders disappear; required markers drift off baseline.
scroll-areaMedium: the final line is cut in half and the light thumb is faint.
scroll-fadeClean.
scroll-shadowMedium: the first line is cut in half and the dark bottom shadow is weak.
search-fieldLow: light hit-chip outlines are too faint.
server-listLow: expanded error spacing is inconsistent and light helper copy is weak.
settingsMedium: unavailable explanation is made unreadable, especially in dark.
sidebarMedium: dark managed and workspace metadata nearly disappear.
sparklineMedium: dark minimum/maximum values are too faint.
split-pane, split-treeHigh: dark dividers and drag handles nearly disappear.
statusMedium: two short status messages stretch nearly the full canvas.
tableLow: the scene now shows ready, a stale refresh, and empty. Managed rows keep their badges; hover and selected are separate washes.
tabsMedium: scene body falls back to large default text and dark disabled tab is too faint.
textareaHigh: dark placeholders, disabled text, and field surfaces disappear.
thinkingMedium: dark secondary/empty copy is weak and short rows stretch full-width.
toastLow: bottom clearance and dark close-control contrast are marginal.
toggleHigh: dark disabled and unselected states nearly disappear.
tool-callClean.
toolbarMedium: scene body falls back to default text; dark controls/boundaries and group spacing are weak.
tooltipMedium: trigger and tooltip have nearly the same visual role and no strong pointing relationship.
transportHigh: dark rails, speeds, status copy, and unknown duration disappear; control baselines and mute forms diverge.
treeMedium: dark ignored state is too faint and selected-row extent is ambiguous.
tree-gridMedium: dark structure disappears and disclosure/icon columns do not reserve consistent slots.
upload-listHigh: dark queued/refusal explanation disappears; progress/action columns and icon sizes vary.
video-playerMedium: volume controls do not share a baseline; dark rails/help disappear and the player lacks a balanced width strategy.
wizardHigh: dark step details and Back boundary disappear; horizontal steps lack a shared title baseline.

This pass does not accept a root-level inherited font as a fix. A component can be embedded beneath any host text style, so each element that emits text must choose its own complete TypeScale step. The typography gate records that contract. It also does not turn disabled content invisible, treat a refusal as empty data, invent a decoder/browser/graph layout, or add application policy to make a fixture look fuller.

What closed the table

The table above stays as the pre-change inventory. What closed most of it was not per-scene patching but two machine-checked rules in the token layer, each of which had been failing silently in every theme:

  • Surface separation. Every nesting step — canvas to panel, panel to card, card to raised, and the overlay against what it covers — now has to differ by at least three CIE L* points, in the right direction, or TokenDocument::validate refuses the theme. The dark neutral ramp was respaced and the light one retuned to pass it. That is what made the card, table, dock and popup boundaries in dozens of the High rows visible; contrast.rs and docs/token-model.md carry the contract.
  • Tone distinction. muted, faint, placeholder and disabled were three different facts wearing one grey in dark, and an inverted ladder in light, while every foreground/background contrast pair passed. Each rung now has to differ by three L* measured as distance from the canvas, so one rule holds in both appearances. That is what made the disabled control, the placeholder, the divider and the explanatory line in the remaining High and Medium rows tell themselves apart.

Card, CardHeader and StyledExt::card_surface replaced three incompatible hand-rolled card shells, which is what closed the surface-rhythm findings in the agent, game and notification rows.

After recapturing all 216 macOS baselines against the retuned themes, the rows that still showed a defect were fixed individually:

SceneWhat was left, and what was done
keymap-editorDefaults was a step larger than its peer Current bindings; both are now Label.
ide-shellThe Problems panel carried a badge counting three problems and one string that claimed Unavailable and Empty at once. It now makes one claim and counts nothing it cannot list. The shell also fills the scene rather than leaving the lower third empty.
notification-centerThe unread marker sat in the trailing flow, so it landed beside the action on one row and beside the close control on the next. It is now a fixed slot beside the title.
node-graphAn edge label was drawn six pixels from the midpoint that also carries the disconnect chip, so the two overlapped. The label now clears the chip's radius.
cascaderThe trigger stretched to the scene while its popup kept its own width. The scene now gives the trigger the width of the surface it opens.

This re-review was one reviewer over the 216 recaptured macOS images, not a repeat of the eight-reviewer pass, and it read the rows the table had flagged most closely. Three findings were looked at and deliberately left: a conversation and a diff still fill the width they are given, because the reading measure is the caller's; the transport's mute control keeps a different form when muted, because that difference is the state; and a scrolled popup still shows a partial row at its clip, because that is what being scrolled looks like.

Motion framework boundaries

The primitives in docs/motion.md cover a value moving from one state to another, motion that is interrupted, composed or driven by a gesture, and springs described as a duration and a bounce. The remaining entries are renderer/framework boundaries, not missing catalog components.

The spring itself is no longer one of those boundaries: GPUI owns analytic fixed- and moving-target evolution, velocity-preserving element retargeting, playback state, projected targets, and finite overshoot. Kit owns the theme and component policy above it. This is an invisible framework primitive and does not add a scene merely to increase catalog coverage.

Touch recognition is no longer a local component approximation either. When a platform supplies raw TouchEvents, GPUI resolves tap/multi-tap, phased axis-locked pan and frame-rate-independent momentum, prediction correction, touch-drag, long press, multi-contact pinch, exclusive ID-based pan/pinch ownership, cancellation and fling interruption through window dispatch. Tests cover contact reordering/degeneracy, either pinch contact cancelling, captured ownership across redraw and a centroid crossing another view, and scroll consumption plus residual handoff/reversal without double movement. Platform inactivity cancels pending recognition and post-release momentum. These tests do not execute native devices, native keyboards or system gestures. Rotation and arbitrary simultaneous recognizer graphs are not provided; captured owners must cancel before unregistering, and manipulation reversal does not transfer ownership back to scrolling. Native geometry forwarding returns unavailable for unsupported queries; its shared tests prove transport and refusal, not platform text geometry. Platform-generated trackpad pinch remains a separate input path.

Framework boundaryWhy it matters
Shape in flipFlip::shape interpolates radius, border width, border colour and background over the same spring that carries position and size, and Shaping::shaped applies the result, so a row becoming a card travels between the two forms. The caller states both forms and applies what comes back, because Flipped wraps an element it did not build and cannot reach the style inside it. What is still not interpolated is anything with no numeric path between the two forms — a shadow set, a gradient, or a change of element kind.
Overscrollmotion::rubber_band damps a pull past a boundary, but nothing in the library overscrolls: a ScrollArea stops dead at its end, so the band is available to a caller and used by no component here.

Media capabilities beyond native playback

PlatformMediaTransport closes the ordinary macOS/Windows decoder, output, clock and native-view gap. MediaCapabilities now makes audio/video, seek, volume, rates, native-track and output-selection support explicit at runtime; unsupported controls are absent or inert. MediaErrorKind preserves no-backend, invalid-source, open, playback and refusal categories through the Kit seam, so downstream code never parses a platform diagnostic. The remaining media work is additive capability, not basic playback: playlist/queue ownership; audio, subtitle and accessibility description track selection; audio output-device switching; DRM; application cache/retry and authentication policy; picture-in-picture and fullscreen; recording/capture; frame-accurate extraction plus waveform/thumbnail generation; and Linux/Web backends. A platform codec refusal remains no-backend, while an unreadable source remains failed; neither is converted to empty media.

Components

The internal inventory is not enough to discover a family that does not exist, so the component catalog is also compared against a mature external baseline. The 2026-08-27 primary baseline is the official Material UI component overview. That page lists 59 entries: Inputs (13), Data display (10), Feedback (6), Surface (4), Navigation (9), Layout (5), Lab (2), and Utils (10). The current MUI component index also exposes newer entries such as Number Field and InitColorSchemeScript; because the two official indexes are not yet identical, this document records the dated overview rather than pretending that a component count is a stable quality score.

MUI X is tracked separately: its current stable advanced families are Data Grid, Date and Time Pickers, Charts, and Tree View. Scheduler is listed as Preview, and advanced Pro/Premium features are not silently counted as free Core coverage. The mapping compares product-neutral behavior, not React, DOM, CSS, or API spelling:

The second coverage baseline is shadcn/ui, whose official description is a set of accessible components and a code distribution platform. Its current component catalog lists 64 first-party entries. That is a valid open-source component coverage baseline alongside MUI: the source-distribution model changes ownership and customisation, not whether an input, overlay, data, or layout capability needs to exist. Community registries remain outside this dated first-party count. GPUI Box already follows the important part of that model — callers own data and actions, while the Kit owns reusable visual and interaction contracts — but those contracts must still be complete and machine-tested like a maintained library. The count is not added to MUI's count because names and granularity differ; every entry is instead mapped by behavior below.

shadcn familyEntriesStrong equivalentPartial / foundationUnimplemented
Interaction and forms20Button, Button Group, Checkbox, Combobox, Calendar, Date Picker, Field, Input, Input OTP, Radio Group, Select, Switch, Textarea, Toggle, Toggle Group, Label, SliderInput Group, Native Select, Questionnaire—
Feedback and surfaces20Accordion, Alert, Aspect Ratio, Avatar, Badge, Bubble, Card, Carousel, Collapsible, Dialog, Empty, Hover Card, Message Scroller, Progress, Sheet, Skeleton, ToastAlert Dialog, Attachment, Message—
Navigation and overlays13Breadcrumb, Command, Context Menu, Drawer, Dropdown Menu, Menubar, Pagination, Popover, Sidebar, Tabs, Tooltip, KbdNavigation Menu—
Data, layout, and foundation11Chart, Data Table, Direction, Item, Separator, Table, Typography, Resizable, Scroll Area, SpinnerMarker—
Total64568—

This normalized shadcn mapping is intentionally stricter than a name search. For example, Message Scroller maps to the virtualized MessageList, Resizable maps to the shared SplitPane divider, and Direction maps to the layout-direction foundation. Native Select is only partial because GPUI's Select is a themed popup control. Input Group and Questionnaire remain composition patterns whose product-specific policy is intentionally caller-owned. Slider and multiple selection are full behavior matches through the orientation-aware Slider and controlled MultiSelect contracts. MultiSelect is an intentional GPUI extension for the multiple-selection use case; it is listed in the library coverage above and in the completed capabilities below, but it is not counted as an additional shadcn entry.

MUI familyEntriesGPUI Box mappingVerdict
Inputs13Autocomplete → Combobox (single-answer); Button/Button Group → Button/ButtonGroup; Checkbox → Checkbox; Radio Group → caller-composed Radio; Select → Select (single-answer); multiple selection → MultiSelect; Slider → orientation-aware Slider; Switch → Switch; Text Field → TextInput/NumberInput/TextArea; Toggle Button → Toggle/ToggleGroupBroad coverage; Floating Action Button remains a deliberate desktop composition.
Data display10Avatar, Badge, Tag, Divider, Icon, List, Table, ImageList, Tooltip, foundation text/type scaleStrong coverage; typography and icon families are correctly foundation/catalog concerns rather than duplicate leaf components.
Feedback6Callout/Banner/StateView, Overlay, Dialog, ProgressBar/ProgressCircle, Rating, Skeleton, Toast/ToastLayerStrong product-neutral coverage, with feedback states retained as caller-owned facts.
Surface4Accordion, DesktopTitlebar/Toolbar, Card, theme surface recipes plus Frost/GlassStrong coverage by composition and complete surface recipes; no need for a second Paper shell.
Navigation9Tabs, Breadcrumb, Drawer, Menu, Pagination, Wizard, SidebarStrong desktop coverage; Bottom Navigation and Speed Dial are mobile-oriented patterns, not missing desktop primitives.
Layout5Responsive, SplitPane, SplitTree, AspectRatio, Grid/ContainerStrong coverage; measured breakpoints preserve source order and caller-owned content.
Lab2Timeline, MasonryStrong coverage with caller-measured tile heights and a documented non-virtualized boundary.
Utils10Popover, Overlay, positioner/focus/portal internals, TextArea, motion system, ResponsiveFoundation coverage is present; these are not all user-facing components and should not inflate the public component count.
MUI X4 stable familiesDataGrid, Calendar/DateInput/RangePicker/TimeInput, chart family, Tree/TreeGridStrong advanced coverage, with the documented non-virtualized Table/Tree limitation.

Several MUI names intentionally resolve to existing primitives instead of new public types. Paper is a surface recipe, Typography is the typed text foundation, Stack and Box are ordinary GPUI composition, Radio Group is the caller's group of Radio controls, and Modal/Popper/Portal/Transitions are overlay and motion infrastructure. This is not a gap: duplicating them as thin wrappers would create another style system. Conversely, a component is not marked covered merely because a similarly named primitive can be composed; the state, input, semantic, and caller-owned event contracts must also line up.

The MUI- and shadcn-derived component review is complete as of 2026-08-27. Every component below has a public contract, a scene exhibit, simulated behaviour coverage, semantic ids, and a documentation entry:

Completed capabilityImplementation boundary
Multiple selection / MultiSelectSearchable controlled listbox with stable option identities, removable chips, disabled options, keyboard toggling, and caller-owned selected values.
Scalar RatingControlled whole/half precision rating with pointer and keyboard input, clearable/unrated and disabled states, and accessible value semantics.
TransferListControlled source/target assignment panes with filtering, truthful counts, individual selection, disabled items, move intents, and no component-owned mutation.
Horizontal and vertical SliderOne orientation-aware contract keeps track geometry, pointer mapping, keyboard direction, marks, range fill, and AccessKit orientation aligned.
Declarative Grid / ContainerToken-backed measured breakpoints, columns, spans, gaps, readable widths, and source-order semantics.
ImageListStable selectable media tiles with measured responsive columns and token-backed layout; media phase remains caller-owned.
MasonryStable variable-height tiles placed into the shortest measured column with responsive token-backed columns and gaps.
Auto-growing TextAreaBounded autosize measures shaped visual rows, preserves editor state, and scrolls after the maximum row bound.
Source EditorOne no-wrap TextArea projection adds hard-line numbers/geometry, revision-safe caller highlights, and caller-owned indentation without duplicating buffer, caret, IME, history, paint, or hit testing.
BubbleNeutral caller-owned message surface with placement, grouping, max width, safe content, and optional actions.
CarouselControlled stable-item track with previous/next/direct selection, keyboard navigation, clipping, reduced motion, and truthful state phases.

Ordinary MultiSelect, TransferList, ImageList, and Masonry instances are intentionally non-virtualized components. Their option/tile sets are caller-owned and should be kept to a bounded presentation size; very large datasets must use List, DataGrid, or another caller-owned virtualized surface. This is a documented performance boundary, not an unimplemented component API.

These are distinct from cross-library or product patterns such as a confirmation popover, product tour, sticky/affixed content, QR code, or watermark. They may be useful additions, but they are not MUI Core entries and must not be reported as MUI coverage gaps. Their priority should be decided by desktop product demand and by whether GPUI has the required framework primitive, not by inflating a benchmark score.

Document tabs, SearchField, FindReplace, NotificationCenter, CodeView, and UploadList are covered above. FailurePanel presents an ordinary caller-owned failure; a render error boundary is not implementable while GPUI rendering is infallible and its draw arenas are not unwind-safe.

Toggle, ToggleGroup, Collapsible, HoverCard, Menubar, CopyButton and AspectRatio are covered above. Three of them were built on top of what was already here rather than beside it, which is the whole reason they are small: Collapsible is an Accordion with one section, Menubar is a row of Menu views with the row's own three behaviours added, and Toggle is a Button that publishes a checked state. Two of them state a limit rather than inventing an answer, and docs/components.md carries both — what CopyButton can and cannot know about the clipboard, and what a hover card's grace period is for. Cascader, AnchorList, and DiagnosticsList are also covered above; they compose the existing popover/menu, navigation, list, filter, badge, and status vocabulary instead of creating parallel application infrastructure.

Mentions in a text field and settings search now share Kit contracts. MentionInput owns trigger detection, querying, stable candidate focus, caret-anchored presentation, and completion insertion while callers own candidate retrieval, stable identity, exact replacement text, and the semantic attachment of accepted identity to plain text. SettingsList filters sections and rows through the installed locale matcher, includes visible row copy and explicit caller-authored aliases/control vocabulary, preserves the settings page's familiar order, counts the result, and presents a distinct no-match state. UndoHistory covers the caller-owned revision list and reports restore intents without keeping or mutating an undo stack. The non-rendering reactive::History<T> is the bounded undo/redo record store beneath callers that need one: ignored records are refused by the model, a divergent push clears redo, and the caller remains responsible for applying every record. PerformanceHud covers live diagnostics without turning the observer into the workload: framework FrameTimingMonitor filters and bounds existing per-window submissions, while the controlled Kit view presents the caller's latest summary and never schedules another frame. Framework records preserve every draw for benchmarks, then pair the latest newly drawn scene with platform submission and its coalesced top-level input. The pairing survives concurrent trace leases and cannot cross a disable/re-enable boundary. It intentionally stops when the synchronous platform draw call returns: compositor/display presentation, upstream journal/hang reporting, and the debug overlay remain outside this port.

Application forms now cover date, time, range, files, and repeating sections. Date facts come from DateAdapter; file admissibility and display names come from SchemaFilePolicy, while the host still opens the OS picker. Repeating sections keep stable visual identity and nested values without owning product data. Host-declared conditional rules resolve into FieldVisibility: visible, or hidden with Omit / Include submission policy. Hidden fields and subtrees cannot become invisible validation blockers. values() remains the complete held-value inventory while submission_values() applies that explicit policy; the form never owns the condition or removes caller data.

LineChart and BarChart now cover the cartesian presentation gap with keyed motion, area fills, pointer and keyboard crosshairs, exact host-formatted text, and stale-data retention. Their existing normalized entry points require caller-prepared coordinates and axis wording. CartesianChart additionally provides raw f64 numeric/category/time scales, mixed layers and separate value axes, orientation, caller ticks, bounded custom glyphs, controlled exploration and keyed raw motion. Shared immutable input, explicit extrema path sampling and exact rectangle hit lookup reduce CPU work without replacing source identities. Full semantic publication and enabled motion remain linear in input size; the chart contract records measured limits. Plot supplies the lower generic measured frame and semantic mark traversal. CandlestickChart and SankeyChart render normalized OHLC and flow geometry through that boundary. SankeyData::layout additionally provides an optional validated DAG layout with one weight-to-height scale and explicit alignment and optional deterministic crossing-order improvement. RawOhlc adapts validated open/high/low/close readings to shared Cartesian intervals; SpecializedChart provides raw hierarchy/distribution/range/waterfall layouts, and ContinuousHeatmap supplies explicit continuous/diverging domains with missing-value and localized status handling. None owns market data or queries.

TraceView and SpanTimeline add controlled raw-time viewports and virtual row windows while retaining an input-sized hierarchy scan. Sized graph layout and complete route-cache inputs preserve controlled geometry; large sparse layout tests do not establish a responsive 10k-node editor. See graph/trace evidence. GeoMap renders immutable caller-supplied projected polygons/holes and points, with controlled selection/camera, localized refusal and measured geometry targets. Its projection subset excludes tile/network services, geodesic topology, antimeridian cutting and a general GIS engine.

Agent and game applications now have product-neutral run, persona, party, objective, ability, and reward families rather than one-off downstream cards. ApprovalPrompt, PermissionMatrix, CostMeter, and the context gauge keep approval, scope, and estimated costs explicit. OfferingCatalog covers Tool, Skill, and Resource results together rather than creating separate ToolCatalog and SkillCard APIs; per-server attribution is part of every result because two servers may offer the same name. LogStream, DiffView, JsonView, SchemaForm, ServerList, OfferingCatalog, and NodeGraph are covered above.

NodeGraph remains the lower-level graph whose caller supplies coordinates. AgentRunCanvas is the high-level run composition for a caller that has only typed topology: AgentRunLayout supplies its deterministic layered, RTL-aware placement. A product-specific force layout or manually persisted coordinates can still use NodeGraph directly without creating a second run presentation contract downstream.

Controlled interaction boundaries

Tree and TreeGrid horizontal navigation only selects enabled direct relatives: entering an expanded branch skips disabled direct children but never selects a grandchild or a neighboring root; a disabled parent refuses upward selection. These rules are exercised in both reading directions. Disabled MultiSelect publishes disabled root and chip semantics and mounts no chip remove buttons.

DockTree accepts a drag only when its exact source tab surface exists in the caller's topology and the dragged panel belongs to that stack. Dock roots may be prefix-related; their fully composed semantic surface ids must still be unique within the window, like other semantic identities.

The window modal stack owns Dialog/Drawer focus restoration, including non-LIFO close and the return chain after a covered modal closes. Drawer releases modal ownership at close; its delayed exit removes paint without restoring focus a second time. Callers must close a modal before removing its entity: arbitrary unmount cleanup is not yet an implemented lifecycle contract.

NodeGraph reconciles active gestures against current caller-owned nodes and interaction mode on redraw. Deleted peers are removed without rebasing surviving drag origins; deleting the active target or revoking its interaction cancels the gesture. Fit prefers native scale but the configured zoom range prevails over that preference, and the same legal zoom determines its offset and event.

Kit's shared paint-only pointer-cancel listener uses GPUI's MouseCancelEvent, not a synthetic release. Custom graph, grid range/resize, split, drawer resize, text-selection, image/model, transport, scrollbar, and drag-session state is abandoned without drop, click, or seek completion. Deterministic regressions dispatch cancellation twice and verify subsequent gestures; they do not claim physical-device/browser delivery coverage beyond the framework platform lanes.

Capabilities that are not components

GapWhy it matters
Grayscale glyph compositingMetal now applies the same DirectWrite-derived contrast/gamma alpha correction as Direct3D and WGPU when tinting atlas coverage. Glyphs remain reusable grayscale masks, so destination-aware Core Text / AppKit smoothing is still not available. macOS stays grayscale-only. Linear atlas sampling can still soften 12–13px geometric faces; changing that filter would also affect monochrome SVG icons that share the same sprite path.
Complete copies across unmounted textGPUI now coordinates one grapheme-safe selection across separately mounted StyledText participants in caller-declared reading order, with pointer capture, keyboard copy/select-all, AccessKit text runs, and overlay scope isolation. AgentDocument, CodeView, Markdown, LogStream, DiffView, and HighlightedText participate. A selection crossing virtualized rows copies the mounted text and reports that it is incomplete; whole-value component copy actions remain the path to content GPUI never laid out.
Text range highlightingHighlightedText, LogStream, CodeView and DiffView render caller-supplied ranges while constructing their text. GPUI still has no API that marks a substring of an arbitrary already-rendered text element, which blocks a generic find-in-page overlay.
Writing directionLayoutDirection supplies logical row order, start/end spacing and borders, text alignment, directional glyph mirroring, and reading-order keyboard traversal across controls, navigation, menus, calendars, trees, structured views, and schema forms. Unicode bidi shaping keeps mixed Arabic/Hebrew, Latin, punctuation, and numbers in logical order. Host-owned localized copy, locale formatting, and a larger language-specific bidi corpus remain integration work rather than component geometry.
Number, date, and quantity formattingNumberAdapter owns every library-authored numeric shape — grouped counts and decimals, editable parsing, plural category, count-of-total, percent, multiplier, dimensions, ordinals, signed deltas, lower bounds, and affix placement. Strings owns every phrase and its zero/one/two/few/many/other variants. Dates remain the parallel DateAdapter contract. See "Numbers a catalogue cannot fix alone" below.
Assistive technology gapsBasic semantics, grapheme-based editable and read-only text runs, shared shaped character/caret geometry, selection actions, explicit live-region properties, same-window labelled-by/described-by relationships, and deferred-overlay active descendants now reach GPUI's AccessKit platform tree. macOS and Windows natively verify relationship-derived field name/help and editable character/caret geometry; Windows additionally verifies ValuePattern editing and MenuItem focus/invocation/lifetime. Cross-tree completion focus is deterministic only. Native-child handoff, platform live-event verification, remaining Windows overlay/event sessions, and Linux AT-SPI validation are active foundation work; see docs/accessibility.md and docs/foundation-roadmap.md.
Validation vocabularyValidationState is the caller-owned Pending / Validating / Invalid { reason } / Valid ladder. FormField presents it without painting in-flight work as failure; SchemaForm keeps field and whole-form validation separate and blocks submission while an explicitly managed check is pending or validating. Rules and timing remain host-owned.
Schema field participationFieldVisibility records the result of a host-owned condition without evaluating it. Hidden fields are absent from rendering and field validation; HiddenSubmission::Omit removes the subtree only from submission_values, while Include preserves its complete held subtree. values stays lossless, and a hidden object or repeated-list parent governs every descendant.
Settings searchSettingsList takes the query a host commonly receives from SearchField and owns matching, filtering, result counting, and the no-match state for complete SettingsSection builders. Label, description, badge, displayed value, management reason, section context, and SettingsRow::search_terms all use the installed SearchMatcher; text hidden inside an arbitrary caller control must be named explicitly. Matches retain section and row order rather than turning preferences into a ranked command palette.
Settings groupingSettingsSection is a headed group of rows on the page plane. It no longer paints a raised card around the rows; heading, spacing, and each row's own padding carry the group. Only the control the typist can operate keeps a bounded surface.
CompositionSlotted lets a caller replace a node a component authored rather than only configure it. A component publishes only positions its public state model can actually reach as SLOTS, and a name outside that list panics rather than silently rendering nothing. Surfaces with loading and failure phases offer those distinct slots; an empty-only collection offers only empty. No component yet slots a node that is not a whole-region state.
Size responseResponsive builds its content from its own measured width, so a component laid out in a sidebar and in a full-width page arranges itself differently without either of them consulting the window. ContainerSize reports Unmeasured for the one frame before there is a width rather than guessing at one. Grid and Container now provide the shared token-backed breakpoint vocabulary; Toolbar measures its cut from the widths it recorded last frame; overflow_after remains for a caller who already knows. The remaining framework boundary is response to a size a component cannot itself be given, such as the width of a sibling.
Style escape hatchThemeOverlay installs a caller-adjusted Theme for one subtree and pops it afterwards, in every element phase, so an override cannot reach a sibling. What comes back is a whole Theme, so the subtree still reads a complete token set and a component inside it cannot tell it was overridden. There is still no way to override one property of one instance without constructing a theme for it, which is deliberate: a per-instance colour is how a library stops being one.
Non-virtualized Table and TreeList, DataGrid, and MessageList virtualize. These two lay out every row.

Numbers a catalogue cannot fix alone

gpui_kit::strings closes both halves of localization without making a component infer either one. Strings places facts inside phrases and selects host-installed zero, one, two, few, many, or other variants. NumberAdapter shapes the facts themselves: grouped counts and decimals, editable decimal parsing, count-of-total, percentages, quantities and affixes, image dimensions, ordered-list markers, signed deltas, lower bounds, and playback multipliers. NumberInput writes and reads through the same adapter, so a localized value never becomes unparsable merely because the control drew it. Pagination, search, grids, document views, media, navigation, forms, canvas, game, and agent surfaces all use that boundary for the numeric facts they author.

That does not make the library the owner of every number it receives. Caller-authored labels, clock readouts, currency and cost strings, source text, terminal output, paths, identifiers, and diagnostics remain verbatim because re-parsing them would change caller-owned meaning. Numeric geometry used only for layout, rendering, hit testing, stable ids, and debug output is not reader copy and is not localized. Date facts remain on the parallel DateAdapter; the host still owns its calendar, locale, and time zone. The built-in English adapter is a complete fallback, not a claim to discover the host locale.

Delivery

CHANGELOG.md and the versioning policy in README.md now say what a consumer pins and what breaks them, including the two breaks the compiler cannot see: a token key and a semantic id. The publishable crates are a crates.io cohort; GPUI Box is no longer a git dependency of itself. An enforceable structural and calibrated timing budget is active foundation work in docs/foundation-roadmap.md; until that phase lands, virtualization behavior tests still do not fail on every class of slowdown. The hosted catalog at gpui-box.origingame.dev is the published documentation; it is deployed from a checkout and is not itself a crates.io release.

The visual regression gate is headless check. It renders offscreen at a fixed device-pixel size, so it does not depend on a composited, frontmost window or the host display. Linux (llvmpipe) is compared at every commit by gate full on the orb; macOS (Metal) and Windows (WARP) are compared when the dispatch-only Platforms workflow runs, which is on demand rather than on push. docs/screenshot-testing.md describes the gate and review workflow.

Rules every covered component follows

  1. The answer belongs to the caller. A component holds hover, focus, open, and animation state; a value, a selection, a sort, and an expansion belong to the host, which is why every one of them reports an intent instead of applying it. Where a component needs a fact it cannot hold either — what day it is, what a month is called, whether a range's days can be listed — it takes an injected reader for that fact rather than deriving one, and a reader that answers "I don't know" is answering.
  2. A refused or disabled control installs no handler at all.
  3. Loading, empty, unstarted, unavailable, and failed are distinct, and a refusal is never rendered as an absence of data. A question nobody could answer is distinct again: a calendar with no month to show renders unavailable rather than blank, and a range whose days could not be enumerated reports unchecked rather than clear.
  4. Ids come from business identity, never from list position.
  5. Anything visible comes from tokens. Wording that belongs to the host — a refusal's reason, a month's name, a message saying why text could not be read — is shown verbatim and never authored by a component. Wording the library does author comes from gpui_kit::strings, so a host replaces it without forking the component; cargo run -p xtask -- strings check fails the build if a component grows a literal a reader could read.
  6. A component that can carry a credential publishes its shape, never its text.

Headless capture order dependence — glass probes

Metal review on 2026-09-08 found a reproducible difference between the complete catalog and the selected sequence actions cascader context-menu dialog drawer form glass mention-input menu menubar multi-select notification-center overlay toast (executed in catalog order). In the glass budget capsules/shadows, dark has 80 pixels differing by more than one channel step (maximum 2), and light has 1,623 (maximum 4). Repeated complete runs agree byte-for-byte; headless check glass alone also agrees with the complete run. Full-catalog captures remain authoritative; the one-step comparison tolerance is unchanged.

Temporary input traces ruled out surface/lobe admission drift: all 16 admitted surfaces have identical bounds, lobe counts and ordering in both runs, and only budget capsules A–G are admitted. H/I are opaque fallbacks in both. The difference is their probe ownership: complete/standalone runs assign H/I slots 14/15 with None, while the selected sequence reuses slots 3/4 and reads the previous scene's Some(0.92572516) in both themes. That value changes glass_shadows.

The former Kit ProbeLease::slot/Drop allocated/released slot numbers without invalidating renderer samples. Keyed state has a two-frame retention grace. Metal read_probe_values overwrites only requested slots, so an unadmitted replacement surface never refreshes its inherited value. The shared headless window/renderer and two-identical-frame settling test cannot detect a stable stale sample. This is a located product probe-lifetime defect, not merely atlas rounding: opening and closing overlays can give a replacement fallback surface another surface's shadow strength.

The product fix moves ownership to framework LuminanceProbeLease and LuminanceProbeCache. The existing u32 material probe is an opaque ID with 4 physical-slot bits and 28 generation bits. Reacquisition changes generation; exhausted slots retire permanently instead of wrapping or issuing the all-ones sentinel. Renderers decode only GPU offsets, retaining full IDs for cached values. Each submitted frame registers its actually encoded probes, including an empty set: fallback/unsubmitted surfaces read None. Callbacks carry the submission sequence and full ID, rejecting old owners, previous activations and out-of-order older samples. Continuously active owners keep their latest completed reading; Metal adds no GPU wait. All three backends use this shared CPU cache without changing shader or GPU buffer layouts. No renderer-per-scene reset or tolerance increase is used. Portable tests cover reuse, fallback, late callbacks and generation exhaustion; a Metal test covers actual GPU submission and slot reuse.

Metal verification: the 14-scene run and complete catalog both pass the existing one-step gate, and the complete catalog is 312/312 without replacement baselines. The original H/I shadow differences are gone. Whole glass frames are not byte-identical: the separate image-atlas rounding below remains.

The strict cross-platform reproduction uses the native Metal renderer on macOS and real software WGPU adapter on Linux/Windows, with no baseline writes:

cargo run --manifest-path tools/headless-visual/Cargo.toml -- check-order glass \
  actions cascader context-menu dialog drawer form mention-input menu menubar \
  multi-select notification-center overlay toast
cargo test -p gpui-box-wgpu a_reacquired_probe_requires_its_own_admitted_wgpu_submission -- --nocapture

check-order compares raw RGBA bytes, reports exact equality, differing-pixel counts, maximum channel step and inclusive bounds as JSON, and saves both target frames under target/headless-order-check/{full,scoped} and an amplified difference map under diff-x64. It fails even on the known one-step atlas difference: it is a diagnostic, not the tolerant catalog gate. The WGPU lease regression requires an adapter on Linux/Windows rather than silently skipping there; only a Metal host may skip the software WGPU execution.

Image-atlas placement precision — separate from probe ownership

After the lease fix, selected/full glass frames differ at 80 dark pixels and 84 light pixels, all by one channel step (none above one). Dark bounds are (1179,504)–(1430,534), light (1158,504)–(1474,538), in the Regular-on-media card's top rim/shadow, not budget capsules H/I. The comparison tolerance is not increased and these frames are not described as byte-identical.

Captured CPU inputs rule out probe ownership: both runs publish the same Regular-media luminance sequence, ending at 0.49284562 in dark and 0.5068491 in light. Sprite bounds are identically (816,448)+(768×288) device pixels. The identical 384×144 image occupies polychrome texture 0 at tile (48,0) (TileId 4096) in the selected run, versus (48,368) (TileId 4100) in the full run, with zero padding in both. Metal's polychrome vertex shader adds this atlas origin before normalized-UV interpolation; its fragment linearly samples those UVs. This is placement-dependent image sampling precision, not a leftover lease value. It is visible at the material/shadow compositing rounding boundary.

Atlas placement is already integral. Removing the half-texel source-rectangle inset would break texel-centre semantics, not correct this precision contract. A future fix needs tile-local interpolation and translation-invariant sampling, with source rectangles, transformed sprites, edge filtering and all three shader backends verified. It potentially changes every image/textured-sprite baseline on Metal, Linux WGPU and Windows; it is not hidden in probe lifetime or addressed by a headless-only atlas reset.

Clear Pill geometry and dimming order

A 200×41 Clear Pill above an image reproduced the downstream failure on Metal: raw and interior pixels were both [100,80,60]. Kit passed the Pill token's 999 radius directly to raw quad/backdrop paint, while the styled child fitted it to half the shortest side (20.5). The oversized SDF discarded both dimming and optics. BackdropLayer and collected GlassPane lobes now use the framework's existing clamp_radii_for_quad_size, matching their styled surfaces without changing raw framework paint semantics.

Fitting the radius exposed a separate ordering dependency: the same-layer black quad attenuated the completed optics, producing [80,66,53]. Clear dimming now multiplies transmission gain by 1 - effect.glassDimming inside the material, before wash/lift/highlights. No extra quad, shader ABI change or product workaround is needed. The pixel regression renders a clipped rounded card, Cover image, absolute Pill and ghost Xs button with the platform's native headless renderer. It checks the independent equation raw × 0.65 × 1.042 + 255 × 0.075, yielding [87,73,60], the retained rim, and untouched pixels outside the rounded arc. Flat elevation isolates material math from shadows. Run it on Metal or Linux/WGPU with:

cargo test --manifest-path tools/headless-visual/Cargo.toml \
  clear_pill_dims_media_inside_a_clipped_card -- --nocapture

Unicode line breaks across text and inline objects

Both LineWrapper::wrap_line and shaped LineLayout use the Unicode 15.0 UAX #14 opportunities from the existing unicode-linebreak 0.1.5 package. Wrapped truncation obtains its line starts and continuation indentation from wrap_line, rather than keeping another word-character table. Single-line ellipsis fitting still measures characters; it does not choose word breaks.

Text fragments are concatenated only for classification, with each inline element represented by U+FFFC. The width walk keeps separate normalized-text and original byte offsets, so an element's arbitrary len_utf8 never shifts the returned caller indices. Shaped glyph runs query the same Unicode rules at their logical byte indices; a font/run boundary is not a break opportunity. Tests force overflowing closing punctuation and opening brackets, cross text and element boundaries, preserve Latin words and nonbreaking glue, exercise CJK/Latin mixing, and check mandatory breaks and ellipsis run lengths.

Emergency character/item wrapping remains the fallback when no legal break fits: extremely narrow widths can still split an otherwise unbreakable unit. This is not punctuation hanging, width expansion, dictionary-based breaking for complex scripts, or a locale-tailored Japanese typography engine. The markdown exhibit includes a narrow mixed Chinese/Latin paragraph, rendered with the bundled Noto Sans SC fallback, to review punctuation and word wrapping in both themes without depending on system fonts.

Intrinsic image sizing is separate from object fitting

The apparent missing image corner mask was a layout error shared by Metal and WGPU: a 480×144 image in an 880×220 size_full card acquired an 880×264 layout box. Its lower rounded corners fell below the parent's rectangular clip. The atlas upload and shader coverage retained the correct radii.

Window::request_intrinsic_layout supplies natural dimensions separately from authored styles. An Intrinsic node is a replaced leaf: its content has a natural size independent of text flow, currently an already-loaded Img. Loading/error image replacements remain ordinary elements; text and other arbitrary measure callbacks remain stock measured leaves. The GPUI layout view uses Taffy's existing container algorithms and cache, but resolves intrinsic leaves before the stock leaf's aspect-ratio height floor can overwrite a determined axis. Ordinary measured text still uses the stock leaf. There is one active result map, retained with the tree cache and cleared with it; no stock high-level layout call runs in parallel with this view. No renderer format or dependency authority changes.

The sizing matrix exercises block, row/column flex, grid, absolute insets, resolved and unresolved percentages, explicit aspect ratio, natural sizes, padding and size constraints. A pure measure callback was insufficient in block final layout: width 240 with auto height became 240×144 instead of 240×72. Injecting the natural ratio only for one auto axis fixed that case, but width 240 with max-height 36 became 120×36. Both are regression targets, not accepted approximations. A zero-size asset has no natural ratio.

The cross-platform headless pixel regression uses 480×220 and 880×220 Cover cards with rounded images and a bottom Clear caption. It checks all four corners against the outside ground, and passes on both Metal and Linux WGPU real adapters. Kit adds no mask or component-specific sizing workaround. cinematic-effects additionally reviews Contain: a 240×140 frame in a 372×154 slot remains centered at 264×154 rather than forcing the slot to the image's intrinsic ratio.

Incremental source-text foundation

The shared EditBuffer uses persistent storage and indexed LF lines/UTF-16 conversion. Grapheme clamping borrows rope chunks, including unbounded combining and regional-indicator context. Tests exercise 200,000 source lines and a four-million-byte single line without materializing a compatibility string on edits. Legacy text() still explicitly materializes a whole value.

No-wrap TextArea and Editor shape visible rows, with exact offscreen geometry shaped on demand. Whole-document selection paints only visible rows, and accessibility cell geometry captures only paintable bytes. Shaping budgets measure actual input bytes and lines (EditableTextWork), not allocation counts. Dense and lazy Unicode/bidi geometry are compared directly.

TextArea Change and Editor Changed carry persistent EditSnapshot values; subscribers opt into contiguous whole-document text with snapshot.text(). The internal Editor subscription preserves the snapshot rather than forcing legacy string materialization. This is an API migration for consumers that previously accepted strings. painted_lines() returns owned shared lines; new event variants and work-counter fields also require consumers with exhaustive matches or struct literals to migrate.

Soft-wrap retains exact paragraph shapes across revisions and viewport changes. Cold layout and font/width reflow shape all paragraphs; a local edit reshapes the changed paragraph, but rebuilds document-wide wrapped and row indexes. Warm scrolling reuses both shapes and indexes. Accessibility receives rows from that same prepaint, avoiding a hard-row to wrapped-row topology reset after an edit. Folded layouts retain full-source accessibility rows, including hidden text. Mounted tests distinguish first-edit publication from the next settled draw rather than hiding rebuild work behind a warm frame.

Static native parents retain complete Value, and identical visible TextRuns are reused. Row arrays and grapheme representability are revision-keyed. These caches do not bound total frame or edit work: stock native tree metadata, changed selection/value publication, explicit full-value queries, and source snapshot materialization still have document-sized costs. A very long visible hard line or wrapped paragraph is shaped whole. No native Value/AXValue is omitted to meet a budget, and no local AccessKit fork is assumed.

Multicursor/rectangle edits, source folding, and caller-owned language services have their own behavior tests and exhibits; their presence does not establish a viewport-bounded large-file editor. Shaping, parser, row-index and publication counters report their named work only, not total allocations or native backend costs. Linux headless evidence does not replace the native platform lanes.

Native browser engines remain independent of Kit

gpui-box-webview supplies caller-owned Wry engine hosts: WKWebView, WebView2, and WebKitGTK on X11/XWayland. It renders full browser HTML/CSS, independently of native Markdown/HTML text rendering. BrowserPanel remains a caller-owned shell; no browser transport enters Kit. The native example and executable smoke live in crates/gpui-webview/examples; see the host contract.

The Linux framework now attaches real X11 children, crops native pixels and input through a separate rectangular parent, allocates the full toolkit viewport, and restores the child on detach. X11 native tests cover geometry, DPI, pointer hit testing, stacking and ownership. Native Wayland embedding, X11 GPUI scene overlays above native children, arbitrary subtree transforms, rounded native masks and alpha compositing are deferred framework gaps, not emulated browser support. macOS/Windows native focus, IME, accessibility and overlay behavior still need platform-run evidence. Origin-aware asynchronous permission approvals are not represented by Wry's kind-only callback; current host policy refuses surfaced permission, popup and download requests.

Native editable geometry preserves affinity, not missing font data

EditableTextLayout exposes selection fragments, cluster-edge carets, visual navigation and Unicode paragraph base-direction queries over the retained WrappedLine glyph cells and soft-wrap indices. NativeTextPosition retains both incident edges at bidi and wrap boundaries; NativeTextSelection stores the primary anchor/head affinities atomically in EditBuffer and its history. Legacy logical selection setters reset both affinities to downstream. Secondary cursors remain logical-only; native primary selection clears them. Active IME cancellation restores the original selection and text, including in secret fields, without enabling completed secret undo/redo history.

The asymmetric geometry tests supply explicit shaped glyph positions and exercise mixed bidi travel in both directions, wrap affinity, actual row heights, graphemes, UTF-16 surrogate rejection, reversed selections and history. They prove platform-independent geometry/model behavior, not UIKit or Android execution. Native adapters and the macOS/Windows lanes need their own evidence. No renderer or component painting changes are implied by these additive APIs.

ShapedGlyph has no font-provided ligature caret table or per-grapheme advances inside a multi-grapheme shaping cluster. An interior caret query, or horizontal movement requiring that caret, returns None; selection covers the complete painted cluster. Supplying GDEF/platform caret data at the shaping boundary is the prerequisite to lifting that limitation; interpolating a width is not a substitute. The current layout paints uniform-height horizontal rows. Paragraph direction overrides are not in the editable model, so mutation stays explicitly unsupported. Native caret bounds resolve only the incident hard paragraph, using indexed UTF-16 conversion for source-backed layouts; painting a visible caret preserves the viewport shaping budget. Point, selection-fragment, farthest-position and movement queries still enumerate the document and may shape offscreen lazy lines; those are not viewport-bounded operations.

Reference contract: Apple UITextInput.

Virtual-root reflow and ambient offsets

UniformList declares its measured row placement and scrolling separately through Window's placement scope. Position FLIP observes slot changes while ignoring scroll and ancestor slides; paint/input/accessibility still share the original element placement. Framework tests cover padded nonzero-origin slots, scrolling, nested placement and transient offsets. Trace mounted regressions cover accepted and refused hierarchy changes, interrupted reflow, ordinary/virtual row paths, new virtual identities, reduced motion and unchanged current readouts.

Variable-height List and custom root adapters retain their prior offset semantics until they declare authoritative content-space placement. This is a documented motion-coverage gap, not a claim of generic virtualizer reflow. Trace removed rows have no paint-retained exit; their live authority retires at once.