[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-vercel-labs-native-ui":3,"mdc--9oirov-key":36,"related-repo-vercel-labs-native-ui":22003,"related-org-vercel-labs-native-ui":22061},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":25,"repoUrl":26,"updatedAt":27,"license":28,"forks":29,"topics":30,"repo":31,"sourceUrl":34,"mdContent":35},"native-ui","build native user interfaces with Zig","Authoring guide for native-rendered Native SDK apps - declarative Native markup (.native) views plus Zig logic on the UiApp loop. Use when building or modifying native UI (widgets, layout, bindings, messages), writing .native files, wiring Model\u002FMsg\u002Fupdate, testing markup views, or verifying a native app through the automation harness.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"vercel-labs","Vercel Labs","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fvercel-labs.png",[12,16,19,22],{"name":13,"slug":14,"type":15},"Native","native","tag",{"name":17,"slug":18,"type":15},"Zig","zig",{"name":20,"slug":21,"type":15},"Desktop","desktop",{"name":23,"slug":24,"type":15},"UI Components","ui-components",6016,"https:\u002F\u002Fgithub.com\u002Fvercel-labs\u002Fnative","2026-07-26T05:49:56.17769",null,244,[],{"repoUrl":26,"stars":25,"forks":29,"topics":32,"description":33},[],"Toolkit for building native desktop apps","https:\u002F\u002Fgithub.com\u002Fvercel-labs\u002Fnative\u002Ftree\u002FHEAD\u002Fskill-data\u002Fnative-ui","---\nname: native-ui\ndescription: Authoring guide for native-rendered Native SDK apps - declarative Native markup (.native) views plus Zig logic on the UiApp loop. Use when building or modifying native UI (widgets, layout, bindings, messages), writing .native files, wiring Model\u002FMsg\u002Fupdate, testing markup views, or verifying a native app through the automation harness.\n---\n\n# Author native UI with markup + Zig\n\nA native-rendered Native SDK app is a markup view plus Zig logic:\n\n- `src\u002F\u003Cview>.native` — the entire UI: elements, layout, bindings, message dispatch.\n- `src\u002Fmain.zig` — `Model` (plain struct), `Msg` (tagged union), `update(model, msg)`, and a `main` that hands them to `native_sdk.UiApp(Model, Msg)`.\n\nThe markup compiles to the same widget tree a hand-written `canvas.Ui(Msg)` builder view would produce: identical structural widget ids, identical typed handler table. Markup can never mutate state — it binds values and dispatches messages; all logic lives in Zig.\n\nEditors highlight `.native` markup well in HTML mode — the default scaffold writes no editor config, so add `.vscode\u002Fsettings.json` with `\"files.associations\": {\"*.native\": \"html\"}` yourself, or scaffold with `native init --full`, which writes it.\n\nStart a new app with `native init` (zero-config: app.zon + src + assets, the CLI generates the build graph), or copy `examples\u002Fhabits\u002F` (smallest): change the name\u002Fid in app.zon and `assets\u002F` copies verbatim — there are no build files to edit. The `native dev|test|build` verbs drive any app directory shaped this way.\n\n## App wiring\n\n```zig\nconst HabitsApp = native_sdk.UiApp(Model, Msg);\n\npub fn main(init: std.process.Init) !void {\n    \u002F\u002F `create` heap-allocates the multi-MB app struct and constructs the\n    \u002F\u002F Model in place — neither ever rides the stack (avoid `App.init(alloc,\n    \u002F\u002F model, ...)`: its by-value Model is a stack-overflow trap once the\n    \u002F\u002F Model grows).\n    const app_state = try HabitsApp.create(std.heap.page_allocator, .{\n        .name = \"habits\",\n        .scene = shell_scene,             \u002F\u002F one window, one gpu_surface view\n        .canvas_label = \"habits-canvas\",  \u002F\u002F must match the ShellView label\n        .update = update,\n        .markup = .{\n            .source = @embedFile(\"habits.native\"),\n            .watch_path = \"src\u002Fhabits.native\", \u002F\u002F dev hot reload; omit in release\n            .io = init.io,\n        },\n    });\n    defer app_state.destroy();\n    app_state.model = initialModel(); \u002F\u002F boot state: assign through the pointer\n    try runner.runWithOptions(app_state.app(), .{ ... }, init);\n}\n```\n\n(`create` requires every Model field to carry a default; the model starts as `.{}` and boot state is assigned through the returned pointer. Tests that instantiate the app per fixture should use `create`\u002F`destroy` too — a runtime-built Model passed to `init` by value crashes the test stack once models get large.)\n\nThe runtime owns the loop: install on first GPU frame, presentation, resize, pointer\u002Fkeyboard dispatch into `update` + rebuild. With `watch_path` set, editing the `.native` file while the app runs hot-reloads the view within ~2s, preserving model state and widget ids; parse failures keep the last good view and set `app_state.markup_diagnostic` (line\u002Fcolumn\u002Fmessage).\n\n**Release: compile the markup at comptime.** `canvas.CompiledMarkupView(Model, Msg, source).build` parses the `.native` source entirely at compile time and produces the identical tree (same ids, handlers, dispatch) with no parser in the binary; markup or binding mistakes become compile errors with line\u002Fcolumn. Hand it to `.view`, and gate the runtime engine per build mode:\n\n```zig\nconst dev = @import(\"builtin\").mode == .Debug;\nconst App = native_sdk.UiAppWithFeatures(Model, Msg, .{ .runtime_markup = dev });\nconst CompiledView = canvas.CompiledMarkupView(Model, Msg, @embedFile(\"habits.native\"));\n\u002F\u002F options:\n.view = CompiledView.build,\n.markup = if (dev) .{ .source = ..., .watch_path = \"src\u002Fhabits.native\", .io = init.io } else null,\n```\n\nWith both set (dev), the compiled view renders until the watched file first changes, then the interpreter hot-reloads it. See `examples\u002Fhabits` for the full pattern.\n\n### Webview panes: canvas + live web content in one window\n\nDeclare the webview in the scene next to the gpu_surface (parent it to the canvas view), reserve its region with an empty panel carrying a semantics label, and let `Options.web_panes` snap the webview to that widget's layout frame while the model drives navigation:\n\n```zig\nconst shell_views = [_]native_sdk.ShellView{\n    .{ .label = \"app-canvas\", .kind = .gpu_surface, .fill = true, .gpu_backend = .metal },\n    .{ .label = \"preview\", .kind = .webview, .parent = \"app-canvas\", .url = \"https:\u002F\u002Fexample.com\u002F\", .x = 240, .y = 76, .width = 704, .height = 548 },\n};\n\u002F\u002F view: ui.panel(.{ .grow = 1, .semantics = .{ .label = \"preview-pane\" } }, .{})\nfn panes(model: *const Model, out: []App.WebViewPane) usize {\n    out[0] = .{ .label = \"preview\", .anchor = \"preview-pane\", .url = model.url(), .reload_token = model.reload_token };\n    return 1;\n}\n\u002F\u002F options: .web_panes = panes,\n```\n\nURL changes navigate; bumping `reload_token` reloads the same URL (the CenterPane\u002FPreview-tab shape). Pane URLs must pass `security.navigation.allowed_origins`. Panes reconcile against the runtime's live webview state on every rebuild and presented frame, so shell relayouts cannot detach them. `examples\u002Fcanvas-preview` is the live reference; `zig build test-canvas-preview-smoke` verifies it.\n\n### Menu-bar extra (status item)\n\n`Options.status_item` installs a macOS `NSStatusItem` once, on the installing frame; its menu items dispatch commands through the same `on_command` mapping the toolbar and menus use (source `.tray`):\n\n```zig\n.status_item = .{ .title = \"ZN\", .tooltip = \"My App\", .items = &.{\n    .{ .id = 1, .label = \"Refresh\", .command = \"app.refresh\" },\n    .{ .separator = true },\n    .{ .id = 2, .label = \"Quit\", .command = \"app.quit\" },\n} },\n```\n\nFor a LIVE menu-bar extra (an open-count badge in the title, a latest-items dropdown), add `Options.status_item_fn` — the `web_panes` pattern: consulted on install and after every rebuild, re-applied only when its output changed (title and menu patch independently; the static `status_item` keeps icon\u002Ftooltip). Format derived strings into the provided scratch; item `command`s dispatch through `on_command` exactly like static items:\n\n```zig\nfn statusItem(model: *const Model, scratch: *App.StatusItemScratch) App.StatusItemState {\n    const title = std.fmt.bufPrint(&scratch.title_buffer, \"ZN {d}\", .{model.open_count}) catch \"ZN\";\n    scratch.items[0] = .{ .id = 1, .label = \"Refresh\", .command = \"app.refresh\" };\n    var count: usize = 1;\n    for (model.latest(), 0..) |issue, i| { \u002F\u002F per-row commands: map \"issue.select.N\" in on_command\n        scratch.items[count] = .{ .id = @intCast(10 + i), .label = issue.title, .command = issue.select_command };\n        count += 1;\n    }\n    return .{ .title = title, .items = scratch.items[0..count] };\n}\n\u002F\u002F options: .status_item_fn = statusItem,\n```\n\nTitle updates retitle the live `NSStatusItem` button without re-creating it; platforms without a tray-title seam keep menu updates and log the title gap once.\n\n### Native scrolling (macOS)\n\nZero app code: on macOS every non-virtualized `scroll` region — and every windowed virtual list (`ui.virtualList`), whose driver content size is the full virtual extent — is driven by an invisible `NSScrollView` — OS momentum and the system overlay scrollbar — while the engine renders the content. `widget.value` stays the offset of record, so the rebuild reconcile rule (\"user offset survives rebuilds until the source offset changes\"), automation snapshot offsets (`scroll=[offset=..]`), and `Options.sync` all work exactly as before; the engine-drawn scrollbar simply stops painting for natively driven regions. Programmatic scrolls still work: change the source offset (or scroll via keyboard\u002Fautomation) and the runtime pushes it into the native scroller. GTK\u002FWin32 and mobile embeds keep the engine's wheel physics unchanged. Nested-scroll saturation handoff (inner region exhausted, outer continues) is per-region native today: the inner region stops at its edge like a standalone scroller.\n\n**Overscroll is off by default, per region, on both paths.** Scroll regions pin at their content edges — the native scroller gets non-elastic edges, the engine's wheel\u002Fkinetic physics clamp, and kinetic motion stops cleanly at the boundary. Bouncing is a per-region opt-in: `overscroll=\"rubber_band\"` in markup (the `scroll` element only — the validator rejects it elsewhere with a teaching error) or `ElementOptions.overscroll = .rubber_band` in Zig views. The `ScrollPhysics.overscroll` design token (`ScrollPhysicsOverrides` in a theme) flips the app-wide default; per-region values override it, and `.none` pins a region regardless of the token. The rubber-band shape — excursion bound, resistance, spring-back rate — stays themable through the `rubberband_*` physics tokens.\n\n### Context menus: one declared menu, platform-decided presentation\n\nAuthors write ONE menu; the platform decides how it presents. The default is the real OS context menu at the pointer — `NSMenu` on macOS, `TrackPopupMenu` on Windows, `GtkPopoverMenu` on Linux — and the selection dispatches the item's typed `Msg`. On hosts without a native menu presenter (the mobile toolkit hosts and embed hosts today), the SAME declared items present automatically as an anchored canvas surface at the click point, with the standard anchored-surface behavior (Escape and outside-click dismiss, late z-pass, window clipping). Never two authored menus, never a canvas imitation where the OS menu exists.\n\nMarkup declares the menu as a `\u003Ccontext-menu>` element — a DIRECT child of the pressable element whose right-click it answers (a hit target, or an element with a bound `on-press`\u002F`on-hold`). It is metadata, not content: it renders nothing in the row's flow. Children are `menu-item`s (`on-press` required, `disabled` optional, the text content is the label) and bare `\u003Cseparator\u002F>`s, with `if`\u002F`else`\u002F`for` around them to swap or repeat items — a menu whose items all evaporate at runtime simply declares no menu (the All Notes row pattern). Conditional MENUS are spelled as conditional ITEMS: the `\u003Ccontext-menu>` itself takes no attributes and cannot sit behind a structure tag. No submenus: the platform channel carries flat items (label, enabled, separator) only.\n\n```html\n\u003Clist-item on-press=\"open_note:{n.id}\" label=\"{n.title}\">\n  \u003Ctext grow=\"1\">{n.title}\u003C\u002Ftext>\n  \u003Ccontext-menu>\n    \u003Cif test=\"{n.deleted}\">\n      \u003Cmenu-item on-press=\"restore_note:{n.id}\">Restore\u003C\u002Fmenu-item>\n      \u003Cmenu-item on-press=\"purge_note:{n.id}\">Delete Permanently\u003C\u002Fmenu-item>\n    \u003C\u002Fif>\n    \u003Celse>\n      \u003Cmenu-item on-press=\"copy_note_id:{n.id}\">Copy\u003C\u002Fmenu-item>\n      \u003Cmenu-item on-press=\"trash_note:{n.id}\">Delete\u003C\u002Fmenu-item>\n    \u003C\u002Felse>\n  \u003C\u002Fcontext-menu>\n\u003C\u002Flist-item>\n```\n\nThe Zig builder's mirror is `ElementOptions.context_menu` — per-widget items in the chrome-menu shape with typed messages:\n\n```zig\nui.listItem(.{\n    .on_press = Msg{ .select = entry.index },\n    .context_menu = &.{\n        .{ .label = \"Open Section\", .msg = Msg{ .select = entry.index } },\n        .{ .separator = true },\n        .{ .label = \"Refresh Dashboard\", .msg = .refresh },\n    },\n}, entry.title)\n```\n\nThe deepest declaring widget on the hit route wins; disabled items and separators are fine (`enabled = false`, `.separator = true`). Zero-code defaults need no declaration: editable text fields present the standard Cut \u002F Copy \u002F Paste \u002F Select All menu wired to the existing clipboard actions, and a selected static text presents Copy (these defaults are presenter-only — without an OS menu they degrade to the keyboard clipboard paths). Touch long-press is design-noted for the mobile embeds: the iOS host's under-slop `Pending` touch state is the timer seam, pending a secondary-button leg in the embed ABI and `UIEditMenuInteraction` presentation.\n\nAutomation drives the native path honestly: snapshots list every widget's declared items in invocation order (`context_menu=[\"Rename\",\"Delete\"]`, separators keep their slots, disabled items say so), `widget-context-press \u003Cview> \u003Cid>` performs the real secondary click (presenting the menu), and `widget-context-menu \u003Cview> \u003Cid> \u003Citem-index>` invokes an item — the selection dispatches as the same `context_menu_action` platform event a real pick produces (so it journals and replays), because the OS menu's tracking loop cannot be driven programmatically. Dead invocations fail by name (undeclared menu, index out of range, separator slot, disabled item). `examples\u002Fnotes` row menus and `examples\u002Fgpu-dashboard` nav rows carry live menus; `zig build test-example-notes`, `test-example-gpu-dashboard`, and the runtime context-menu suite verify dispatch, the fallback surface, and the verb.\n\n## Elements\n\n| Markup | Widget | Notes |\n| --- | --- | --- |\n| `row`, `column` | flex containers | main axis horizontal \u002F vertical |\n| `stack`, `panel`, `card` | overlay containers | children stack on top of each other — `gap` can never space them and is a validation error (put a `column`\u002F`row` inside for flow) |\n| `scroll` | scroll_view | wrap multiple children in a `column` inside it |\n| `list`, `grid` | list, grid | vertical stack \u002F cell grid |\n| `tabs`, `toggle-group`, `button-group`, `radio-group`, `breadcrumb`, `pagination` | row containers | children flow horizontally (tab buttons, toggle-buttons, radios, ...) |\n| `table` > `table-row` > `table-cell` | table, data_row, data_cell | rows only inside a table, cells only inside a row (for\u002Fif wrappers are fine); cells are text leaves, dispatch with `on-press` |\n| `dropdown-menu` | dropdown_menu | vertical menu surface; children are `menu-item`s. `anchor=\"below\\|above\"` floats it against its PARENT's frame (see Pickers): late z-pass above the whole tree, window-clipped, auto-flipping at the window edges, zero flow space. Pair with `on-dismiss` |\n| `accordion` | accordion | header via `text` attr; children show while `selected`, dispatch `on-toggle` |\n| `alert`, `bubble` | surfaces | `alert` title via `text` attr; children stack inside. `bubble` hugs its message up to 80% of the thread (`ghost` exempt; explicit `width` wins) and takes one `\u003Creactions>` child — the reaction pill straddling its bottom edge, one text run, dock via `text-alignment` (default `end`); `text=` on bubble itself is a teaching error (that channel belongs to the pill). Grouped runs are spacing, not vocabulary: 8 gap within a sender's run, 32 between turns |\n| `dialog`, `drawer`, `sheet` | modal surfaces | rendered in place — title via `text` attr, wrap in `\u003Cif>` to show conditionally |\n| `resizable` | resizable | engine-managed drag handle; `width` sets the initial width |\n| `split` | split | two-pane horizontal splitter: exactly two element children (nest splits for more panes), the engine synthesizes the draggable divider between them. `value` binds the model-owned first-pane fraction (0 lays out at 0.5), `on-resize` names an f32 Msg variant dispatched with every applied fraction (echo it back through `value` — see Splitters), `min-width` on the panes bounds the drag, `gap` sets the divider band thickness. The divider is focusable: Left\u002FRight (Shift for bigger steps) adjust, Home\u002FEnd jump to the clamp edges |\n| `tree` | tree | disclosure-tree container (vertical flow): descendant rows carrying `role=\"treeitem\"` — at ANY nesting depth — form one roving keyboard focus set with the ARIA tree keymap. Up\u002FDown walk visible rows (selection follows focus through each row's `on-press`), Left collapses an expanded row or moves to the parent row, Right expands a collapsed row or moves to the first child row, Home\u002FEnd jump to the edges, Enter\u002FSpace activate. Expandable rows bind `expanded` and `on-toggle`; the model owns selection and expansion (collapsed children are simply not rendered) |\n| `text`, `badge`, `tooltip` | text leaves | text content, `{}` interpolation allowed; `text` line policy via `wrap` (`\"true\"` word-wraps; `\"false\"`\u002Funset paint one honest line, overflow eliding by default — `overflow=\"clip\"` opts out), and `text` alone takes the typography rungs `size=\"heading\"`\u002F`size=\"display\"` (themable token steps above title — section headings, hero stats, timer numerals). `tooltip` with `anchor=\"above\\|below\"` floats against its parent (the stack wrapping trigger + tooltip, the dropdown pattern) and the RUNTIME owns its visibility — hover intent on the trigger: shows after `tooltip-delay` ms (default 600; `\"0\"` = instant) and immediately on keyboard focus; hides on leave, focus departure, Escape, or a press of the trigger (a press also closes the warm window), and a shared 400ms warm window after a pointer-hovered tooltip hides on leave (the only hide that warms) shows the next trigger's tooltip instantly; the model never hears hover. These are shadcn\u002Fui's defaults (Base UI). Without `anchor` it stays a static leaf that paints whenever the view renders it |\n| `text` > `span` | inline styled runs | mixed-style text in ONE wrapped paragraph: span children style runs with `weight=\"regular\\|medium\\|bold\"`, `mono`, `italic`, `scale` (a positive multiplier on the paragraph's base size — inline headings, hero stats), `underline`, `foreground` (token name); `{bindings}` interpolate inside spans; whitespace between runs collapses to a single space (none = the runs abut); spans do not nest, take no events, and the paragraph announces as one text run — see \"Rich text\" |\n| `button`, `toggle-button`, `list-item`, `menu-item`, `toggle`, `switch`, `select`, `avatar` | text-bearing controls | label is the text content; `button`, `toggle-button`, `list-item`, and `menu-item` also take `icon=\"save\"` — a vector icon drawn inline (buttons\u002Ftoggle-buttons before the label, icon-only when the content is empty: add a `label`; list\u002Fmenu items as a leading slot), ONE hit target whose icon follows the element's enabled\u002Fdisabled tint (no overlay stacking, no duplicated `on-press`); tab strips are `toggle-button` children, so tabs get icons this way; `select` shows `placeholder` while empty and dispatches `on-press`; `avatar` renders initials, or a runtime image via `image=\"{binding}\"` (see the Images section) |\n| `checkbox`, `radio`, `slider`, `progress` | value controls | `checked`, `value` (a 0..1 fraction on slider and progress; progress clamps out-of-range values at render, never an error); the checkbox\u002Fradio label rides `text=\"...\"` — these are not text-bearing elements, so text content is a teaching error (`label=` alone names one for accessibility without a visible label); a slider's `value` follows the source when it MOVES (model-driven progress renders every rebuild) and keeps the user's drag while the source replays the same value — use `slider` for seek bars, `progress` for display-only; a markup slider's `on-change` dispatches a PLAIN Msg with no value payload — mirror the applied value into the model with `Options.sync` (the Zig builder's `on_value = Ui.valueMsg(.tag)` does deliver the applied f32) |\n| `text-field`, `input`, `search-field`, `combobox`, `textarea` | text entry | `placeholder`; edits via `on-input`, enter via `on-submit` on single-line kinds; in a `textarea`, Enter (and Shift+Enter) inserts a newline and `on-submit` dispatches on primary+Enter (cmd on macOS, ctrl elsewhere); `search-field` carries a built-in trailing clear affordance whenever it holds text (press the x, or Escape while focused — both clear through the text-edit path, so `on-input` hears it; no attribute, no external Clear button needed) |\n| `status-bar` | status bar | text leaf: content only, no children |\n| `separator`, `spacer` | separator, flexible space | `separator` is axis-aware: a horizontal rule in a `column`, a thin vertical divider in a `row`; give `spacer` a `grow` |\n| `skeleton`, `spinner` | loading leaves | size `skeleton` with `width`\u002F`height` |\n| `icon` | vector icon leaf | `name` picks the icon: a bare literal is a curated built-in stroke icon (compile-checked; 49 names: search, plus, x, x-circle, check, check-circle, chevron-up\u002Fdown\u002Fleft\u002Fright, arrow-up\u002Fdown\u002Fright, menu, panel-left, panel-right, settings, terminal, wrench, trash, edit, copy, external-link, play, pause, skip-back\u002Fforward, shuffle, repeat, music, volume, info, alert, download, save, folder, folder-open, file-text, sun, moon, eye, clock, git-pull-request, git-merge, git-branch, circle-dot, archive, refresh-cw, send); `app:\u003Cname>` reaches an icon the app registered at boot with `canvas.icons.registerAppIcons` (declare the table as `pub const app_icons` on the app root so `native check` verifies the name against the model contract), and one `{binding}` defers the choice to model data - an unknown resolved name draws the missing-icon fallback (a slashed circle) with a Debug warning naming the value, never a silent gap; tint with `foreground`, size with `width`\u002F`height` |\n| `media-surface` | media surface leaf | composites a texture produced OUTSIDE the widget tree (video decoder, camera, an external renderer like mpv) into the layout like any widget — clipped, z-ordered, rounded. `surface=\"{binding}\"` (required) binds the model-owned u64 surface id a Zig-tier producer targets (`runtime.acquireMediaSurfaceProducer` pushes RGBA8 frames, latest-wins, paced by the presented-frame clock; 0 = unbound, draws nothing; usable ids are nonzero values below the reserved bit 63). No intrinsic size — give it `width`\u002F`height` or `grow`; display-only (presses fall through); `label` it (pictorial content). Texture contents are presentation chrome: goldens, reference screenshots, and session replay show the deterministic id-derived placeholder, never producer frames |\n| `image` | runtime image leaf | draws a RUNTIME-REGISTERED image by its model-owned u64 ImageId — the id `Cmd.imageLoad` (TS) or `fx.loadImage`\u002F`fx.registerImageBytes` (Zig) registered pixels under. `image=\"{binding}\"` (required) binds a model field\u002Ffn; ids are model data, never markup literals, and 0 draws nothing (store the id only when the load reports loaded — see the Images section). No intrinsic size — give it `width`\u002F`height` or `grow`; display-only (presses fall through); `label` it (pictorial content) |\n| `markdown` | rendered markdown subtree | leaf; `source` is one `{binding}` — see \"Markdown in markup\" |\n| `stepper` > `step` | composite stage track | `active=\"{index}\"` (required) derives each step's completed\u002Factive\u002Fpending state; steps are text leaves (no attributes) joined by connectors; stepper also takes `key`, `global-key`, `label` |\n| `timeline` > `timeline-item` | composite ledger list | items only inside a timeline (for\u002Fif fine); items are leaves — `title` (required), `description`, `meta`, `indicator`, `variant`, `connector=\"false\"` on the last item, `selected`; `on-press` makes the whole item pressable with a trailing chevron |\n| `chart` > `series` | composite data chart | series only inside a chart, and only series (the set is static — data varies through bindings); each series is a leaf — `values=\"{binding}\"` (required) names a model `[]const f32` iterable, `kind` is `line`\u002F`area`\u002F`bar` (literal), `color` a token name, `label` the semantics name; chart takes `y-min`, `y-max`, `grid-lines`, `baseline`, `x-labels`, `y-labels`, `hover-details`, `stroke-width`, box options, `label` — see \"Charts\" |\n| `context-menu` | consumed by its parent | right-click menu on its DIRECT parent (a hit target or an element with `on-press`\u002F`on-hold`); metadata, never a flow child. Children: `menu-item`s (`on-press` required, `disabled` optional, no `icon`) and bare `separator`s, with `if`\u002F`else`\u002F`for` around them. Attribute-less; presents natively where the host has a menu presenter, as an anchored surface elsewhere — see \"Context menus\" |\n| `input-group` > `textarea` + `input-group-actions` | composite grouped input | the composer shape: ONE bordered field wrapping exactly one `textarea` (first — document order is focus order) plus an optional `input-group-actions` row of controls inside the same border. The group wears the focus ring for its focused descendant and the textarea's own chrome dissolves automatically, so the whole group reads as one field; the textarea keeps its full behavior (`text`, `placeholder`, `on-input`, `on-submit`, `autofocus`). Group takes `label`, `width`, `height`, `min-width`, `grow`, `key`, `global-key`; the actions row takes `gap` and holds ordinary elements (`if`\u002F`else`\u002F`for` work — swap send for stop while streaming) — put a `\u003Cspacer grow=\"1\"\u002F>` between leading and trailing controls (`Ui.inputGroup`\u002F`Ui.inputGroupActions` are the Zig-view equivalents) |\n\nNot markup-expressible (deliberately — write these as Zig view functions with `canvas.Ui`): `icon_button` (`\u003Cbutton icon=\"...\">` with empty content is the declarative icon button), `data_grid` (per-column cell templates), `popover`\u002F`menu_surface` (anchored to runtime geometry), `segmented_control` (use `tabs`\u002F`toggle-group`: `\u003Cbutton>` children of `\u003Ctabs>` lower to segmented triggers automatically, so the active tab lifts per the house treatment). Charts ARE expressible: `\u003Cchart>` with `\u003Cseries values=\"{binding}\">` children binding model f32 iterables — see the Charts section (`.band` series and dynamic series composition stay with `ui.chart`). Built-in vector icons ARE expressible: `\u003Cicon name=\"search\"\u002F>` (closed, compile-checked name set; `Ui.icon` is the Zig-view equivalent). App-authored icons: `canvas.svg_icon.parseComptime(@embedFile(\"icons\u002Flogo.svg\"))` parses any SVG in the common 24x24 stroke-icon dialect at comptime; register the parsed table once at boot with `canvas.icons.registerAppIcons(&table)` and draw by name via `ui.appIcon(.{...}, \"logo\")` or `ElementOptions.icon` — registered names render exactly like built-ins on every draw path. Markup `\u003Cicon>`\u002F`\u003Cbutton icon>` stay built-in-only (the compiled engine validates names at comptime, where runtime registrations cannot exist — engine parity). Runtime images ARE expressible: `\u003Cimage image=\"{cover}\" width=\"120\" height=\"80\" label=\"Cover art\"\u002F>` and `\u003Cavatar image=\"{user_image}\">CT\u003C\u002Favatar>` bind a `u64` ImageId model field\u002Ffn (the id is just model data; 0 draws nothing \u002F keeps the initials fallback) — see the Images section; the `image` binding is required on the leaf (an unbound `\u003Cimage>` is dead markup) and stays avatar+image scoped.\n\n## Attributes\n\nLayout: `gap` (flow containers only — stacking containers `stack`\u002F`panel`\u002F`card`\u002F`alert`\u002F`bubble`\u002F`dialog`\u002F`drawer`\u002F`sheet`\u002F`resizable` layer their children, so `gap` there is a validation error, not silence: wrap the children in a `column`\u002F`row` inside; on `split` it sets the divider band thickness), `padding` (uniform), `grow`, `width`, `height` (definite: the element is exactly that size — intrinsic content neither shrinks nor silently overflows it; `resizable` treats `width` as the initial width), `min-width` (a floor WITHOUT `width`'s definite max — the element may grow past it but never shrink below; on split panes it bounds the divider drag), `wrap` (`text` only: `wrap=\"true\"` word-wraps at the width the element receives and reserves the wrapped height in columns; `wrap=\"false\"` and unset are honest single-line — one line whose overflow follows `overflow`), `overflow` (`text` only, a teaching error elsewhere: what a single line does with content that does not fit — `ellipsis`, the default, elides behind a trailing … measured with the same metrics paint uses, right for width-constrained list-row titles; `clip` hard-cuts at the frame for fixed-format content like a duration column where \"1…\" beats nothing; there is deliberately no overflow-visible), `text-alignment` (start|center|end — text leaves, status bars, surface titles; controls that own their label placement ignore it), `columns` (`grid` only: fixed column count, omit for the derived near-square grid; a teaching error elsewhere), `main` (start|center|end|space_between), `cross` (stretch|start|center|end), `virtualized`, `virtual-item-extent`, `anchor` (`dropdown-menu` and `tooltip`, literal `below`\u002F`above`: floats the surface against its parent instead of the flow — auto-flips when the preferred side does not fit, height clamps to the chosen side, x clamps into the window; an anchored tooltip's visibility is runtime-owned hover intent, unlike the model-owned dropdown), `anchor-alignment` (with `anchor`: `start`\u002F`end`\u002F`stretch` — stretch also widens the surface to at least the anchor's width, the select-menu look), `anchor-offset` (with `anchor`: literal gap in points, default 4), `tooltip-delay` (`tooltip` only, beside `anchor` — a teaching error elsewhere or without it: hover-intent show delay in ms, default 600, `\"0\"` = instant; keyboard-focus reveals are always immediate), `overscroll` (`scroll` only, a teaching error elsewhere: `none` pins the region at its content edges — the shipped default via the `ScrollPhysics.overscroll` token — `rubber_band` lets it bounce past them on both the engine and native paths, `default` follows the token).\nAppearance\u002Fstate: `variant` (default|primary|secondary|outline|ghost|destructive), `size` (the control scale default|sm|lg|icon on every sized element; on `text` also the typography rungs heading|display — named typography token steps (`heading_size` 28, `display_size` 48, themable like every token) for section headings and hero stats\u002Ftimer numerals. The two axes stay apart: heading\u002Fdisplay on a control is a teaching error naming text as their home, unknown values list the vocabulary, and numeric sizes are refused by design — retheme the typography tokens to move the whole scale), `disabled`, `checked`, `selected`, `value`, `placeholder`, `icon` (`button`, `toggle-button`, `list-item`, `menu-item`: vector icon drawn inline — buttons\u002Ftoggle-buttons before the label, list\u002Fmenu items as a leading slot; a teaching error anywhere else. A built-in name, `app:\u003Cname>`, or one `{binding}` resolving to such a name). **One size register per row**: every control class shares the control height at a given register (default 36, sm 31.5, lg 40.5 before density), so a toolbar\u002Ffilter row reads as one height exactly when every control in it carries the SAME `size` — mixing `size=\"sm\"` buttons with a default field renders two heights in one row, and hand-sized pressable panels (`height=\"30\"`) never land on the scale; compose rows from real controls at one register.\nFocus: `autofocus` (focusable controls only — a teaching error elsewhere): moves keyboard focus to the element when it MOUNTS or when the bound value turns on, edge-triggered so holding it true never re-steals focus from the user. The TEA way to focus an editor on note-create (`\u003Ctext-field autofocus=\"{editing}\" ...>` or mount the field under an `\u003Cif>` with `autofocus=\"true\"`; Zig views use `ElementOptions.autofocus`) and to give keyboard-first apps their first focus without a click.\nSemantics: `role` (listitem, treeitem, button, ...; `treeitem` also makes the row part of its tree's roving keyboard focus set), `label` (accessible name — it REPLACES the element's text content as the announced name, so snapshot greps and screen readers see the label, never the text; don't `label` an element whose visible text you grep for), `expanded` (tree rows: disclosure state, model-owned — omit on leaves). Accessible names are ENFORCED: an interactive control with no text content, no `text=`, and no `label=` is a validation error (icon-only controls need `label`; text-entry controls need `label` or `placeholder`), unknown\u002Fmisused literal roles are errors (`role=\"tree\"` on a text leaf can never hold rows), unnamed avatars and labels duplicating the text content are warnings (`label=\"\"` marks an image decorative). Zig-built trees get the same discipline from `canvas.expectA11yAuditSweepClean` (missing names as the bridges would announce them, focusables clipped out of keyboard reach, identically labeled siblings) — adopt it next to the layout sweep.\nIdentity: `key` (sibling-scoped), `global-key` (parent-independent — use for items that move between containers, e.g. board cards; ids then survive reparenting).\nWindow chrome: `window-drag=\"true\"` (Zig: `.window_drag = true`) marks the element as a window-drag surface for hidden-titlebar windows — pressing its background or plain text\u002Ficons inside moves the WINDOW (drag starts only on actual movement), double-click zooms per the OS convention, and press-claiming children (buttons, fields) stay fully interactive via the ordinary press fall-through. macOS-only; elsewhere the press is dead space. See \"Hidden titlebar\" below.\nRender channel (Zig-only, no markup attributes): `ElementOptions.opacity` and `ElementOptions.transform` wrap the element's emitted commands without reflowing siblings — the defaults (1, identity) emit nothing, opacity 0 culls painting (pair with `disabled` when fading interactive content), and a transform moves both rendering and pointer hit-testing while accessibility frames stay at the layout frame. Pair with `UiApp.Options.animations` for tweening.\n\nNumbers are plain (`gap=\"12\"`), booleans are `true`\u002F`false` or a binding.\n\nWhen children's minimum sizes exceed their container, debug builds log a `zero_canvas_layout` diagnostic naming the container, axis, and overflow in pixels — flex overflow is never silent. In Zig views, `.gap` on a stacking kind (`ui.panel(.{ .gap = 8 }, ...)`) logs a `zero_canvas_ui` warning in debug builds with the same lesson — it never fails the build.\n\n### Chips: exclusive selection with `selected=`\n\nThe chip pattern — an exclusive group where the model owns which one is active — is a `toggle-group` of `toggle-button`s (or plain `button`s) whose `selected=` binds the model:\n\n```html\n\u003Ctoggle-group gap=\"2\" label=\"Theme\">\n  \u003Cfor each=\"theme_prefs\" as=\"p\">\n    \u003Ctoggle-button size=\"sm\" selected=\"{p == theme_pref}\" on-toggle=\"set_theme:{p}\">{p}\u003C\u002Ftoggle-button>\n  \u003C\u002Ffor>\n\u003C\u002Ftoggle-group>\n```\n\nA `toggle-button` whose source asserts `selected` (this rebuild or the previous one) is model-driven: the source wins over the runtime's retained toggle on every rebuild, so exactly the model's selection is active — pressing a chip dispatches the Msg, the model moves the selection, and the old chip deactivates. Without a `selected=` that ever asserts, a `toggle-button` is uncontrolled: the runtime retains its pressed state across rebuilds (the multi-select formatting-bar case — bold\u002Fitalic chips with zero app wiring). `button` with `selected=` is always model-driven (buttons never retain state) and dispatches `on-press`; `toggle-button` dispatches `on-toggle` (its activation is the toggle intent — an `on-press` there never fires). Model-driven chips need a handler that actually moves the model: a chip whose Msg is ignored keeps its retained press until the model asserts its `selected=`.\n\n### Pickers: select is the trigger — compose the options as an ANCHORED dropdown\n\n`select` and `combobox` are trigger controls, not complete pickers: `select` renders the closed dropdown shape (current value as content, `placeholder` while empty, `on-press` to open) and `combobox` is a text entry with a menu chevron — neither owns an options list. There is no `options=` attribute (the closed grammar has no list-valued attributes — list-shaped vocabulary is element children, the way `\u003Ccontext-menu>` declares its items); the options ARE the composition — a `dropdown-menu` of `menu-item`s under an `if`, beside the trigger inside a `stack`, floated with `anchor`:\n\n```html\n\u003Cstack>\n  \u003Cselect placeholder=\"Pick a repo\" text=\"{current_repo}\" on-press=\"toggle_repo_picker\"\u002F>\n  \u003Cif test=\"{repo_picker_open}\">\n    \u003Cdropdown-menu anchor=\"below\" anchor-alignment=\"stretch\" on-dismiss=\"close_repo_picker\">\n      \u003Cfor each=\"repos\" key=\"name\" as=\"r\">\n        \u003Cmenu-item on-press=\"pick_repo:{r.name}\" selected=\"{r.name == current_repo}\">{r.name}\u003C\u002Fmenu-item>\n      \u003C\u002Ffor>\n    \u003C\u002Fdropdown-menu>\n  \u003C\u002Fif>\n\u003C\u002Fstack>\n```\n\nHow the pieces fit, all model-owned (TEA):\n\n- **Open state is the model's.** `toggle_repo_picker` flips the bool; the surface exists only while the `if` renders it. There is no hidden engine open flag.\n- **`anchor` floats the menu.** The dropdown positions against its PARENT's frame (the `stack`, sized by the trigger): below it by default, flipping above when it doesn't fit and the other side has more room, height clamped to the chosen side, x clamped into the window. It consumes NO space in the flow (siblings never reflow), paints in a late z-pass above the whole tree, and escapes every ancestor scroll\u002Fclip region — window-clipped, not pane-clipped. `anchor-alignment=\"stretch\"` widens it to at least the trigger's width (the select look).\n- **`on-dismiss` closes it model-side.** Escape and a click outside the menu dismiss the surface and dispatch the Msg; `close_repo_picker` clears the bool. Escape works even when the trigger took no focus (a plain-text crumb): with no relevant focus chain it dismisses the topmost mounted anchored surface. The engine hides the surface immediately (the optimistic echo), and the next rebuild's source tree is truth — a model that keeps `open` true gets it back. Clicking the TRIGGER while open never double-fires: the anchor region owns its surface's toggling, so only `toggle_repo_picker` dispatches.\n- **Items close on pick.** `pick_repo` sets the value AND clears the open flag — a click inside the surface never dismisses.\n- **Keyboard**: once focus is in the menu (tab into it), tab wraps inside the surface (the floating focus scope) and Enter\u002FSpace activate items; Escape dismisses from the trigger or the menu.\n- **Automation sees everything**: the floating menu and its items appear in widget snapshots at their real frames and `widget-click \u003Citem-id>` works while it is open.\n\n`combobox` composes the same way (the model filters the `for` source as the user types via `on-input`). The Zig mirror is `ElementOptions.anchor`\u002F`anchor_alignment`\u002F`anchor_offset` + `on_dismiss` on a `dropdown_menu` (or `popover`\u002F`menu_surface`, which stay Zig-only) built with `ui.eachCtx` for the options. Budget: at most 16 anchored surfaces may be mounted per view (`max_canvas_widget_anchored_per_view`, loud `error.WidgetAnchoredSurfaceLimitReached`) — an `anchor` inside a `\u003Cfor>` body is almost always a mistake.\n\n### Splitters: split panes with a model-owned fraction\n\n`split` is the resizable two-pane seam: exactly two element children, and the engine synthesizes the draggable divider between them (resize cursor, focusable, ARIA separator whose value is the fraction). The fraction is MODEL-OWNED — the runtime applies each drag\u002Fkeyboard step as an optimistic echo, dispatches `on-resize` with the applied fraction, and the model echoes it back through `value` so the next rebuild lays the panes exactly there:\n\n```html\n\u003Csplit value=\"{sidebar_split}\" on-resize=\"sidebar_resized\">\n  \u003Ccolumn min-width=\"150\">…sidebar…\u003C\u002Fcolumn>\n  \u003Csplit value=\"{list_split}\" on-resize=\"list_resized\">\n    \u003Ccolumn min-width=\"220\">…list…\u003C\u002Fcolumn>\n    \u003Ccolumn min-width=\"280\">…editor…\u003C\u002Fcolumn>\n  \u003C\u002Fsplit>\n\u003C\u002Fsplit>\n```\n\n- **`on-resize` names an f32 Msg variant** (`sidebar_resized: f32`); `update` stores it (`model.sidebar_split = fraction`). The delivered fraction is the value the runtime already applied and clamped, so echoing it never fights the reconcile.\n- **`min-width` on the panes bounds the drag** — the divider clamps so neither pane shrinks below its floor, on drag, keyboard, and layout alike.\n- **Uncontrolled works too**: without `on-resize`, the divider position survives rebuilds under the source-wins reconcile (a source-side `value` change wins), but pane CONTENT lays out at the declared fraction until the model echoes — bind the handler for the exact controlled loop.\n- **Keyboard**: Tab reaches the divider; Left\u002FRight step the fraction (Shift for 2x), Home\u002FEnd jump to the clamp edges. Automation drives it with `widget-drag`\u002F`widget-key`, and snapshots show the divider as `role=separator` with the fraction as its value.\n- **Animated resize**: `resize-duration=\"180\"` (milliseconds, `split` only — a teaching error elsewhere) makes the bound `value` a target — a model-driven move (a collapse toggle, not a drag echo) eases the rendered fraction there one presented frame at a time instead of snapping, dispatching the same `on-resize` echoes a drag would; `resize-easing` (`linear`\u002F`standard`\u002F`emphasized`\u002F`spring`) shapes the ramp and needs the nonzero duration beside it (alone it is a teaching error), and reduced-motion appearances snap automatically — apps declare nothing extra.\n- Three panes = nested splits, as above. More than two children is a validation error (put conditional content inside a pane).\n\n### Trees: disclosure rows with the ARIA tree keymap\n\n`tree` turns a rail of pressable rows into a keyboard-navigable disclosure tree. Rows are ROLE-driven: any pressable element carrying `role=\"treeitem\"` — at any nesting depth under the tree — joins one roving focus set:\n\n```html\n\u003Ctree gap=\"2\" label=\"Folders\">\n  \u003Cfor each=\"folderRows\" key=\"id\" as=\"f\">\n    \u003Cpanel role=\"treeitem\" expanded=\"{f.expanded}\" on-press=\"select_folder:{f.id}\" on-toggle=\"toggle_folder:{f.id}\" label=\"{f.label}\">\n      \u003Crow gap=\"8\" cross=\"center\">\u003Cicon name=\"folder\"\u002F>\u003Ctext grow=\"1\">{f.name}\u003C\u002Ftext>\u003C\u002Frow>\n    \u003C\u002Fpanel>\n  \u003C\u002Ffor>\n\u003C\u002Ftree>\n```\n\n- **Up\u002FDown walk the visible rows** in tree order, across nesting levels. Selection follows focus: each move dispatches the landed row's `on-press`, so the model owns the selection exactly like a click.\n- **Left\u002FRight are disclosure keys**: Left on an expanded row dispatches its `on-toggle` (collapse); on a collapsed row or leaf it moves focus to the PARENT row. Right on a collapsed row dispatches `on-toggle` (expand); on an expanded row it moves to the first child row.\n- **Home\u002FEnd** jump to the scope's first\u002Flast row; **Enter\u002FSpace** activate (`on-press`).\n- **Expansion is model-owned**: expandable rows bind `expanded` (omit it on leaves) and the model renders child rows only while expanded — collapsed subtrees are simply not in the tree, so \"visible rows\" needs no engine bookkeeping. Flat rails (the notes folder list) are honest trees of leaves: Up\u002FDown\u002FHome\u002FEnd\u002FEnter work, Left\u002FRight are inert.\n- Single-select: selecting a row clears the previous selection across the WHOLE tree scope (rows nest, so this is not per-parent).\n\n### Press-and-hold: on-hold\n\n`on-hold` is the click-acts, hold-reveals menu-button shape — a control that acts on click and offers more on hold: a pointer held ~350 ms dispatches the hold Msg (the release then presses nothing), a quick click dispatches `on-press` as usual, and a right\u002Fctrl-click whose route offers no context menu dispatches the hold Msg immediately (a declared `\u003Ccontext-menu>` always wins the right-click — hold is the primary-button gesture, not the context-menu channel). Like `on-press`, binding it makes any element pressable. The breadcrumb-switcher pattern: `on-press` selects the crumb, `on-hold` opens an anchored `dropdown-menu` of its siblings — an app-designed hold-reveal surface, distinct from the row's right-click menu. Both legs are live-drivable: `native automate widget-hold \u003Cview> \u003Cid>` runs the pointer+timer gesture, `widget-context-press \u003Cview> \u003Cid>` the secondary click.\n\n```html\n\u003Cbutton on-press=\"select_crumb:{c.id}\" on-hold=\"open_crumb_menu:{c.id}\">{c.name}\u003C\u002Fbutton>\n```\n\n### Select, then act: double press and row-level Enter\n\nThe desktop list convention — click selects, the primary action (open the record, play the track) rides the double click and Enter — is two bindings on the same row. `on_double_press` (Zig views only — `ElementOptions.on_double_press`; the closed markup grammar has no double-click event) dispatches on the release whose runtime-derived click count reached 2, in place of a second press: the first click still dispatches `on_press` on its own release, so the pairing is additive, never a delay — no press timer, no swallowed first click. Like `on_press`, binding it makes the element a hit target; a widget with no double handler treats a double click as two single clicks. The keyboard mirror is row-level Enter: on a `list-item`, `on-submit` grows a second home beyond text entry — with a submit handler bound, plain Enter on a keyboard-focused row dispatches it as the row's PRIMARY action while Space keeps the select activation (`on-press`); rows without one resolve Enter as select, unchanged. It works from markup (`\u003Clist-item on-press=\"select_track:{t.id}\" on-submit=\"play_track:{t.id}\">{t.title}\u003C\u002Flist-item>`) and Zig views alike, and tests drive it through `msgForKeyboard` (an Enter event resolves the submit handler, Space the press). `examples\u002Fsoundboard`'s track rows are the live reference: `on_press` select, `on_double_press` play, `on_submit` play.\n\n## Widget budgets and virtualization\n\nEvery view has fixed per-view capacities (`src\u002Fruntime\u002Fcanvas_limits.zig`): **1024 retained widget nodes** (`max_canvas_widget_nodes_per_view` — the budget that matters for tree design; semantics and spans match it), 64 KiB retained widget text, **512 declared context-menu items** summed across all widgets of the view (`max_canvas_widget_context_menu_items_per_view` — separators count as items), **64 chart series \u002F 16384 chart points** summed across all charts of the view (`max_canvas_widget_chart_*` — `ui.chart` downsamples every series to 256 points, so this is 64 maximal series or hundreds of sparklines), and per-frame content budgets (2048 commands, 8192 glyphs, 32 KiB frame text, 2048 path elements shared by icons and charts). Overflow is loud: `error.WidgetLayoutListFull` \u002F `error.WidgetNodeLimitReached` \u002F `error.WidgetContextMenuLimitReached` \u002F `error.WidgetAnchoredSurfaceLimitReached` (at most **16 anchored floating surfaces** mounted per view — `max_canvas_widget_anchored_per_view`) fail tests under the harness's propagate policy and log a teaching diagnostic naming the budget in production (the app degrades to the previous frame). Watch headroom without overflowing: automation snapshots report `widget_nodes=N\u002F1024 widget_semantics=N\u002F1024 context_menu_items=N\u002F512` on every gpu_surface view line.\n\nBudget rules of thumb: 1024 nodes is roomy for a three-pane desktop app (~500 nodes measured for a dense sidebar + markdown detail + run surface), but node count scales with what is MOUNTED, not what is visible — so bound every unbounded collection:\n\n- **Dataset-scale uniform rows (feeds, logs, tables of thousands+): use the WINDOWED virtual list** (`ui.virtualWindow` + `ui.virtualList`, Zig views — see the next section). The view builds only the visible window; the runtime owns the scroll; budgets stay viewport-sized at 100k items.\n- `virtualized` on `scroll`\u002F`list`\u002F`grid`\u002F`table` (with `virtual-item-extent` for fixed-extent items) lays out only the visible window + overscan; a 10,000-item list materializes ~viewport\u002Fextent nodes. It bounds NODES, not your source data: the builder still walks every item, so it suits row sets the model already holds (hundreds). Legacy virtualized containers without a declared item count are app-driven for scrolling (wheel offsets do not mutate them).\n- For non-uniform content (chat transcripts, ledgers, diffs), keep a bounded window in the model and slide it with `on-scroll` (see Messages) or explicit paging — the window follows the scrollbar instead of mounting everything.\n- Remember multi-node rows multiply: a 4-node row × 50 mounted rows is 200 nodes before chrome.\n\n## Windowed virtual lists (Zig views): 100k rows, viewport-sized budgets\n\nThe honest infinite-scroll primitive. The RUNTIME owns the viewport math (retained scroll offset + viewport → visible index range, from a fixed per-item extent), the MODEL owns the data, and the view is the seam between them: ask `ui.virtualWindow` for the visible range, build ONE keyed node per item in it, hand both to `ui.virtualList`. The list is a runtime-scrolled scroll region — engine wheel\u002Fkinetic\u002Fkeyboard everywhere, the native scroll driver on macOS — whose scrollbar spans the FULL virtual extent (`item_count × stride`), and every scroll observation re-derives the view so the window follows the offset with no app wiring.\n\n```zig\nconst options = Ui.VirtualListOptions{\n    .id = \"timeline\",            \u002F\u002F stable identity: global key + scroll-state lookup\n    .item_count = model.loaded,  \u002F\u002F TOTAL items the model holds right now\n    .item_extent = 84,           \u002F\u002F fixed row height (v1 contract: uniform rows)\n    .overscan = 4,\n    .grow = 1,\n    .on_reach_end = .load_more,  \u002F\u002F infinite fetch: update appends the next batch\n};\nconst window = ui.virtualWindow(options);\nconst rows = ui.arena.alloc(Ui.Node, window.itemCount()) catch { ui.failed = true; return ui.column(.{}, .{}); };\nfor (rows, 0..) |*row, offset| {\n    const index = window.start_index + offset;\n    var node = rowView(ui, model, index);\n    node.key = .{ .int = @intCast(index) };  \u002F\u002F identity = the ITEM, not the slot\n    row.* = node;\n}\nreturn ui.virtualList(options, window, .{rows});\n```\n\nRules:\n\n- **Key every row by item identity** (index or id). A row that scrolls away and back returns under the same structural id, so engine-owned row state and model-owned per-item state (selection washes, like counts keyed by index in the model) survive window shifts.\n- **No `on_scroll` needed.** The runtime re-derives the view on scroll for mounted virtual lists; bind `on_scroll` only when the model wants to observe the position. Do not echo an offset into `value` — `virtualList` mirrors the runtime offset itself.\n- **`on_reach_end` has hysteresis built in**: fires once when a scroll comes within one viewport of the end, re-arms past 1.5 viewports — which appending a batch causes on its own by growing the extent. One Msg per approach, never a fetch storm. It works on ANY scroll container (`on-reach-end` on `scroll` in markup). `on_reach_start` is the exact mirror for the content START (load older history; Zig views only).\n- **Uniform rows are the fast path**: a non-zero `item_extent` makes 100k rows pure arithmetic. Give such rows single-line text (`wrap = false`) or fixed sub-layouts that fit the extent.\n- **Variable-extent rows (chat transcripts, mixed-height feeds, markdown-bearing lists)**: set `extent_estimate` (a cheap pure fn: `fn (context, logical_index) f32`, derived from model facts like line\u002Fbyte counts — NEVER from layout) and leave `item_extent` 0. Rows lay out at their intrinsic wrapped heights; the engine measures mounted rows and corrects an internal offset table, so the scrollbar geometry CONVERGES to truth as the user scrolls. Corrections are anchored on the first visible row — the scrollbar may drift as estimates correct (the honest behavior), but visible content NEVER jumps. Rough estimates are fine; wildly wrong ones just mean more scrollbar drift.\n- **Tail anchoring (the chat contract)**: `anchor = .trailing` opens the list at the bottom and keeps it pinned there while the user sits at the bottom (appends never yank a scrolled-away viewport). Works for uniform rows too (`item_extent` doubles as the estimate).\n- **Prepending history**: give items LOGICAL indices via `index_base` (e.g. the first loaded message's sequence number) and key rows by `index_base + physical`. To load older history, decrease `index_base` by the prepended count in `update` — row identity, measured extents, and the viewport anchor all survive, and the offset grows by the prepended extent so the user keeps reading the same rows. `on_reach_start` re-arms from that growth exactly like `on_reach_end` re-arms from an append.\n- **First build \u002F resize converge automatically**: `UiApp` resolves the window against retained scroll state, and re-derives once against the fresh geometry when a build's window under-covers it (first build, window grew) or a measured correction is pending.\n- **Markup exclusion (documented call)**: the closed grammar has no channel for a `for` binding to receive the runtime's range request, nor a binding form for the extent-estimate fn, so the windowed list (uniform and variable) is builder-only; markup keeps bounded `\u003Clist virtualized>` (layout-culled) plus `on-reach-end` on `scroll` for honest infinite fetch.\n\nThe `examples\u002Ffeed` app is the reference: a 100,000-post deterministic MIXED-HEIGHT corpus (one-liners to long-form walls, estimate from body length), reach-end batching, per-post state by index, a zero-jump scroll-storm test, and snapshot telemetry (`widget_nodes=`) proving the window stays viewport-sized.\n\n## Style token attributes\n\nColor and radius come from the design tokens, referenced by token NAME — literals only, no bindings, no raw colors (dynamic styling stays in Zig via `ElementOptions.style`):\n\n- Color attributes: `background`, `foreground`, `accent`, `accent-foreground`, `border-color`, `focus-ring`. Values are `canvas.ColorTokens` field names — the complete list: `background`, `surface`, `surface_subtle`, `surface_pressed`, `text`, `text_muted`, `border`, `accent`, `accent_text`, `destructive`, `destructive_text`, `success`, `success_text`, `warning`, `warning_text`, `info`, `info_text`, `focus_ring`, `shadow`, `disabled`. `info` is the violet identity hue beside the status trio (merged PR badges, \"new\" chips). (`border-color`, not bare `border` — that name is reserved for a future width shorthand.)\n- `radius` — `canvas.RadiusTokens` field names: `sm`, `md`, `lg`, `xl`.\n\n```html\n\u003Crow background=\"surface\" radius=\"md\" padding=\"8\">\n  \u003Ctext foreground=\"text_muted\">Muted caption\u003C\u002Ftext>\n\u003C\u002Frow>\n```\n\nReferences resolve against the app's LIVE tokens on every rebuild (`finalizeWithTokens`), so a themed app (`tokens`\u002F`tokens_fn`) re-resolves them when the theme changes — dark mode flips `surface` automatically. The DEFAULT theme follows the system appearance: an app that sets neither `tokens` nor `tokens_fn` derives the stock tokens from the OS light\u002Fdark setting (plus high-contrast and reduce-motion) and re-themes live when the user flips it. Pass explicit `tokens` for a fixed look, or `tokens_fn` for model-owned theming (custom palettes usually still follow the system scheme through `on_appearance`). An explicit `style` value set in Zig always wins over a token ref on the same field. Unknown token names are validation\u002Fcompile errors.\n\nOne Zig-only style knob rides beside the tokens: `ElementOptions.style = .{ .quiet_hover = true }` silences a pressable surface's pointer HOVER wash only — press and selection fills, the focus ring, cursor intent, and hit testing stay — for image-forward content tiles (cover art, photo cards) where the pointer rests on content rather than a control register. Acting controls (list rows, menu items, buttons, tab triggers) keep their washes: there the hover fill IS the affordance.\n\n## Expressions — the complete grammar\n\nAttribute values take a literal or exactly ONE `{expression}`; text content interpolates any number (`{open_count} open · {done_count} done`). An expression is PURE and TOTAL — spreadsheet power, never a programming language: no user-defined functions, no effects, no computed message names, guaranteed termination.\n\n- Operands: binding paths (`{c.title}`), numbers (`3`, `0.5` — no exponents), `'strings'` (single quotes, no escapes), `true`\u002F`false`.\n- Arithmetic `+ - * \u002F`: numbers only (int op int stays int; any float promotes; `\u002F` ALWAYS produces a float — wrap in `round()`\u002F`floor()`\u002F`ceil()` for whole-number attributes). Division by zero, integer overflow, and non-finite float results are loud errors, never silent zeros\u002FNaN\u002Finf.\n- Comparison `== != \u003C \u003C= > >=`: ordering takes numbers only; equality compares any two values (different types are simply NOT equal; int\u002Ffloat compare numerically). Comparisons do not chain (`a \u003C b \u003C c` is an error — use `and`). Comparison operands reject arena-computed bindings (compare source fields, or bind a `pub fn ... bool`).\n- Boolean `and` \u002F `or` \u002F `not`: booleans only — write `{count > 0}`, not `{count}`. Both sides always evaluate (pure, so only errors observable).\n- `++` concatenation: joins ANY values as display text, formatted exactly like interpolation (`{'$' ++ fixed(price, 2)}`).\n- `msg` or `msg:{path}` on `on-*` attributes stays its own form: tags and payloads are paths, never expressions.\n\nThe function library is CLOSED (17 functions; adding one is a toolkit change — anything else is a model fn):\n\n| fn | notes |\n|---|---|\n| `fixed(x, digits)` | exact decimals, digits 0-6, half-away rounding (`fixed(3.14159, 2)` → `3.14`) |\n| `thousands(n)` | whole number with `,` separators (`1,234,567`) |\n| `percent(fraction, digits?)` | `percent(0.42)` → `42%`; digits default 0 |\n| `date(ts)` \u002F `time(ts)` \u002F `datetime(ts)` | unix SECONDS from the model, formatted UTC (`2026-07-05`, `14:03`); formatting model time is pure — `now()` is a teaching error: reading the clock is an effect, keep a timestamp field updated by update\u002Ffx |\n| `upper(s)` \u002F `lower(s)` \u002F `trim(s)` | ASCII case map \u002F whitespace trim; non-ASCII passes through unchanged |\n| `min(a, b)` \u002F `max(a, b)` \u002F `abs(x)` | numbers; int stays int |\n| `round(x)` \u002F `floor(x)` \u002F `ceil(x)` | number → whole number |\n| `plural(count, singular, plural)` | count exactly 1 picks singular (`{plural(n, 'item', 'items')}`) |\n| `pad(x, width)` | zero-pads the integer value of x to `width` digits (`pad(7, 2)` → `07`); a negative sign precedes the zeros and does not count toward width; numbers wider than width print in full — the mm:ss counter fn (`{pad(minutes, 2)}:{pad(seconds, 2)}`) |\n\nBounds (taught one past): 256 bytes, 64 terms, 16 nesting levels per expression. Where expressions are allowed: text interpolation, attribute values, `if` tests, template args at use sites. Path-only by design: message tags\u002Fpayloads, `for each` iterables, import paths. Both engines evaluate through ONE shared evaluator — results are bit-for-bit identical, floats included — and `native markup check` validates syntax, bounds, function names (with did-you-mean), arity, and literal types without needing the model.\n\nAnything stateful or beyond the grammar is a Zig model function you bind to (`each=\"visible\"`, `{summaryLine}`).\n\nWhere the line sits between inline arithmetic and a model fn: inline expression arithmetic is sanctioned for ONE-OFF presentation-level derivation — `{percent(done \u002F total)}` on the single readout that shows it is exactly what expressions are for. The moment a derivation is REUSED in a second binding, deserves a NAME, or carries meaning the model owns (a threshold, a rule, a policy), it belongs in a named model function: `{completionRate}` reads at the binding site, tests in Zig, and changes in one place.\n\n## Binding resolution rules\n\nA path like `{h.streak}` resolves left to right, starting from the model or a `for` variable:\n\n- everything bindable is declared INSIDE the Model struct: fields, and `pub fn` METHODS in the struct body. A file-scope `pub fn visibleRows(model: *const Model, ...)` written NEXT TO the struct is invisible to bindings and `for each` — a model-free `native markup check` still passes (grammar-only), and the view then fails at test\u002Frun time with \"each does not name an iterable\"; with a fresh model contract (refreshed by `native test`), `native check` catches it instantly with a did-you-mean. If a binding or `each` cannot find your fn, first check it lives inside `pub const Model = struct { ... }`\n- struct fields bind directly: `{habit_count}`, `{h.done}`\n- zero-arg pub methods bind like fields: `{totalDays}` calls `pub fn totalDays(m: *const Model) usize`\n- arena-taking scalar methods bind the same way: `{summary}` calls `pub fn summary(m: *const Model, arena: std.mem.Allocator) []const u8` — format derived display strings straight into the build arena (it lives exactly one view build). Works anywhere a scalar binding does — text interpolation, attribute values, message payloads, expression function arguments (`{upper(summary)}`) — EXCEPT as a comparison operand (`==`, `\u003C`, ...), which rejects arena-computed values with a teaching error: compare the source fields, or bind a `pub fn ... bool`\n- enums resolve to their tag name — so `{f}` renders \"active\", `{f == filter}` compares tags, and `set_filter:{f}` coerces the tag back into an enum payload\n- `for each=\"name\"` resolves, in order: a Model field that is a slice\u002Farray, a pub array\u002Fslice decl (`pub const filters = [_]Filter{...}`), a pub fn `(*const Model) []const T`, or a pub fn `(*const Model, std.mem.Allocator) []const T` — the allocator variant is how filtered\u002Fderived lists work (allocate from the passed arena)\n- item methods work too: `{h.name}` may be a field or `pub fn name(h: *const Habit) []const u8`\n\nBindings are zero-argument. A parameterized query (cards of column X) becomes one model function per case.\n\n## Derive, don't store\n\nThe model stores source-of-truth state ONLY: the raw items, the current filter, the draft text. Anything the view shows that is computable from those — counts, sums, filtered views, formatted strings — is a pub method the markup binds to, never a model field. A cached derivable must be re-maintained in every `update` arm and goes stale the moment one is missed; a derived method cannot.\n\n```zig\n\u002F\u002F WRONG: derived state cached in the model, maintained by hand in update()\nvisible_count: usize,\nsummary_storage: [64]u8,   \u002F\u002F preformatted display string\n\n\u002F\u002F RIGHT: the model keeps integers + the filter; methods derive per rebuild\npub fn visibleCount(model: *const Model) usize { ... }\npub fn visibleCents(model: *const Model) u64 { ... }\n```\n\nDerived numbers need no allocation: bind the methods and let text interpolation compose the line — this is exactly how the examples' status bars work (`examples\u002Fhabits`):\n\n```html\n\u003Cstatus-bar>{habit_count} habits · {totalDays} total days\u003C\u002Fstatus-bar>\n```\n\nComputed strings (money, dates, percentages) are formatted into the BUILD ARENA inside the `for each` allocator fn — derive display rows whose string fields are `allocPrint`ed there. The arena lives for exactly one view build, so nothing is stored and nothing goes stale. Store amounts as integer cents; format at view time:\n\n```zig\npub const VisibleExpense = struct { id: u32, date: []const u8, amount: []const u8 };\n\n\u002F\u002F A METHOD — declared inside `pub const Model = struct { ... }`. Bindings and\n\u002F\u002F `for each` resolve Model decls only; the same fn at file scope is invisible.\npub fn visible(model: *const Model, arena: std.mem.Allocator) []const VisibleExpense {\n    const out = arena.alloc(VisibleExpense, model.expense_count) catch return &.{};\n    var count: usize = 0;\n    for (model.expenses[0..model.expense_count]) |*e| {\n        if (!model.matches(e.*)) continue;\n        out[count] = .{\n            .id = e.id,\n            .date = e.date(),\n            .amount = std.fmt.allocPrint(arena, \"${d}.{d:0>2}\", .{ e.amount_cents \u002F 100, e.amount_cents % 100 }) catch \"\",\n        };\n        count += 1;\n    }\n    return out[0..count];\n}\n```\n\nA one-off formatted line that plain interpolation can't express (e.g. a currency total in a summary) is an arena-taking scalar fn bound directly:\n\n```zig\npub fn summary(model: *const Model, arena: std.mem.Allocator) []const u8 {\n    return std.fmt.allocPrint(arena, \"{d} expenses · {s} total\", .{\n        model.visibleCount(), formatCents(arena, model.visibleCents()),\n    }) catch \"\";\n}\n```\n\n```html\n\u003Cstatus-bar>{summary}\u003C\u002Fstatus-bar>\n```\n\n(The old workaround — wrapping the string in a one-element slice and iterating it with `\u003Cfor each=\"summary\" as=\"s\">` — is no longer needed; bind the fn directly. Item methods take the arena too: `{e.amount}` may call `pub fn amount(e: *const Expense, arena: std.mem.Allocator) []const u8`.)\n\nFor `\u003Cif test>`, prefer an explicit boolean predicate method over numeric truthiness: `test=\"{hasHabits}\"` with `pub fn hasHabits(m: *const Model) bool` states the condition; `test=\"{habit_count}\"` works (non-zero is truthy, non-empty strings too) but hides it.\n\n## Messages\n\n`on-press`, `on-toggle`, `on-change`, `on-submit` (enter in a text field; primary+enter in a textarea, where enter inserts a newline), `on-dismiss` (dismissible surfaces: dialog, drawer, sheet, dropdown-menu — dispatched when Escape or a click outside dismisses the surface, so the model owns the close), `on-hold` (press-and-hold, see the Pickers section), and `on-hover-enter`\u002F`on-hover-leave` (the pointer-hover containment pair, below) take `tag` or `tag:{payload}`. The tag must be a variant of your `Msg` union; payload bindings coerce to the variant's payload type: integers, floats, enums (from tag names), `[]const u8`, bool. `on-input` is special: name a `Msg` variant whose payload is `canvas.TextInputEvent` and the runtime delivers each text edit in it. `on-scroll` (the `scroll` element only) is the same shape: name a `Msg` variant whose payload is `canvas.ScrollState` and the runtime delivers the post-scroll state — `offset`, `viewport_extent`, `content_extent`, `maxOffset()` — after every user scroll (wheel, kinetic momentum steps, keyboard, accessibility). In Zig views the constructors are `Ui.inputMsg(.tag)` \u002F `Ui.scrollMsg(.tag)` on `on_input` \u002F `on_scroll`. `on-reach-end` (the `scroll` element only; `on_reach_end` in Zig views, any scroll container including the windowed virtual list) is a plain Msg dispatched when a user scroll comes within one viewport of the content end — the infinite-fetch signal, fired once per approach with hysteresis (re-arms past 1.5 viewports, which appending a batch causes by growing the extent). A programmatic jump to the end fires once and NEVER re-arms while the offset stays near the end — re-arming needs a post-scroll observation at least 1.5 viewports from it.\n\nScroll offsets follow the same mirror discipline as text: the Msg carries the offset the runtime ALREADY applied, so store it in the model and echo it back through the scroll's `value` — the echoed source value equals the runtime offset, which the scroll reconcile rule treats as \"unchanged\", so rebuilds never stomp live scrolling. `on-scroll` is how long content pages or lazy-loads: keep a bounded window in the model and slide it from `offset` (near-end when `offset + viewport_extent` approaches `content_extent`).\n\n`on-hover-enter` and `on-hover-leave` (any element; `ElementOptions.on_hover_enter` \u002F `on_hover_leave` in Zig views) are the pointer-hover containment pair — Elm's onMouseEnter\u002FonMouseLeave: enter dispatches once when the pointer enters the element's hit region, leave once when it exits — discrete edges, never per-move, so hover previews, prefetch, and hover cards are ordinary Msgs. Binding either makes the element hover-hittable the way a bound press makes it pressable — but never pressable: clicks fall through, no accessibility action, and NO hover wash (the wash is the visual channel of acting controls; a `quiet_hover` content tile that binds hover stays visually quiet while the model hears it). Nested bound elements track containment independently (entering a bound row inside a bound card never leaves the card); enters fire outermost-first, leaves innermost-first. Every enter is answered by exactly one eventual leave — the leave Msg is captured while the element stands (kept fresh across rebuilds, retained if unbound), so it still arrives when the exit is the element unmounting. Exits resolve exactly like the hover wash: moving off and the pointer leaving the window are direct edges; content scrolling or reflowing out from under a stationary pointer re-hit-tests the last pointer position; a dismissal removing the surface under the pointer delivers that surface's leaves immediately, and whatever it reveals is entered when the model's close rebuild re-hit-tests (pair dismissible surfaces with `on-dismiss`, as always); overlays occlude hover the way they occlude clicks. Mouse\u002Ftrackpad only — containment advances on hover-phase motion, a pointer floating without contact that touch physically cannot produce, so touch never synthesizes hover and hover-revealed affordances need a second path.\n\nA handler or update error DEGRADES, it does not exit the app: dispatch catches it, records it in a bounded ring (`runtime.dispatchErrors()`, the `error event=... name=...` lines and `dispatch_errors=` count in automation snapshots, and a `dispatch.error` trace record at error level), and the app keeps running. Trace-sink capacity failures likewise never fail dispatch — dropped records are counted (`dropped_trace_records=`), not fatal. Design for it: an arm that can fail should still surface its own status in the model; the error ring is the safety net, not the UX.\n\nPresses follow ONE rule: a click lands on the nearest pressable widget under the pointer — plain text, icons, images, badges, and layout containers let it fall through to their closest pressable ancestor, and dragging still selects text. \"Pressable\" means an interactive kind (button, checkbox, list-item, ...) or ANY element with a bound `on-press`\u002F`on-toggle` — the handler itself makes the element a hit target, so a pressable row is just `\u003Cpanel on-press=\"open:{id}\">` (or `\u003Crow on-press=...>`, `ui.row(.{ .on_press = ... })`) with plain text children: no empty-text overlays, no duplicating the handler onto every text leaf. Nested pressables resolve to the deepest one (a button inside a pressable row wins over the row); editable text fields, scroll containers, and modal surfaces (dialog, drawer, sheet, popover, menus) always claim their own presses, so a click in a field inside a pressable row places the caret instead of activating the row. The value\u002Ftext handlers (`on-change`, `on-submit`, `on-input`) still only belong on controls — both engines and `markup check` reject them on layout\u002Fdecoration elements with a teaching error.\n\n### Keys: quiet list rows and the app-level fallback\n\nKeyboard focus has two registers. RING focus is the keyboard contract: Tab\u002FShift+Tab walk the focusables and draw the visible ring, and a ring-focused widget owns its keys in full — activation, group arrows, Home\u002FEnd. QUIET focus is bookkeeping: a pointer press records which widget the user last touched, with no ring drawn (editable text kinds are the exception — a caret is a visible promise, so they show it however focus arrives). A key event resolves top to bottom, the focused widget always outranking the app: (1) the focused widget's bound handler; (2) structural consume — any key on an editable text kind (typing stays typing, checked by widget KIND, so a focused search field blocks app shortcuts without knowing they exist) and any key the widget's kind maps as a control intent; (3) only an unclaimed key_down reaches `Options.on_key`, the app-level fallback (a target-less event — nothing focused — skips straight there). The fallback is a plain function from the key event to an optional Msg:\n\n```zig\n\u002F\u002F options: .on_key = onKey,\npub fn onKey(keyboard: canvas.WidgetKeyboardEvent) ?Msg {\n    if (keyboard.modifiers.hasNavigationModifier() or keyboard.modifiers.shift) return null;\n    if (std.ascii.eqlIgnoreCase(keyboard.key, \"space\")) return .toggle_play;\n    if (std.ascii.eqlIgnoreCase(keyboard.key, \"arrowdown\")) return .select_next;\n    if (std.ascii.eqlIgnoreCase(keyboard.key, \"arrowup\")) return .select_previous;\n    return null;\n}\n```\n\nOne rule bridges the registers: quiet focus on a plain `list-item` routes NO keys — arrows, Enter, and Space on a row the user merely clicked all fall through to the fallback exactly as if nothing were focused. The why is selection-follows-intent apps: in a music library the selection is MODEL state — a click selects a row, and the arrows should keep moving that app-owned selection through the fallback (`select_next`\u002F`select_previous` above), not silently start walking engine focus from whichever row was clicked last. Tab restores the full row contract (arrows walk the rows, Space selects, Enter submits — see \"Select, then act\"), rows carrying `role=\"treeitem\"` keep the tree keymap under either register, and every other widget kind keeps its keys under quiet focus (a quietly focused button still presses on Space). The fallback fn is directly unit-testable (feed it key events, assert the Msgs), and `examples\u002Fsoundboard` pins the whole pattern: Space toggles the transport from anywhere, a tabbed-to row takes Space for select and Enter for play, and the search field takes every key as typing.\n\n## Text fields: the elm-style mirror pattern\n\nThe model applies every edit event and is the source of truth; the runtime keeps caret\u002Fselection while your source text matches, and a source-side change (like clearing on submit) wins:\n\n```html\n\u003Ctext-field text=\"{draft}\" placeholder=\"New task…\" on-input=\"draft_edit\" on-submit=\"add\" grow=\"1\" \u002F>\n```\n\n```zig\ndraft_buffer: canvas.TextBuffer(64) = .{},            \u002F\u002F model field: text + selection + composition\npub fn draft(model: *const Model) []const u8 {        \u002F\u002F the fn the markup's text= binds\n    return model.draft_buffer.text();\n}\n.draft_edit => |edit| model.draft_buffer.apply(edit), \u002F\u002F mirror every edit\n.add => { model.addTask(model.draft_buffer.text()); model.draft_buffer.clear(); },  \u002F\u002F clearing the source clears the field\n```\n\nThe markup binds the FN (`text=\"{draft}\"`), never the buffer: binding a `TextBuffer` model FIELD directly is a teaching error pointing at this pattern — the buffer holds editor state, and the binding wants the text it derives. See `examples\u002Fui-inbox` for the complete pattern.\n\n### Clipboard and selection (free — do not reimplement)\n\nThe runtime owns cmd\u002Fctrl+C\u002FX\u002FV in editable text: copy writes the current selection to the system clipboard, cut copies then delivers the removal to your `on-input` handler as an `insert_text \"\"` edit, and paste arrives as an ordinary `insert_text` edit — the TEA mirror above stays consistent with zero extra code. Paste is clamped to the view's text capacity: when bytes were dropped, the keyboard event carries `edit_truncated = true` and your `TextBuffer` mirror sets its own `truncated` flag (check it if lost paste bytes matter to your UX; `TextBuffer` clamps oversized insertions at a UTF-8 boundary rather than dropping the edit). Shift+arrows\u002Fhome\u002Fend extend the selection from the keyboard.\n\nStatic text is selectable too: click-drag inside one `text` leaf or `paragraph` (markdown bodies included) selects with a highlight, cmd\u002Fctrl+C copies it, and pressing anywhere else clears it. Selection and pressing coexist inside pressable rows — dragging selects (and presses nothing), a plain click collapses the selection and lands on the row's `on-press`. Selection is per-widget by design — there is no document model ordering text across widgets, so a drag cannot span two paragraphs (copy per paragraph). The selection survives rebuilds while that widget's text bytes are unchanged, and shows up in semantics\u002Fautomation snapshots as `selection=a..b` on the widget line. Clipboard access from `update` is `fx.writeClipboard` \u002F `fx.readClipboard` on the effects channel (see Effects) — never a `pbcopy` spawn; `runtime.readClipboard(&buffer)` \u002F `runtime.writeClipboard(text)` remain for code that holds the runtime.\n\n## Effects: subprocesses and HTTP from update\n\n`update` can take a third parameter — the effects channel — by declaring `.update_fx` instead of `.update` (existing two-argument apps are untouched; set exactly one):\n\n```zig\nconst App = native_sdk.UiApp(Model, Msg);\nconst Effects = App.Effects;\n\npub fn update(model: *Model, msg: Msg, fx: *Effects) void { ... }\n\u002F\u002F options: .update_fx = update,\n```\n\nBoot-time effects — fetching the data the app opens with — go in `.init_fx`, TEA's init command. It runs exactly once, on the installing frame, before the first view build, so a loading flag set there is in the very first paint; results arrive as ordinary Msgs. This is THE way to boot-fetch — never a guarded `on_frame` (`on_frame` is the per-frame hook for renderer diagnostics and presented-frame reactions, and unguarded spawning from it refires forever):\n\n```zig\nfn boot(model: *Model, fx: *Effects) void {\n    model.loading = true;\n    fx.spawn(.{\n        .key = issues_key,\n        .argv = &.{ \"gh\", \"issue\", \"list\", \"--json\", \"number,title\" },\n        .output = .collect,                        \u002F\u002F whole JSON on the exit Msg\n        .on_exit = Effects.exitMsg(.issues_loaded),\n    });\n}\n\u002F\u002F options: .init_fx = boot,   (works with either update form)\n```\n\n`fx.spawn` runs a subprocess on a runtime-owned worker thread and streams each stdout line back as a typed Msg; the exit arrives as one more Msg. Keys are caller-chosen `u64`s you keep in the model — no handles:\n\n```zig\npub const Msg = union(enum) {\n    start,\n    cancel,\n    line: native_sdk.EffectLine,     \u002F\u002F payload types are fixed\n    exited: native_sdk.EffectExit,\n};\n\n.start => fx.spawn(.{\n    .key = stream_key,                          \u002F\u002F model-stored identity\n    .argv = &.{ \"gh\", \"issue\", \"list\" },\n    .stdin = null,                              \u002F\u002F optional, written once\n    .on_line = Effects.lineMsg(.line),          \u002F\u002F comptime constructors,\n    .on_exit = Effects.exitMsg(.exited),        \u002F\u002F like ui.inputMsg(.tag)\n}),\n.cancel => fx.cancel(stream_key),\n.line => |line| model.recordLine(line),         \u002F\u002F COPY line.line — it is\n                                                \u002F\u002F drain scratch, dead after\n                                                \u002F\u002F this update call\n.exited => |exit| model.finish(exit),           \u002F\u002F exit.reason, exit.code\n```\n\nRules that keep this honest:\n\n- Effects are update-side ONLY. The view never spawns anything — a button dispatches a Msg, and that Msg's update arm spawns. Markup stays declarative.\n- One `on_exit` Msg per spawn, always. A spawn that cannot run (all `max_effects = 16` slots busy, duplicate active key, argv over capacity) still delivers it, with reason `.rejected`. Reasons: `exited` (code is real), `signaled`, `cancelled`, `rejected`, `spawn_failed`.\n- After `fx.cancel(key)` returns, no further `on_line` Msgs for that spawn arrive; exactly one `.cancelled` exit follows. The process is killed and reaped — no zombies. Streaming a chat agent's stdout for minutes and cancelling mid-stream is the designed-for case.\n- Overflow is never silent: a full completion queue drops lines but the next delivered line's `dropped_before` and the exit's `dropped_lines` carry the count; over-long lines arrive truncated with `truncated = true`. Capacities: 16 in-flight effects, 4 KiB per line by default, 64 queued completions.\n- Agent CLIs emit whole events as single NDJSON lines far beyond 4 KiB (`claude -p --output-format stream-json` repeats the entire answer on one line). Raise the bound per spawn with `.max_line_bytes = 64 * 1024` — anything up to `max_effect_line_bytes_ceiling` (256 KiB); requests above the ceiling (or zero) are rejected through `on_exit`, never silently clamped. Lines beyond the granted bound still arrive truncated and flagged. The ceiling has envelope headroom: a stream that WRAPS another stream's lines (sandbox exec NDJSON envelopes carrying JSON-escaped agent events) can carry a full 64 KiB inner line with escaping overhead to spare — size the outer bound at roughly 2-3x the inner one.\n- JSON-over-stdout (`gh --json`, `jq -c`, `curl`) emits one giant line the 4 KiB line cap would destroy. Spawn with `.output = .collect` instead of the default `.lines`: whole stdout (up to 512 KiB) arrives ONCE on the exit Msg as `exit.output`, plus the child's stderr tail (last 4 KiB) in `exit.stderr_tail` — check it when `exit.code != 0` (auth errors, usage messages). No `on_line` Msgs fire for a collect spawn; overflow arrives cut with `output_truncated`\u002F`stderr_truncated` set, never silently. COPY `exit.output`\u002F`exit.stderr_tail` in update — drain scratch like `line.line`; the scalar exit fields stay plain data, safe to store. (`.lines` mode still ignores stderr entirely; use `.collect`, or an sh `2>&1` re-route if you truly need interleaved streaming.)\n\n`fx.fetch` runs one HTTP(S) request on a worker thread and delivers its terminal outcome — response, classified failure, timeout, or cancel — as exactly ONE Msg:\n\n```zig\npub const Msg = union(enum) {\n    load,\n    stop,\n    fetched: native_sdk.EffectResponse,   \u002F\u002F the fixed payload type\n};\n\n.load => fx.fetch(.{\n    .key = search_key,                     \u002F\u002F same key space + 16 slots as spawns\n    .method = .POST,                       \u002F\u002F std.http.Method; default .GET\n    .url = \"https:\u002F\u002Fapi.example.com\u002Frun\",  \u002F\u002F http:\u002F\u002F or https:\u002F\u002F only\n    .headers = &.{.{ .name = \"authorization\", .value = \"Bearer abc\" }},\n    .body = \"{\\\"q\\\":\\\"zig\\\"}\",             \u002F\u002F optional request payload\n    .timeout_ms = 10_000,                  \u002F\u002F whole exchange; default 30 s\n    .on_response = Effects.responseMsg(.fetched),\n}),\n.stop => fx.cancel(search_key),\n.fetched => |response| model.record(response),  \u002F\u002F COPY response.body — drain\n                                                \u002F\u002F scratch, dead after this call\n```\n\nFetch rules:\n\n- Exactly one `on_response` Msg per fetch, always terminal. `response.outcome` says what happened: `.ok` (real HTTP status in `.status` — non-2xx included; an HTTP-level error is still a delivered response), `.rejected` (never started: slots busy, duplicate active key, malformed URL or non-http(s) scheme, over-capacity URL\u002Fheaders\u002Fpayload), `.connect_failed` (DNS or TCP), `.tls_failed`, `.protocol_failed` (mid-exchange), `.timed_out`, `.cancelled`.\n- `response.body` is binary-safe bytes (zeros and high bits round-trip). Bodies over 256 KiB arrive cut at that bound with `truncated = true` — never silently. Capacities: 2 KiB URLs, 8 extra headers (1 KiB of names+values total), 64 KiB request payloads.\n- `fx.cancel(key)` keeps the spawn promise: exactly one `.cancelled` response Msg, nothing for that fetch after it.\n\nStreaming responses (`.response = .stream`) frame the body into `on_line` Msgs as lines arrive — the spawn `.lines` contract over HTTP. This is THE mode for NDJSON\u002FSSE endpoints that hold the connection open for a command's whole lifetime (Vercel Sandbox `POST ...\u002Fcmd` with wait+logs, agent event streams):\n\n```zig\n.run => fx.fetch(.{\n    .key = exec_key,\n    .method = .POST,\n    .url = sandbox_cmd_url,\n    .headers = &.{.{ .name = \"authorization\", .value = token }},\n    .body = cmd_json,\n    .timeout_ms = 600_000,                 \u002F\u002F covers the STREAM's whole lifetime\n    .response = .stream,                   \u002F\u002F body arrives as on_line Msgs\n    .max_line_bytes = 256 * 1024,          \u002F\u002F envelope lines WRAP agent events\n                                           \u002F\u002F (JSON-escaped): size the outer\n                                           \u002F\u002F bound 2-3x the inner one\n    .on_line = Effects.lineMsg(.exec_event),\n    .on_response = Effects.responseMsg(.exec_done),\n}),\n.exec_event => |line| model.recordEvent(line),  \u002F\u002F COPY line.line — drain scratch\n.exec_done => |response| model.finish(response), \u002F\u002F status set, body always empty\n```\n\nStream rules: each body line is one `on_line` Msg (same payload type and copy rule as spawn lines; `max_line_bytes` mirrors the spawn override with the same 256 KiB ceiling); the terminal `on_response` Msg carries the real HTTP status with an empty body; `fx.cancel(key)` mid-stream stops the lines and delivers exactly one `.cancelled` terminal; the whole-exchange `timeout_ms` covers the stream's full lifetime, so raise it for long-running commands; lines dropped on a full queue that no later line reported ride the terminal's `response.dropped_before`. In the fake executor, `feedLine` feeds a stream fetch's lines and `feedResponse(key, status, \"\")` delivers its terminal.\n\n`fx.writeFile` \u002F `fx.readFile` are TEA-friendly file persistence — session snapshots, app state — without smuggling an `Io` handle from `main` into `update`. Same discipline as spawn and fetch: bounded, key-based (shared key space and 16 slots), exactly one terminal Msg with an explicit outcome:\n\n```zig\npub const Msg = union(enum) {\n    save,\n    boot,\n    saved: native_sdk.EffectFileResult,   \u002F\u002F the fixed payload type\n    loaded: native_sdk.EffectFileResult,\n};\n\n.save => fx.writeFile(.{\n    .key = save_key,\n    .path = model.sessionPath(),           \u002F\u002F ≤ 1 KiB; parent dirs are created\n    .bytes = model.snapshotJson(),         \u002F\u002F ≤ 1 MiB, copied at call time\n    .on_result = Effects.fileMsg(.saved),\n}),\n.boot => fx.readFile(.{\n    .key = load_key,\n    .path = model.sessionPath(),\n    .on_result = Effects.fileMsg(.loaded),\n}),\n.saved => |result| model.noteSaved(result.outcome),\n.loaded => |result| model.restore(result),  \u002F\u002F COPY result.bytes — drain scratch\n```\n\nFile rules:\n\n- `result.outcome` is explicit: `.ok` (a read's whole content in `result.bytes`; a write fully on disk), `.not_found` (reads only — writes create the path, parent directories included), `.io_failed` (permissions, path is a directory, disk), `.truncated` (the file exceeds the 1 MiB `max_effect_file_bytes`; `result.bytes` is the first bound bytes — its own outcome, not a flag, because a cut JSON snapshot must not parse as whole), `.rejected` (never ran: slots busy, duplicate key, empty\u002Fover-long path, write bytes over the bound — an over-bound WRITE is rejected outright since a partial write would corrupt the file), `.cancelled`.\n- Writes replace the file whole; `writeFile` bytes are copied at call time so the caller's buffer is immediately reusable. Reads deliver drain-scratch bytes — copy what the model keeps.\n- In the fake executor: `pendingFileAt(0)` records `key`\u002F`op`\u002F`path`\u002F`bytes` for assertions; `feedFileResult(key, .ok, \"{...}\")` answers a read (over-bound content is cut and rewritten to `.truncated`, mirroring the real reader), `feedFileResult(key, .ok, \"\")` acknowledges a write; failure outcomes pass through as fed.\n\n`fx.writeClipboard` \u002F `fx.readClipboard` put text on (and read it from) the system clipboard through the platform pasteboard — the same seam the runtime's cmd+C copy uses. Never spawn `pbcopy`\u002F`pbpaste`\u002F`xclip` for this. Same discipline: key-based (shared key space and 16 slots), exactly one terminal Msg with an explicit outcome. The pasteboard call is synchronous on the loop thread (no worker), but the result still arrives as an ordinary Msg on the next drain:\n\n```zig\npub const Msg = union(enum) {\n    share,\n    copied: native_sdk.EffectClipboardResult,   \u002F\u002F the fixed payload type\n};\n\n.share => fx.writeClipboard(.{\n    .key = share_key,\n    .text = model.shareLine(),             \u002F\u002F ≤ 64 KiB, copied at call time\n    .on_result = Effects.clipboardMsg(.copied),\n}),\n.copied => |result| model.noteCopied(result.outcome),\n```\n\nClipboard rules:\n\n- `result.outcome` is explicit: `.ok` (a write is on the clipboard whole; a read's content is in `result.text` — drain scratch, copy what the model keeps), `.failed` (the platform refused: no clipboard service on the host, read content over the 64 KiB `max_effect_clipboard_bytes`, pasteboard error — a read never arrives cut), `.rejected` (never ran: slots busy, duplicate key, write text over the bound), `.cancelled`.\n- Writes are text\u002Fplain and replace the clipboard whole; rich-data clipboard stays on the runtime API (`runtime.writeClipboardData`).\n- In the fake executor: `pendingClipboardAt(0)` records `key`\u002F`op`\u002F`text` for assertions; `feedClipboardResult(key, .ok, \"pasted\")` answers a read, `feedClipboardResult(key, .ok, \"\")` acknowledges a write; failure outcomes pass through as fed. Under the real executor the test harness's null platform records the write — assert `harness.null_platform.lastClipboardData()`.\n\n`fx.startTimer` \u002F `fx.cancelTimer` are key-based timers on the same channel — an auto-refresh, a poll, a debounce — one-shot or repeating, each fire delivered as one `on_fire` Msg. Timers are their own fixed table (16, `max_effect_timers`) and their own key namespace: they consume none of the 16 effect slots and never collide with spawn\u002Ffetch\u002Ffile keys:\n\n```zig\npub const Msg = union(enum) {\n    tick: native_sdk.EffectTimer,    \u002F\u002F the fixed payload type\n    ...\n};\n\nfx.startTimer(.{\n    .key = refresh_key,\n    .interval_ms = 30_000,\n    .mode = .repeating,               \u002F\u002F .one_shot (default) fires once, then retires\n    .on_fire = Effects.timerMsg(.tick),\n}),\n.tick => |timer| switch (timer.outcome) {\n    .fired => model.refresh(),        \u002F\u002F timer.timestamp_ns is the platform fire time\n    .rejected => model.noteTimerRejected(timer.key),\n},\n```\n\nTimer rules: starting a key that is already an active timer REPLACES it (interval\u002Fmode\u002F`on_fire` update in place — the friendly behavior for an auto-refresh whose cadence changes); `fx.cancelTimer(key)` stops it, unknown keys are a no-op; rejection is never silent — a full timer table, a zero `interval_ms`, or a platform without a timer service delivers exactly one Msg with outcome `.rejected`. In the fake executor, `pendingTimerAt(0)` records `key`\u002F`interval_ms`\u002F`mode` and `fireTimer(key)` fires by hand (one-shot slots retire after the fire), draining through the same `.wake` path as `feedExit`. The mode enum is public as `native_sdk.TimerMode` (`.one_shot`\u002F`.repeating`) — asserting a recorded request's mode must qualify it (`try testing.expectEqual(native_sdk.TimerMode.repeating, request.mode)`; a bare `.repeating` as `expectEqual`'s first argument does not infer).\n\n`fx.playAudio` plays a track through the platform's single audio player (macOS: AVAudioPlayer for local files, AVPlayer for streamed URLs, both in the AppKit host), and the transport rides the same channel: `fx.pauseAudio()` \u002F `fx.resumeAudio()` \u002F `fx.stopAudio()` \u002F `fx.seekAudio(position_ms)` \u002F `fx.setAudioVolume(0.0—1.0)`. Sources resolve in a fixed order — the local `path` first; when it is absent or missing, the `url`, where a verified entry at `cache_path` plays locally (no network) and anything else STREAMS progressively (audible before the download finishes) while the bytes fill the cache for next time (`expected_bytes`, the track's known size from a manifest, is the integrity gate: partial or stale cache entries never play). One player is the whole surface — a new `playAudio` replaces whatever played before, exactly like a music app switching tracks. The audio key is its own namespace (like timer keys) and consumes none of the 16 effect slots; every report arrives as one `on_event` Msg:\n\n```zig\npub const Msg = union(enum) {\n    audio_event: native_sdk.EffectAudio,   \u002F\u002F the fixed payload type\n    ...\n};\n\n.play_track => |track| fx.playAudio(.{\n    .key = track.id,                        \u002F\u002F echoed in every event\n    .path = track.path,                     \u002F\u002F local file, tried first\n    .url = track.url,                       \u002F\u002F optional http(s) fallback: cache hit or stream\n    .cache_path = track.cache_path,         \u002F\u002F empty disables caching (stream-only)\n    .expected_bytes = track.bytes,          \u002F\u002F cache integrity gate (0 = unknown)\n    .on_event = Effects.audioMsg(.audio_event),\n}),\n.audio_event => |event| switch (event.kind) {\n    .loaded => model.duration_ms = event.duration_ms,   \u002F\u002F the real decoded duration\n    .position => model.elapsed_ms = event.position_ms,  \u002F\u002F ~every 500ms while playing\n    .completed => model.playNext(fx),                   \u002F\u002F exactly once, at natural end\n    .failed, .rejected => model.noteAudioUnavailable(), \u002F\u002F never a crash, never silence\n},\n```\n\nAudio rules: `event.kind` is explicit — `.loaded` acknowledges a successful load (position ticks and `.completed` follow only after it), `.position` is a coarse honest readout (~500ms cadence — a readout, not a frame clock; drive animations from your model, not from tick density), `.completed` fires exactly once with position pinned to the duration, `.failed` reports an unreadable file with no url fallback, an async decode error, a network failure mid-stream (or offline with a cold cache), or a platform without audio playback (GTK\u002FWin32 today — named unsupported, not half-implemented), `.rejected` reports loop-side validation (path and url both empty, or any string over `max_effect_audio_path_bytes`). `event.buffering` is the stream's honest stall flag — true while an un-paused stream waits for network bytes (local files and cache hits never buffer); show it as its own UI state, distinct from playing and paused. All payload fields are plain data — safe to store in the model, no drain-scratch copying. Pause\u002Fstop\u002Fseek\u002Fvolume never echo events (the caller commanded them); seek and volume work mid-stream; volume is remembered across tracks. The streamed bytes cache at `cache_path` — key it by URL hash with `native_sdk.audioCachePath(&buffer, cache_dir, url)` under the app_dirs `.cache` directory (macOS `~\u002FLibrary\u002FCaches\u002F\u003Capp>\u002Faudio\u002F`, Linux `$XDG_CACHE_HOME\u002F\u003Capp>\u002Faudio\u002F`), so clearing the cache is deleting that one directory. The automation snapshot reports playback honestly (`audio key=... state=playing|paused|buffering source=local|cache|stream position_ms=... duration_ms=...`) — an advancing `position_ms` is the automation-visible evidence music is actually playing, and `source=` proves the resolution order. In the fake executor, `pendingAudio()` records the single channel's `key`\u002F`path`\u002F`url`\u002F`cache_path`\u002F`expected_bytes`\u002F`playing`\u002F`volume`, `feedAudioEvent(.position, 1_500, 89_160, true)` feeds any event by hand (draining through the same `.wake` path; `feedAudioEventBuffering` adds the stall flag), and `audioSnapshot()` exposes the mirrors; under the real executor the test harness's null platform is a deterministic fake player — `setAudioDuration(suffix, ms)` seeds durations, `takeAudioLoaded()` \u002F `advanceAudio(delta_ms)` \u002F `stallAudio()` synthesize the platform events a live host would deliver, position never advances on its own, `audio_local_files = false` models the assets-absent machine (local loads answer `AudioSourceNotFound`, which is what sends the cascade to the url), and a streamed track that runs to completion flips into the fake cache so the next play of the same url resolves `.cache`.\n\n`fx.openChannel` is the external-source seam — the one sanctioned way an app-owned thread (a socket reader, a file watcher, a sampling worker) feeds the UI. Reach for it when the data originates OUTSIDE update and arrives on its own schedule: open a channel under a caller-chosen `u64` key, hand the returned thread-safe `ChannelHandle` to the source thread, and every accepted `handle.post(bytes)` arrives as one `on_event` Msg — no timer polling anywhere, the post itself wakes the loop. Channels are their own fixed table (8, `max_effect_channels`) and consume none of the 16 effect slots, but the key shares the effect families' one key space (a same-key fetch is blocked while the channel lives):\n\n```zig\npub const Msg = union(enum) {\n    sample: native_sdk.EffectChannelEvent,   \u002F\u002F the fixed payload type\n    ...\n};\n\n.start => {\n    const handle = fx.openChannel(.{\n        .key = monitor_key,                   \u002F\u002F echoed in every event\n        .on_event = Effects.channelMsg(.sample),\n        .max_pending = 8,                     \u002F\u002F staging bound, clamped 1..32 (default 32)\n    });\n    \u002F\u002F Launch the source only for a LIVE handle — under session replay\n    \u002F\u002F the open parks and handle.live() answers false, so the producer\n    \u002F\u002F never starts (the journaled events are the whole stream either\n    \u002F\u002F way). Posting from the source thread is thread-safe and never\n    \u002F\u002F blocks (given the platform's conforming enqueue-only wake hook —\n    \u002F\u002F every first-party host; see PlatformServices.wake_fn):\n    \u002F\u002F handle.post(bytes) answers .accepted, .dropped_full,\n    \u002F\u002F .dropped_oversized, or .closed.\n    if (handle.live()) startSampler(handle);\n},\n.stop => fx.closeChannel(monitor_key),\n.sample => |event| switch (event.kind) {\n    .data => model.recordSample(event.bytes),                \u002F\u002F drain scratch — copy what the model keeps\n    .closed => model.noteMonitorStopped(event.dropped_total), \u002F\u002F exactly once, final totals aboard\n    .rejected => model.noteMonitorUnavailable(),              \u002F\u002F duplicate live key or no idle slot\n},\n```\n\nChannel rules: `handle.post` answers a four-way `PostResult`, and the producer contract reads directly off it — `.accepted` (exactly one `.data` event delivers on the next drain), `.dropped_full` (transient back-pressure: skip this message and keep producing; the consumer's next drain relieves it), `.dropped_oversized` (a programming error no retry fixes: bound your bytes at `max_effect_channel_bytes`, 4 KiB), `.closed` (the occupancy is over: exit the producer loop for good — the handle is generation-stamped, so posting after close, after the slot was reused, or after runtime teardown is always memory-safe and always `.closed`). The back-pressure contract in one line: refused posts are never silent — they count into the event's `dropped_pending`\u002F`dropped_total` counters and ride the next delivered event, and a refused post never wakes the host, so a producer storming a full stage cannot grow the loop's queue. `post` never blocks its producer given a conforming host wake (the platform's `wake_fn` is contractually a bounded, enqueue-only nudge — every first-party host conforms; see `PlatformServices.wake_fn`), and the runtime holds no channel lock across the wake, so even a violating embedder hook hangs only its own posting thread. The replay story, honestly: channel events journal at the effect boundary, so a recorded session replays the whole stream from the journal and never NEEDS the source — under replay `openChannel` parks the occupancy and returns an inert handle whose every post answers `.closed`. But the opening update re-executes (app code is app code), so a producer launched unconditionally really starts — connects and blocking setup before its first post included — and is only stopped AT that first post; `handle.live()` is the producer-launch check (false for parked replay handles, refused opens, and closed occupancies — advisory only, the post's own answer stays authoritative), so a producer gated on it never starts and replay stays fully offline. `fx.closeChannel(key)` flushes the staged backlog, delivers the one `.closed` terminal, and frees the key at that delivery; `fx.channelHandle(key)` re-resolves the open channel's handle loop-side. See `examples\u002Fchannel-monitor` for the complete worker-loop pattern, including the wind-down discipline.\n\nTest effects with the fake executor — deterministic, no processes, no network:\n\n```zig\napp_state.effects.executor = .fake;             \u002F\u002F before dispatching (and before\n                                                \u002F\u002F the first frame if using init_fx —\n                                                \u002F\u002F the boot spawn is then recorded too)\ntry app_state.dispatch(&harness.runtime, 1, .start);\nconst request = app_state.effects.pendingSpawnAt(0).?;   \u002F\u002F assert key\u002Fargv\u002Foutput mode\ntry app_state.effects.feedLine(stream_key, \"stream line 1\");\ntry app_state.effects.feedExit(stream_key, 0);\ntry harness.runtime.dispatchPlatformEvent(app, .wake);   \u002F\u002F drain -> update\n\n\u002F\u002F .collect spawns: feedLine accumulates (bytes + newline, like a real child\n\u002F\u002F printing that line), feedStderr fills the tail, feedExit delivers both.\ntry app_state.effects.feedLine(issues_key, \"{\\\"number\\\":1}\");   \u002F\u002F no on_line Msg\ntry app_state.effects.feedStderr(issues_key, \"warning: slow\\n\");\ntry app_state.effects.feedExit(issues_key, 0);                  \u002F\u002F exit.output + exit.stderr_tail\n\nconst fetch_req = app_state.effects.pendingFetchAt(0).?; \u002F\u002F assert key\u002Fmethod\u002Furl\u002Fheaders\u002Fbody\ntry app_state.effects.feedResponse(search_key, 200, \"{\\\"ok\\\":true}\");\ntry harness.runtime.dispatchPlatformEvent(app, .wake);   \u002F\u002F Msg{ .fetched = ... }\n```\n\nThe `.wake` platform event is how live platforms marshal worker completions onto the loop thread (macOS main-queue dispatch, GTK `g_idle_add`, Win32 `PostMessage`); dispatching it in tests exercises the same drain path. Note that after `fx.cancel(key)` runs in `update`, a subsequent `feedExit(key)` correctly fails with `error.EffectNotFound` — the cancel already delivered the terminal `.cancelled` exit, so there is no active effect left to feed. See `examples\u002Feffects-probe` for the complete pattern, including the live cancel flow.\n\n> **Effects test surface — exact signatures.** These are the complete fake-executor entry points; do not excavate the SDK source. Harness and switch: `const harness = try native_sdk.TestHarness().create(std.testing.allocator, .{ .size = geometry.SizeF.init(w, h) });` then `app_state.effects.executor = .fake;` before dispatching. Every `feed*`\u002F`fire*` returns `error{EffectNotFound}!void`; every `pending*At(index: usize)` returns an optional request record; each family's `pending*Count()` returns `usize`.\n>\n> - Spawn: `pendingSpawnAt(index: usize) ?SpawnRequest` · `feedLine(key: u64, bytes: []const u8)` · `feedStderr(key: u64, bytes: []const u8)` · `feedExit(key: u64, code: i32)` · `feedExitReason(key: u64, code: i32, reason: EffectExitReason)` (`feedExit` is the clean-exit sugar; the reason enum covers `signaled`\u002F`cancelled`\u002F...) · `feedOutput(key: u64, bytes: []const u8)` (raw collect-stdout append, no line framing).\n> - Fetch: `pendingFetchAt(index: usize) ?FetchRequest` · `feedResponse(key: u64, status: u16, body: []const u8)` · `feedResponseOutcome(key: u64, outcome: EffectFetchOutcome, status: u16, body: []const u8)`.\n> - Files: `pendingFileAt(index: usize) ?FileRequest` · `feedFileResult(key: u64, outcome: EffectFileOutcome, bytes: []const u8)`.\n> - Clipboard: `pendingClipboardAt(index: usize) ?ClipboardRequest` · `feedClipboardResult(key: u64, outcome: EffectClipboardOutcome, text: []const u8)`.\n> - Host commands: `pendingHostAt(index: usize) ?HostRequest` · `feedHostResult(key: u64, ok: bool, bytes: []const u8)`.\n> - Timers: `pendingTimerAt(index: usize) ?TimerRequest` · `fireTimer(key: u64)` (one-shot slots retire after the fire).\n> - Audio (one channel): `pendingAudio() ?AudioRequest` · `feedAudioEvent(kind: EffectAudioEventKind, position_ms: u64, duration_ms: u64, playing: bool)` · `feedAudioEventBuffering(kind, position_ms, duration_ms, playing, buffering: bool)` · `feedAudioSpectrum(bands: [32]u8, position_ms: u64, duration_ms: u64)` · `audioSnapshot() AudioSnapshot`.\n> - Channels: `channelHandle(key: u64) ?ChannelHandle` (post from the test exactly as a source thread would — no feed needed for live posts) · `feedChannelEvent(key: u64, kind: EffectChannelEventKind, bytes: []const u8, dropped_pending: u32, dropped_total: u32) error{EffectNotFound, EffectQueueFull}!void` (hand-feeds an event, the replay path's seam).\n>\n> After feeding, drain with `try harness.runtime.dispatchPlatformEvent(app, .wake);` — results become Msgs through the same path live platforms use.\n\n## Secondary windows: model-declared (`windows_fn` + `window_view`)\n\nWindows are model state, like an anchored surface's open flag. `Options.windows_fn` returns the descriptors that should exist RIGHT NOW (presence is visibility — no `visible` flag; the platform window channel has no hide); `Options.window_view` builds each declared window's whole canvas tree by window label. The runtime reconciles after every dispatch: create the newly declared, close the no-longer-declared, rebuild every open window's view from the same model.\n\n```zig\nfn windows(model: *const Model, scratch: *App.WindowsScratch) []const App.WindowDescriptor {\n    var count: usize = 0;\n    if (model.settings_open) {\n        scratch.windows[count] = .{\n            .label = \"settings\", .canvas_label = \"settings-canvas\",\n            .title = \"Settings\", .width = 360, .height = 320,\n            .min_width = 320, .min_height = 280, \u002F\u002F the WINDOW enforces the floor (macOS contentMinSize)\n            .on_close = .settings_closed,   \u002F\u002F the user's close button, as a Msg\n        };\n        count += 1;\n    }\n    return scratch.windows[0..count];\n}\nfn windowView(ui: *App.Ui, model: *const Model, window_label: []const u8) App.Ui.Node { ... }\n\u002F\u002F options: .windows_fn = windows, .window_view = windowView,\n```\n\nRules that matter:\n- **Every canvas label must be unique across the app** (main + declared windows); input routes back by it, and automation verbs (`widget-click \u003Ccanvas-label> \u003Cid>`, `screenshot`) address any window's canvas the same way.\n- **A user close dispatches `on_close`** (the dismissal precedent): the window is already gone as the optimistic echo; clear the open flag in `update` — or keep declaring the window and the next rebuild brings it back (source wins). A close the model itself initiated never echoes a Msg.\n- **Budget**: at most `UiApp.max_ui_windows` (4) declared windows; excess warns and is ignored. Every dispatched Msg rebuilds every open window's view.\n- **Present-before-show**: canvas windows (any `gpu_surface` view — startup, scene, and declared windows alike) are created ordered-out and become visible only after their first canvas frame presents, so opening one never flashes blank. Automatic (`WindowOptions.show = .on_first_present`, derived from the views); webview windows show immediately. The null platform records `window_show`, `window_visible`, and present\u002Fshown sequence numbers for ordering assertions; `NATIVE_SDK_WINDOW_TIMING=1` logs create→show latency on macOS.\n- **Markup binds ONE window's content** — there is no `window` element in the closed grammar. A markup-authored secondary window is a `canvas.CompiledMarkupView` whose `build` `window_view` calls for that label.\n- **Min size**: descriptors accept `.min_width`\u002F`.min_height` — a content min-size floor the WINDOW enforces (macOS `contentMinSize`), so a preferences window's resize stops at its honest floor instead of the layout clamping\u002Fclipping its panes. Same fields on app.zon windows and `ShellWindow` (the first shell window's declaration threads through the startup create like `titlebar`; negative values fail `zig build validate`). The null platform records `window_min_width`\u002F`window_min_height` for seam assertions.\n- **Titlebar**: descriptors accept `.titlebar = .hidden_inset` (content under a transparent titlebar, macOS keeps the traffic lights) or `.hidden_inset_tall` (the taller unified band; macOS centers the lights in it); give the window's header `window-drag=\"true\"` so it moves the window. See \"Hidden titlebar\" below.\n- **Settings windows open the standard way**: the app-menu Settings item and its primary+comma shortcut (an app.zon `.shortcuts` entry mapped in `on_command`), never an in-window settings button. Ship them fixed-size (`.resizable = false`) at exactly the content's box, title them \"Settings\", and let changes apply live through the shared model — no Apply\u002FOK row, no copy explaining the window.\n- Tests: after the open Msg, deliver the new window's `gpu_surface_frame` (its window id from `runtime.listWindows`) to install its tree; simulate a user close by dispatching `.window_frame_changed` with `open = false`. See `examples\u002Fsystem-monitor` (settings shortcut -> settings window).\n\n## Hidden titlebar: `titlebar = \"hidden_inset\"`\u002F`\"hidden_inset_tall\"` + `window-drag` + `on_chrome`\n\nThe modern editor-app shape — content under a transparent titlebar, the app's header as the working titlebar. Two heights: `hidden_inset` keeps the compact band (~28pt, traffic lights hug the top), `hidden_inset_tall` switches to the unified-toolbar band (~52pt, macOS vertically centers the traffic lights — the tall unified-toolbar look). Pick tall when the header replacing the titlebar is toolbar-height, so the lights center against it. Three parts, all declared:\n\n1. **app.zon**: `.titlebar = \"hidden_inset\"` or `\"hidden_inset_tall\"` on the shell window (and the matching `.titlebar = .hidden_inset`\u002F`.hidden_inset_tall` on the `ShellWindow` in main.zig). The first shell window's declaration threads through the STARTUP window create, so the main window's chrome is right from the first frame; `zig build validate` checks the value.\n2. **The header row** gets `window-drag=\"true\"`: its background (and plain text\u002Ficons inside) moves the window; buttons inside stay buttons; double-click zooms (macOS honors the user's titlebar double-click preference).\n3. **`Options.on_chrome`** (`fn (chrome: platform.WindowChrome) ?Msg`) delivers the chrome overlay geometry — `chrome.insets`: titlebar band height on top (compact or tall), traffic-light extent on the leading edge; `chrome.buttons`: the traffic-light cluster's frame in content coordinates (top-left origin), the vertical truth for centering. All-zero in fullscreen, on standard chrome, and on other platforms. It fires BEFORE the first view build and on changes; store the geometry in the model, pad the header with a leading `\u003Cspacer width=\"{chrome_leading}\" \u002F>`, and with the tall band match the header's height to `insets.top` (floored at its natural height) so `cross=\"center\"` puts its controls on the lights' centerline.\n\nmacOS-first like `resizable = false`: GTK\u002FWin32 keep standard chrome and the whole channel is harmless there. Full retrofit: `examples\u002Fmarkdown-viewer` (tall band; toolbar row is the drag region and tracks the band height). Tests: the null platform records `startWindowDrag` calls (`window_drag_starts`), per-window `window_titlebar`, and serves settable `window_chrome` (insets + buttons frame).\n\n## Time: wall clock + monotonic, with a testable seam\n\nZig 0.16 puts `std.time.milliTimestamp` behind `std.Io`, which `update` never sees — do NOT call `clock_gettime` yourself. The facade owns the clocks:\n\n```zig\nnative_sdk.nowMs()                  \u002F\u002F wall ms since the Unix epoch (i64) — ledger timestamps\nnative_sdk.nowNanoseconds()         \u002F\u002F wall ns (i128)\nnative_sdk.monotonicMs()            \u002F\u002F duration clock (u64, arbitrary origin, never goes backwards)\nnative_sdk.monotonicNanoseconds()   \u002F\u002F subtract two reads for an elapsed time\n```\n\nTime-DEPENDENT logic (elapsed-time display, timeouts driven from update) should hold the seam in the model instead of calling the free functions, so tests stay deterministic:\n\n```zig\npub const Model = struct { clock: native_sdk.Clock = .system, ... };\n.step_started => model.entry.started_ms = model.clock.wallMs(),\n\n\u002F\u002F in tests:\nvar test_clock: native_sdk.TestClock = .{};\nmodel.clock = test_clock.clock();\ntest_clock.advanceMs(1500);          \u002F\u002F moves wall + monotonic together\ntest_clock.setWallMs(1_700_000_000_000);  \u002F\u002F NTP-style wall jump, monotonic untouched\n```\n\nWall answers \"what time is it?\" (jumps with OS clock adjustments); monotonic answers \"how long did it take?\". Don't subtract wall timestamps for durations.\n## Images: runtime-registered pixels + the avatar pattern\n\nImage pixels are runtime-registered resources keyed by a caller-chosen `ImageId` (`u64` in the model, effect-key style; 0 = no image). The framework bundles NO codecs — encoded bytes decode through the platform (CGImageSource \u002F gdk-pixbuf \u002F WIC) via `PlatformServices.decode_image_fn`. Registration lives on the effects channel (synchronous calls, not effects — no Msg follows):\n\n```zig\n\u002F\u002F The fetch-avatar path, one update arm; id reaches the model ONLY on success,\n\u002F\u002F so the avatar shows initials while loading and after failure.\n.fetched => |response| {\n    if (response.outcome == .ok and response.status == 200) {\n        _ = fx.registerImageBytes(avatar_image_id, response.body) catch return;\n        model.avatar_image = avatar_image_id;\n    }\n},\n```\n\n```zig\n\u002F\u002F Zig views (image and icon content is markup-excluded):\nui.avatar(.{ .image = model.avatar_image, .semantics = .{ .label = \"Octocat\" } }, \"OC\"),\nui.image(.{ .image = model.chart_image, .width = 120, .height = 80, .semantics = .{ .label = \"Chart\" } }),\n```\n\n```html\n\u003C!-- Markup avatars bind the same model id: one {binding} to the u64 ImageId\n     (a field or pub fn — never a literal); 0 renders the initials fallback. -->\n\u003Cavatar image=\"{avatar_image}\" label=\"Octocat\">OC\u003C\u002Favatar>\n```\n\nRules:\n\n- `fx.registerImage(id, w, h, rgba8)` takes already-decoded straight-alpha RGBA8 (exactly `w*h*4` bytes; the runtime copies — your buffer is free on return). `fx.registerImageBytes(id, bytes)` decodes first. `fx.unregisterImage(id)` frees the slot. Outside UiApp: `Runtime.registerCanvasImage` \u002F `registerCanvasImageBytes` \u002F `unregisterCanvasImage`.\n- Re-registering an id replaces the pixels; every view repaints and GPU caches re-upload off the changed content fingerprint — no invalidation calls. For caches, mint fresh ids (effect-key style, monotonically increasing) and `unregisterImage` the evictee — never re-key different content onto a live id.\n- Bounded and loud (`canvas_limits`): 16 slots (`max_registered_canvas_images`), 1 MiB per image (`max_registered_canvas_image_pixel_bytes`, 512×512 RGBA8 — avatar\u002Ficon scale). Errors: `error.ImageRegistryFull`, `error.ImageTooLarge`, `error.ImageDecodeFailed`, `error.InvalidImageId`\u002F`InvalidImageDimensions`, `error.UnsupportedService` (codec-less platform).\n- A draw referencing an unregistered id skips — a transient loading state can never fail presentation. `ui.avatar` clips a set image to the circle (`cover` fit) and renders the initials argument otherwise.\n- Registered images render in live presentation AND `renderCanvasScreenshot`\u002Fautomation screenshots, so goldens can assert on them.\n- Deterministic tests: `harness.null_platform.image_decode = true` enables a strict decoder for the exact PNG subset `canvas.png.writeRgba8` emits — encode a raw RGBA fixture with the canvas PNG writer and drive the full decode→register→draw path with no bundled codec (`src\u002Fruntime\u002Fcanvas_image_tests.zig` is the reference).\n\n## Structure tags\n\n```html\n\u003Cfor each=\"visible\" key=\"id\" as=\"t\"> \u003Crow>...\u003C\u002Frow> \u003C\u002Ffor>   \u003C!-- one or more element children; key names an item field -->\n\u003Cif test=\"{c.movable}\"> \u003Cbutton ...>Move\u003C\u002Fbutton> \u003C\u002Fif>\n\u003Celse> \u003Ctext>Done!\u003C\u002Ftext> \u003C\u002Felse>                             \u003C!-- must directly follow the if -->\n```\n\nA `\u003Cfor>` body takes one or more children — elements, `\u003Cuse>`, `\u003Cif>`\u002F`\u003Celse>` arms, or nested `\u003Cfor>`s — so polymorphic rows need no wrapper node: put the `\u003Cif>`\u002F`\u003Celse>` arms directly in the body and each item emits whichever arm wins. With `key`, every node an item emits shares the item's identity (same-kind siblings within one item are disambiguated automatically); a node's own `key`\u002F`global-key` still wins. Unkeyed same-kind siblings take POSITIONAL identity (sibling index), so an `\u003Cif>` that inserts or removes an earlier same-kind sibling re-disambiguates the trailing ones — engine-owned state like carets and scroll offsets can hop; keyed items and keyed ancestors hold identity. An `\u003Celse>` directly after a `\u003C\u002Ffor>` renders the empty state when the iterable has no items:\n\n```html\n\u003Cfor each=\"visible\" key=\"id\" as=\"t\">\n  \u003Cif test=\"{t.done}\"> \u003Cbadge>done\u003C\u002Fbadge> \u003C\u002Fif>\n  \u003Celse> \u003Ctext>{t.title}\u003C\u002Ftext> \u003C\u002Felse>\n\u003C\u002Ffor>\n\u003Celse> \u003Ctext>Nothing yet\u003C\u002Ftext> \u003C\u002Felse>                       \u003C!-- renders when visible is empty -->\n```\n\nThere is no `else-if` chain tag: nest an `\u003Cif>`\u002F`\u003Celse>` inside the `\u003Celse>` body instead. `\u003Cif>` has no negation operator either — prefer an explicit boolean predicate method on the model per arm.\n\n## Templates: `\u003Ctemplate>` + `\u003Cuse>`\n\nWhen the same subtree repeats with different data (board columns, dashboard sections), define it ONCE at the top of the file — zero or more `\u003Cimport>` lines, then zero or more `\u003Ctemplate>` definitions, then the view root (a file that is ALL templates is a component file, valid only as an import target):\n\n```html\n\u003Ctemplate name=\"board-column\" args=\"title cards\">\n  \u003Ccolumn grow=\"1\" gap=\"8\" label=\"{title}\">\n    \u003Ctext foreground=\"text_muted\">{title}\u003C\u002Ftext>\n    \u003Cfor each=\"cards\" key=\"id\" as=\"c\">\n      \u003Crow global-key=\"{c.id}\">\u003Ctext>{c.title}\u003C\u002Ftext>\u003C\u002Frow>\n    \u003C\u002Ffor>\n  \u003C\u002Fcolumn>\n\u003C\u002Ftemplate>\n\u003Crow grow=\"1\" gap=\"12\">\n  \u003Cuse template=\"board-column\" title=\"Todo\"  cards=\"{todoCards}\" \u002F>\n  \u003Cuse template=\"board-column\" title=\"Doing\" cards=\"{doingCards}\" \u002F>\n  \u003Cuse template=\"board-column\" title=\"Done\"  cards=\"{doneCards}\" \u002F>\n\u003C\u002Frow>\n```\n\nRules and semantics:\n\n- A template takes `name` (kebab-case), optional `args` (space-separated names, each optionally `name=default`), and exactly one element child. `\u003Cuse template=\"name\">` is allowed anywhere an element is (including as a `for` child or the view root); its other attributes must match the template's `args` exactly — missing args without a default and extra args are errors.\n- Arg defaults are LITERALS only (`args=\"title trend=flat count=0\"`): a default evaluates in no scope, so `{binding}` defaults are errors. A use site may omit any defaulted arg. `args=\"name=\"` declares an EMPTY-STRING default; defaults are unquoted — quotes in a default would be literal characters, so a quoted default (`name='x'`) is a teaching error.\n- The template body is built IN PLACE of the `\u003Cuse>`: structural widget ids hash through the parent chain at the expansion site, exactly as if you had written the body inline. Two uses at different sites get different ids; the same site is stable across rebuilds. Rewriting copy-pasted markup as a template does not change any widget id.\n- Args bind like `for` variables: an arg whose value is a `{binding}` naming an iterable (model slice\u002Farray field, pub decl, or model fn — the same set `for each` accepts) is iterable inside the template (`\u003Cfor each=\"cards\" ...>`); any other arg (literal or scalar binding) is a value usable in bindings, interpolation, and equality (`{title}`, `label=\"{title}\"`). Args are evaluated at the use site; inside the body only the args, the model, and the body's own loop variables are in scope. Value args are scalars — `{arg.field}` is an error.\n- Uses inside a template body may only reference templates defined EARLIER in the file (this also makes recursion impossible). Bindings stay zero-argument: the template deduplicates the view, the per-case query stays a named model function.\n- SLOTS: a template body may contain one `\u003Cslot\u002F>` (attribute-less, childless; named slots do not exist). The `\u003Cuse>` site's children build IN THE CONSUMER'S SCOPE — they see the model paths and loop variables where the use is written — and land at the slot's position; ids hash as if inlined. A use with no children renders the slot empty; children on a slotless template are an error; a `\u003Cslot\u002F>` inside use-site children (forwarding) is an error.\n- IMPORTS: `\u003Cimport src=\"components\u002Fcards.native\"\u002F>` lines go at the very top of a file, before its templates. Paths are relative to the importing file (subdirectories and transitive imports fine, always under the root view file's directory — absolute paths and escapes are errors). An imported file defines templates ONLY (a component file; a view root inside one is an error, and a component file checks standalone). Importing splices the file's templates (transitively) BEFORE yours, in import order — as if pasted at the import site — so define-before-use stays the only ordering rule. Cycles are reported with the cycle path; duplicate template names are an error naming both definition sites.\n\nBoth engines implement templates, defaults, slots, and imports: the interpreter expands at build time (hot reload re-resolves imports from disk, so edits to imported files reload), and the compiled engine inlines at comptime with the identical result. A document with imports compiles through `canvas.CompiledMarkupImports(Model, Msg, \"root.native\", &sources)` where `sources` is a `canvas.ui_markup.SourceFile` set (`.{ .path = \"components\u002Fcards.native\", .source = @embedFile(\"components\u002Fcards.native\") }`, paths relative to the root file's directory); pass the same set on `MarkupOptions.sources` for the runtime engine. See `examples\u002Fkanban\u002Fsrc\u002Fboard.native` + `examples\u002Fkanban\u002Fsrc\u002Fcomponents\u002Fboard-column.native`.\n\n## Markdown in markup: `\u003Cmarkdown>`\n\nA leaf element that renders a markdown string (the GFM subset below) as ordinary widgets, wiring `native_sdk.markdown` for you — both engines implement it identically:\n\n```html\n\u003Cmarkdown source=\"{issue_body}\" on-link=\"open_url\" on-details=\"toggle_details\" details-expanded=\"{details_expanded}\" \u002F>\n```\n\n- `source` (required): one `{binding}` producing the markdown text — a `[]const u8` field, zero-arg fn, or arena-taking fn (compose the document into the build arena at view time).\n- `on-link` (optional): a BARE Msg tag — no `:{payload}` — whose payload is the pressed link URL; declare `open_url: []const u8` in `Msg`.\n- `on-details` (optional): a bare Msg tag whose payload is the `\u003Cdetails>` block's document-order index; declare `toggle_details: usize`.\n- `details-expanded` (optional): one `{binding}` naming a `[]const bool` iterable (a model field, pub decl, or fn — the same sources `for each` accepts); flags are read in details-block document order. Keep a bounded `details_expanded: [8]bool` in the model and toggle it in `update`.\n- `issue-link-base` (optional): a literal URL prefix or one `{binding}` producing it; `#123` references at word boundaries become links to base ++ number (`issue-link-base=\"ghissue:\u002F\u002F\"` links `#123` to `ghissue:\u002F\u002F123` — an app scheme your `on-link` handler intercepts, or a web base like `https:\u002F\u002Fgithub.com\u002Fowner\u002Frepo\u002Fissues\u002F`). Off by default: resolving a ref needs repo context.\n- No children, no text content, no other attributes (teaching errors point at misuse). Without the details wiring, `\u003Cdetails>` blocks render collapsed and inert; without `on-link`, links render styled but inert.\n\n## Pipeline composites: stepper, timeline, nav\n\nThree composites for pipeline\u002Frun UIs — pure compositions of existing widgets (no new kinds), identical from markup and `canvas.Ui`:\n\n```html\n\u003Cstepper active=\"{stage_index}\">\n  \u003Cstep>Work\u003C\u002Fstep>\u003Cstep>Triage\u003C\u002Fstep>\u003Cstep>Review · {round}\u003C\u002Fstep>\u003Cstep>Fix\u003C\u002Fstep>\u003Cstep>Ready\u003C\u002Fstep>\n\u003C\u002Fstepper>\n\u003Ctimeline gap=\"4\">\n  \u003Cfor each=\"ledger\" key=\"slot\" as=\"entry\">\n    \u003Ctimeline-item title=\"{entry.title}\" description=\"{entry.summary}\" meta=\"{entry.meta}\" variant=\"{entry.tone}\" on-press=\"open_step:{entry.slot}\" \u002F>\n  \u003C\u002Ffor>\n\u003C\u002Ftimeline>\n```\n\n- Stepper semantics: a `list` of `listitem`s; the active step is `selected` and every label carries its state (`\"Review (active)\"`) plus list position — assert pipeline stage from automation snapshots by label.\n- Timeline item: leading badge (dot colored by `variant`, or `indicator` text like `\"✓\"`), connector rail (`connector=\"false\"` ends it), bold title, wrapped muted description, muted meta line. With `on-press` the item gains a trailing chevron and the press binds to the item's root (role `listitem`, focusable, labeled by the title) — clicks on the title\u002Fdescription\u002Fmeta fall through to it, so a click anywhere dispatches and dragging still selects the text. No hover fill or description line-clamp in v1.\n- Zig: `ui.stepper(.{ .active = ... }, &.{ .{ .label = \"Work\" }, ... })`, `ui.timeline(options, items)`, `ui.timelineItem(.{ .title = ..., .on_press = ... })`.\n- Nav (Zig-only; markup swaps with `\u003Cif>`): `ui.nav(.{ .active = model.nav_depth, .retain = true }, .{ pageA, pageB })` — the model owns the stack; pages are index-keyed so widget ids (and engine scroll\u002Ftext state) are stable across swaps; `retain=true` keeps inactive pages mounted-but-hidden (state preserved, excluded from render\u002Fhit-test\u002Ffocus\u002Fsemantics), default unmounts. Instant swap, no animation in v1; move focus in `update` when pushing\u002Fpopping if the focused widget lives on the outgoing page.\n\n## Charts\n\n`\u003Cchart>` is the data-visualization composite: `\u003Cseries>` children bind model `[]const f32` iterables and draw through the vector path pipeline with token colors — charts retheme with the palette, repaint exactly when their data changes (value equality, not identity), and report series semantics to automation. Both engines lower it through `ui.chart`, so markup charts and Zig charts are pixel- and semantics-identical.\n\n```html\n\u003C!-- Star-history: cumulative stars per repo. 10k-point series are fine —\n     charts downsample deterministically past 256 points per series. -->\n\u003Cchart grow=\"1\" height=\"220\" y-min=\"0\" grid-lines=\"3\" label=\"Star history\">\n  \u003Cseries kind=\"area\" values=\"{sdkStars}\" color=\"accent\" label=\"native-sdk\" \u002F>\n  \u003Cseries kind=\"line\" values=\"{examplesStars}\" color=\"info\" label=\"examples\" \u002F>\n\u003C\u002Fchart>\n\u003C!-- Sparkline tile: zero-baseline bars pinned to an absolute 0..1 domain. -->\n\u003Cchart width=\"239\" height=\"32\" y-min=\"0\" y-max=\"1\" label=\"CPU history\">\n  \u003Cseries kind=\"bar\" values=\"{cpuSpark}\" \u002F>\n\u003C\u002Fchart>\n```\n\n- Kinds (literal): `line` (polyline; one sample draws a dot), `area` (a line filled to the baseline — the builder's `fill = true`), `bar` (one bar per value, ALWAYS anchored at zero — the auto domain forces 0 in, negatives hang below; a zero value draws nothing).\n- Data: `values` takes one `{binding}` naming an f32 iterable — a model field (slice or array), pub decl, or fn (arena fns work), the SAME resolution set as `for each` (slice-valued template args included). Values are y samples at uniform x steps, oldest first. `NaN` = missing sample, draws a gap — pad a filling window with leading `NaN` in a model fn so the trace enters from the right (see examples\u002Fsystem-monitor). The series SET is static: the data varies through bindings, and dynamic series composition stays with the Zig builder.\n- Domain: derived per side from the data unless `y-min`\u002F`y-max` pin it (literals or scalar bindings); a flat series expands symmetrically. `grid-lines=\"N\"` draws N horizontal token hairlines (opt-in, none by default); `baseline=\"true\"` marks the zero line; `stroke-width` overrides the 1.5 default.\n- Axis labels (opt-in, muted register, reserved gutters): `x-labels=\"{binding}\"` names a model iterable of strings — one category label per sample, oldest first, thinned deterministically to fit the width (dropped entirely when a series downsamples: bucketed indices no longer name the labeled samples). `y-labels=\"true\"` adds numeric ticks on a nice-step lattice (1\u002F2\u002F5 × 10^k — labels exact at their precision); with `grid-lines` set the gridlines ride the same lattice so grid and labels agree.\n- Hover details: `hover-details=\"true\"` snaps the pointer to the nearest sample and floats a card (sample label + every series' name and value, hairline cursor, dots on line points). Interaction-only chrome — static renders never show it; the chart becomes a hover target but still never claims presses.\n- Box: `width`\u002F`height` (definite; omitted keeps the intrinsic 160x48 sparkline default), `grow` (flexes the pane like any element — `grow=\"1\"` is how a chart fills its column), `padding`, `key`\u002F`global-key`, `label`.\n- Colors are token names (`accent`, `info`, `success`, `warning`, `destructive`, ...) — never raw colors — so both themes hold up; default `accent`.\n- Downsampling: past 256 points per series, deterministic index-bucket min\u002Fmax decimation (spikes survive; same series → same pixels, golden-testable). The generated semantics summary still describes the SOURCE series.\n- Semantics: role `chart`; label = a generated summary (`\"chart: stars 10000 pts last 9999.00\"`) unless `label` is set; accessibility value = the first series' latest point — assert live data from snapshots without pixels.\n- Display-only: no `on-*` events and never a press claimer; clicks fall through to the nearest pressable ancestor, so charts inside pressable rows keep the row clickable (`hover-details` makes it a hover target only).\n- Zig escape hatch: `ui.chart(ChartOptions, &.{canvas.ChartSeries...})` is the same code with the same options, plus what markup deliberately excludes — `.band` series (min\u002Fmax envelope: `values` upper, `low` lower — a PAIRED second slice per point) and series lists composed at view time (`examples\u002Fdeck` builds its peak-trace series data in the model; a truly dynamic series COUNT is builder territory).\n\n## Rich text: inline spans and markdown\n\nMixed-style text inside ONE wrapped paragraph: put `\u003Cspan>` children inside a `\u003Ctext>` element. Each span styles one run — `weight` (`regular`\u002F`medium` — the semibold rung —\u002F`bold`), `mono`, `italic`, `scale` (a positive multiplier on the paragraph's base size), `underline`, and `foreground` (a color token name) — and the whole thing word-wraps as one paragraph, announcing to assistive tech as ONE text run (spans are visual). Bindings interpolate inside spans like any text:\n\n```html\n\u003Ctext>\n  Disk \u003Cspan weight=\"bold\">{diskUsed}\u003C\u002Fspan> of\n  \u003Cspan foreground=\"text_muted\">{diskTotal}\u003C\u002Fspan> — run\n  \u003Cspan mono=\"true\">native doctor\u003C\u002Fspan> if this looks wrong.\n\u003C\u002Ftext>\n```\n\n- Whitespace between runs collapses to a single space; runs written with NO whitespace between them abut (`\u003Cspan mono=\"true\">init.zig\u003C\u002Fspan>.` puts the period flush against the mono run).\n- Spans do not nest and take no events or keys — layout (`width`, `grow`, `text-alignment`), identity, and `label` stay on the enclosing `\u003Ctext>`. A span paragraph always word-wraps, so `wrap`\u002F`overflow` on it are teaching errors.\n- `scale` multiplies the base size the paragraph's typography resolves to — `size=\"heading\"` with `scale=\"1.5\"` draws heading × 1.5 — and line breaking measures scaled runs at their scaled size, so the wrap is honest. Only positive finite multipliers are accepted (a literal number or one `{binding}` producing one); zero\u002Fnegative\u002Fnon-numeric scales are teaching errors. One limit to design around: the paragraph reserves ONE uniform line height sized by its largest scale and every run shares that baseline — use mixed scale for inline headings and hero stats beside their captions, not to stack independent text sizes.\n- `underline` is a pure decoration (true\u002Ffalse or a `{binding}`) — it does not make the run a link.\n- Markup exposes the span model's weight\u002Fmono\u002Fitalic\u002Fscale\u002Funderline\u002Fcolor channels; `strikethrough`, `background` highlights, and `link` spans stay Zig-builder territory (below).\n\nThe same paragraph from a Zig view, plus the builder-only channels:\n\n```zig\nconst spans = [_]canvas.TextSpan{\n    .{ .text = \"Ship the \" },\n    .{ .text = \"bold\", .weight = .bold },\n    .{ .text = \" parts, run \" },\n    .{ .text = \"zig build test\", .monospace = true },\n    .{ .text = \", then read \" },\n    .{ .text = \"the guide\", .link = \"https:\u002F\u002Fexample.com\u002Fguide\" },\n};\nui.paragraph(.{ .on_link = Ui.linkMsg(.open_url) }, &spans)\n```\n\n- Each span carries `weight` (regular\u002Fmedium\u002Fbold), `italic`, `monospace`, `color` (a `ColorTokens` field name), `underline`, `strikethrough`, `scale` (size multiplier vs the body token — how headings work), and `link`.\n- Wrapping is span-aware and measured with the same provider the platform draws with; a paragraph reserves its real wrapped height when stacked in a column.\n- Link spans are hit-testable: they appear in automation snapshots as `role=link` named by their visible text, show a pointer cursor, and pressing one dispatches `on_link(span.link)` — declare `open_url: []const u8` in `Msg` and pair with `Ui.linkMsg(.open_url)`.\n- Capacities: `canvas.max_text_spans_per_paragraph` (32) spans per paragraph; overflow truncates deterministically.\n\nMarkdown (GitHub-flavored subset) maps onto the same widgets. In markup use the `\u003Cmarkdown>` element above; from a Zig view call it directly:\n\n```zig\nconst Md = native_sdk.markdown.Markdown(Msg);\n\u002F\u002F inside view():\nMd.view(ui, model.body_markdown, .{\n    .on_link = Ui.linkMsg(.open_url),\n    .on_details = Md.detailsMsg(.toggle_details),   \u002F\u002F Msg{ .toggle_details = usize }\n    .details_expanded = &model.details_expanded,     \u002F\u002F caller-owned [N]bool\n})\n```\n\n- Supported: `#`–`###` headings, paragraphs with `**bold**`\u002F`*italic*`\u002F`` `code` ``\u002F`~~strike~~`\u002F`[links](url)`, bare `http(s):\u002F\u002F` URLs (autolink, trailing punctuation trimmed), `#123` issue refs (opt-in: set `Options.issue_link_base` and the ref links to base ++ number), bullet + ordered + task lists (task checkboxes are display-only, disabled), fenced code blocks, `> blockquotes`, `---` rules, GFM pipe tables (header bold, `:---`\u002F`:--:`\u002F`---:` column alignment, inline spans + clickable links inside cells, `\\|` escapes a pipe in a cell; columns share width equally, and a missing\u002Fmismatched delimiter row degrades the block to paragraphs), `\u003Cdetails>\u003Csummary>`.\n- Not in v1 (degrades to plain text, never fails): reference links, raw HTML, footnotes, backslash escapes (except `\\|` in table rows).\n- `\u003Cdetails>` state is elm-style: the CALLER owns the expanded flags. Keep a bounded `details_expanded: [8]bool` in the model, toggle it in `update` on the details message, and pass the slice back in.\n\n## Zig 0.16, not 0.15: the std idioms that changed\n\nThe toolkit requires Zig 0.16.0, and code written from memory of older Zig fails with \"no member named\" errors on std APIs. The recurring ones, old → current:\n\n| Compile error says | Old idiom | Write instead |\n| --- | --- | --- |\n| `struct 'array_list.Aligned(u8,null)' has no member named 'init'` | `std.ArrayList(T).init(allocator)`, `list.append(x)` | `var list: std.ArrayList(T) = .empty;` then `list.append(allocator, x)`, `list.deinit(allocator)` — the allocator rides every call |\n| `struct 'heap' has no member named 'GeneralPurposeAllocator'` | hand-built GPA in `main` | `pub fn main(init: std.process.Init) !void` — use `init.gpa`, `init.arena.allocator()`, `init.io` (the generated `main` already does) |\n| `struct 'fs' has no member named 'cwd'` | `std.fs.cwd().openFile(path, .{})` | `std.Io.Dir.cwd().openFile(io, path, .{})` — file IO lives on `std.Io.Dir` and takes an `io`; in `update` use `fx.readFile`\u002F`fx.writeFile` instead |\n| `struct 'std' has no member named 'io'` | `std.io.getStdOut().writer()` | `std.Io.File.stdout().writer(io, &buffer)` then print through `&writer.interface` and `flush()`; `std.debug.print` is unchanged |\n| `struct 'time' has no member named 'sleep'`\u002F`'milliTimestamp'` | `std.time.sleep(ns)`, `std.time.milliTimestamp()` | clocks live on `Io` — in app code use `fx.wallMs()` \u002F `native_sdk.nowMs()` \u002F `fx.startTimer` (see Time above), in tests `std.Io.sleep(std.testing.io, ...)` |\n| `invalid format string 's' for type '...'` | `{s}` on an enum | `{t}` prints the tag name; custom `format` methods take `(self, writer: *std.Io.Writer)` and print with `{f}` |\n| `struct 'process.Child' has no member named 'init'` | `Child.init` + `child.spawn()` | `fx.spawn` in apps; `std.process.spawn(io, .{ .argv = ... })` + `child.wait(io)` in tools |\n\nTests that touch files or clocks get their `Io` from `std.testing.io`. The full catalog — env\u002Fargs, sockets, readers, `build.zig` shapes, each with the SDK's live reference file — is its own skill: `native skills get zig`.\n\n## Validate without building\n\n`native markup check src\u002Fview.native` — instant grammar\u002Fstructure validation with `file:line:column` errors, including the font-coverage tofu guard: literal text with a codepoint outside the bundled face (⌘, ✓, ⑂, dingbats, CJK) is a teaching error naming the character, because it renders as a tofu box on the reference\u002Fscreenshot and mobile paths — register a font that covers it (`UiApp.Options.fonts`) and bind the text from the model (the guard skips `{bindings}`), or use a vector icon (`icon=` \u002F `\u003Cicon name>`) or plain words. Dynamic strings get the same lesson as a Debug-build `zero_canvas_ui` diagnostic when the view builds. The accessibility lint rides the same pass: unnamed interactive controls and role misuse are errors, unnamed images and redundant labels are warnings (`--strict` promotes).\n\nThe model side checks at check time too: the model-contract step (refreshed by `native test`, or run directly as `zig build model-contract` in an app that owns its build) reflects Model\u002FMsg into `zig-out\u002Fmodel-contract.zon`, and `native check` (or `markup check` run in the app directory) then verifies every binding path, iterable, `key` field, message tag, payload type, and expression type against the app's real surface — did-you-mean over your actual field names, and type errors naming the field's Zig type. It also WARNS on model state and Msg tags no view uses; opt update-only names out with `pub const view_unbound = .{ \"next_id\" };` on Model or Msg (`--strict` turns the warnings into failures) — state consumed only by a Zig-BUILT view needs `view_unbound` too, because the markup checker cannot see Zig view reads. A stale artifact degrades to grammar-only checking with a loud note (\"model contract: not yet built - bindings checked structurally only; run `native test` to enable typed checks\"); binding paths and message tags are always re-enforced when the app builds (and on hot reload).\n\n## Testing pattern\n\nUnit tests exercise the real dispatch path — no GUI needed:\n\n```zig\nvar view = try canvas.MarkupView(Model, Msg).init(arena, main.habits_markup);\nvar ui = canvas.Ui(Msg).init(arena);\nconst tree = try ui.finalize(try view.build(&ui, &model));\nconst button = findByText(tree.root, .button, \"Done today\").?;   \u002F\u002F walk tree.root\nmain.update(&model, tree.msgForPointer(button.id, .up).?);        \u002F\u002F dispatch exactly like the runtime\n\u002F\u002F rebuild and assert: text updated, widget ids stable\n```\n\nTwo `msgForPointer` traps: a **disabled** control yields `null` (assert `== null` rather than unwrapping when testing disabled states), and the tree is a snapshot — after each dispatch, rebuild the view before pressing anything again.\n\nOne widget-field trap: there is no `Widget.enabled` — enabled-ness is spelled `widget.state.disabled` (a `bool`, disabled-positive), so a test asserts `try testing.expect(!button.state.disabled);` for an enabled control and `try testing.expect(save.state.disabled);` for a disabled one.\n\n`msgForPointer` has a sibling for every handler channel — use the one matching the interaction under test, all on the finalized `Tree`: `msgForKeyboard(id, keyboard_event)` (activation keys, slider steps, enter-to-submit, text edits), `msgForResize(id, fraction)` (the split-divider round-trip: dispatch the fraction, assert the model stored it, rebuild, assert the `value` echo), `msgForDismiss(id)` (an anchored surface's `on-dismiss`), `msgForHold(id)` (`on-hold`), `msgForTextEdit(id, edit)` (text entry), `msgForValue(id, value)` (BUILDER views only — it fires the `on_value` constructor, so it returns null for a markup slider; a markup slider binds a plain `on-change`, so assert its dispatch with `msgFor(id, .change)` — the accessibility set-value intent falls back to the same handler), `msgForScroll(id, state)`, and `msgForContextMenu(id, item_index)`. For tree keyboard NAVIGATION there is nothing app-side to unit test: Up\u002FDown\u002FLeft\u002FRight\u002FHome\u002FEnd run engine-side over `role=\"treeitem\"` rows and dispatch the landed row's `on-press`\u002F`on-toggle` — assert those Msgs (via `msgForPointer`\u002F`msgFor(id, .toggle)`) and the model transitions; the keymap itself is runtime behavior (drive it live with `native automate widget-key`).\n\nRuntime-integration tests use `native_sdk.TestHarness()` on the null platform; heap-allocate both the harness and the app struct (they are multi-megabyte; stack allocation crashes).\n\n## Verify live through the automation harness\n\n```bash\nnative build -Dautomation=true\n.\u002Fzig-out\u002Fbin\u002F\u003Capp> &   # run from the example directory\nnative automate wait                     # blocks until ready=true\ncat .zig-cache\u002Fnative-sdk-automation\u002Fsnapshot.txt   # widgets with ids, roles, names, bounds, state\nnative automate widget-click \u003Ccanvas-label> \u003Cid>   # id is the bare number (snapshot prints #id)\nnative automate widget-hold \u003Ccanvas-label> \u003Cid>    # press-and-hold: drives on_hold via the real timer path\nnative automate widget-context-press \u003Ccanvas-label> \u003Cid>   # right-click: context menu, or on_hold when none\n```\n\nSnapshots expose the same structural widget ids your tests see, so live assertions are greps: click by id, re-read the snapshot, and check names\u002Fvalues\u002Fcounts changed. Widget ids are stable across rebuilds, reorders, and hot reloads — asserting an id stayed constant while its bounds or state changed is the standard way to prove keyed identity.\n\nFor scripted checks (and the CI workflow `native init --full` scaffolds), replace grep-and-sleep with `native automate assert`: each argument is a regex that must match the snapshot, polled up to `--timeout-ms` (default 30000), with `--absent` inverting the check. Failure names the missing patterns and prints the snapshot tail.\n\n```bash\nnative automate assert 'gpu_nonblank=true' 'role=button name=\"Reset\"' 'count: 0'\nnative automate assert --absent 'error event='\n```\n",{"data":37,"body":38},{"name":4,"description":6},{"type":39,"children":40},"root",[41,50,56,124,137,174,211,218,429,473,509,543,598,611,618,631,717,754,760,795,842,886,980,992,998,1051,1116,1122,1158,1246,1724,1737,1808,1845,1913,1919,4107,4326,4332,5087,5115,5152,5164,5197,5448,5527,5533,5626,6058,6063,6201,6313,6319,6343,6656,6845,6851,6868,7319,7409,7415,7482,7564,7570,7672,7678,7791,7796,7881,7887,7914,8055,8060,8342,8363,8369,8381,8642,8795,8874,8887,8893,8914,9128,9133,9467,9495,9514,9535,9541,9561,9794,9799,9805,9817,9879,9890,9929,9949,10096,10101,10147,10185,10214,10250,10256,10468,10508,10554,10599,10666,10672,10685,10755,10796,10802,10807,10936,10990,11019,11025,11083,11162,11168,11194,11240,11267,11352,11370,11527,11532,11832,11843,11990,11995,12113,12148,12282,12355,12395,12557,12562,12719,12756,12847,12852,12972,13006,13129,13258,13352,13505,13777,13826,14047,14216,14221,14370,14442,14766,14786,14815,14937,14942,15281,15314,15335,15467,15520,15526,15562,15601,15606,15676,15681,15687,15714,15783,15814,15913,15917,16124,16130,16404,16498,16789,16829,16847,16867,17558,17563,17765,17824,17836,17849,17959,18175,18181,18192,18650,18795,18801,18833,19387,19740,19746,19828,20043,20185,20190,20268,20395,20407,20470,20641,20647,20652,21098,21133,21139,21203,21280,21286,21291,21346,21381,21425,21587,21600,21606,21859,21864,21900,21998],{"type":42,"tag":43,"props":44,"children":46},"element","h1",{"id":45},"author-native-ui-with-markup-zig",[47],{"type":48,"value":49},"text","Author native UI with markup + Zig",{"type":42,"tag":51,"props":52,"children":53},"p",{},[54],{"type":48,"value":55},"A native-rendered Native SDK app is a markup view plus Zig logic:",{"type":42,"tag":57,"props":58,"children":59},"ul",{},[60,73],{"type":42,"tag":61,"props":62,"children":63},"li",{},[64,71],{"type":42,"tag":65,"props":66,"children":68},"code",{"className":67},[],[69],{"type":48,"value":70},"src\u002F\u003Cview>.native",{"type":48,"value":72}," — the entire UI: elements, layout, bindings, message dispatch.",{"type":42,"tag":61,"props":74,"children":75},{},[76,82,84,90,92,98,100,106,108,114,116,122],{"type":42,"tag":65,"props":77,"children":79},{"className":78},[],[80],{"type":48,"value":81},"src\u002Fmain.zig",{"type":48,"value":83}," — ",{"type":42,"tag":65,"props":85,"children":87},{"className":86},[],[88],{"type":48,"value":89},"Model",{"type":48,"value":91}," (plain struct), ",{"type":42,"tag":65,"props":93,"children":95},{"className":94},[],[96],{"type":48,"value":97},"Msg",{"type":48,"value":99}," (tagged union), ",{"type":42,"tag":65,"props":101,"children":103},{"className":102},[],[104],{"type":48,"value":105},"update(model, msg)",{"type":48,"value":107},", and a ",{"type":42,"tag":65,"props":109,"children":111},{"className":110},[],[112],{"type":48,"value":113},"main",{"type":48,"value":115}," that hands them to ",{"type":42,"tag":65,"props":117,"children":119},{"className":118},[],[120],{"type":48,"value":121},"native_sdk.UiApp(Model, Msg)",{"type":48,"value":123},".",{"type":42,"tag":51,"props":125,"children":126},{},[127,129,135],{"type":48,"value":128},"The markup compiles to the same widget tree a hand-written ",{"type":42,"tag":65,"props":130,"children":132},{"className":131},[],[133],{"type":48,"value":134},"canvas.Ui(Msg)",{"type":48,"value":136}," builder view would produce: identical structural widget ids, identical typed handler table. Markup can never mutate state — it binds values and dispatches messages; all logic lives in Zig.",{"type":42,"tag":51,"props":138,"children":139},{},[140,142,148,150,156,158,164,166,172],{"type":48,"value":141},"Editors highlight ",{"type":42,"tag":65,"props":143,"children":145},{"className":144},[],[146],{"type":48,"value":147},".native",{"type":48,"value":149}," markup well in HTML mode — the default scaffold writes no editor config, so add ",{"type":42,"tag":65,"props":151,"children":153},{"className":152},[],[154],{"type":48,"value":155},".vscode\u002Fsettings.json",{"type":48,"value":157}," with ",{"type":42,"tag":65,"props":159,"children":161},{"className":160},[],[162],{"type":48,"value":163},"\"files.associations\": {\"*.native\": \"html\"}",{"type":48,"value":165}," yourself, or scaffold with ",{"type":42,"tag":65,"props":167,"children":169},{"className":168},[],[170],{"type":48,"value":171},"native init --full",{"type":48,"value":173},", which writes it.",{"type":42,"tag":51,"props":175,"children":176},{},[177,179,185,187,193,195,201,203,209],{"type":48,"value":178},"Start a new app with ",{"type":42,"tag":65,"props":180,"children":182},{"className":181},[],[183],{"type":48,"value":184},"native init",{"type":48,"value":186}," (zero-config: app.zon + src + assets, the CLI generates the build graph), or copy ",{"type":42,"tag":65,"props":188,"children":190},{"className":189},[],[191],{"type":48,"value":192},"examples\u002Fhabits\u002F",{"type":48,"value":194}," (smallest): change the name\u002Fid in app.zon and ",{"type":42,"tag":65,"props":196,"children":198},{"className":197},[],[199],{"type":48,"value":200},"assets\u002F",{"type":48,"value":202}," copies verbatim — there are no build files to edit. The ",{"type":42,"tag":65,"props":204,"children":206},{"className":205},[],[207],{"type":48,"value":208},"native dev|test|build",{"type":48,"value":210}," verbs drive any app directory shaped this way.",{"type":42,"tag":212,"props":213,"children":215},"h2",{"id":214},"app-wiring",[216],{"type":48,"value":217},"App wiring",{"type":42,"tag":219,"props":220,"children":224},"pre",{"className":221,"code":222,"language":18,"meta":223,"style":223},"language-zig shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const HabitsApp = native_sdk.UiApp(Model, Msg);\n\npub fn main(init: std.process.Init) !void {\n    \u002F\u002F `create` heap-allocates the multi-MB app struct and constructs the\n    \u002F\u002F Model in place — neither ever rides the stack (avoid `App.init(alloc,\n    \u002F\u002F model, ...)`: its by-value Model is a stack-overflow trap once the\n    \u002F\u002F Model grows).\n    const app_state = try HabitsApp.create(std.heap.page_allocator, .{\n        .name = \"habits\",\n        .scene = shell_scene,             \u002F\u002F one window, one gpu_surface view\n        .canvas_label = \"habits-canvas\",  \u002F\u002F must match the ShellView label\n        .update = update,\n        .markup = .{\n            .source = @embedFile(\"habits.native\"),\n            .watch_path = \"src\u002Fhabits.native\", \u002F\u002F dev hot reload; omit in release\n            .io = init.io,\n        },\n    });\n    defer app_state.destroy();\n    app_state.model = initialModel(); \u002F\u002F boot state: assign through the pointer\n    try runner.runWithOptions(app_state.app(), .{ ... }, init);\n}\n","",[225],{"type":42,"tag":65,"props":226,"children":227},{"__ignoreMap":223},[228,239,249,258,267,276,285,294,303,312,321,330,339,348,357,366,375,384,393,402,411,420],{"type":42,"tag":229,"props":230,"children":233},"span",{"class":231,"line":232},"line",1,[234],{"type":42,"tag":229,"props":235,"children":236},{},[237],{"type":48,"value":238},"const HabitsApp = native_sdk.UiApp(Model, Msg);\n",{"type":42,"tag":229,"props":240,"children":242},{"class":231,"line":241},2,[243],{"type":42,"tag":229,"props":244,"children":246},{"emptyLinePlaceholder":245},true,[247],{"type":48,"value":248},"\n",{"type":42,"tag":229,"props":250,"children":252},{"class":231,"line":251},3,[253],{"type":42,"tag":229,"props":254,"children":255},{},[256],{"type":48,"value":257},"pub fn main(init: std.process.Init) !void {\n",{"type":42,"tag":229,"props":259,"children":261},{"class":231,"line":260},4,[262],{"type":42,"tag":229,"props":263,"children":264},{},[265],{"type":48,"value":266},"    \u002F\u002F `create` heap-allocates the multi-MB app struct and constructs the\n",{"type":42,"tag":229,"props":268,"children":270},{"class":231,"line":269},5,[271],{"type":42,"tag":229,"props":272,"children":273},{},[274],{"type":48,"value":275},"    \u002F\u002F Model in place — neither ever rides the stack (avoid `App.init(alloc,\n",{"type":42,"tag":229,"props":277,"children":279},{"class":231,"line":278},6,[280],{"type":42,"tag":229,"props":281,"children":282},{},[283],{"type":48,"value":284},"    \u002F\u002F model, ...)`: its by-value Model is a stack-overflow trap once the\n",{"type":42,"tag":229,"props":286,"children":288},{"class":231,"line":287},7,[289],{"type":42,"tag":229,"props":290,"children":291},{},[292],{"type":48,"value":293},"    \u002F\u002F Model grows).\n",{"type":42,"tag":229,"props":295,"children":297},{"class":231,"line":296},8,[298],{"type":42,"tag":229,"props":299,"children":300},{},[301],{"type":48,"value":302},"    const app_state = try HabitsApp.create(std.heap.page_allocator, .{\n",{"type":42,"tag":229,"props":304,"children":306},{"class":231,"line":305},9,[307],{"type":42,"tag":229,"props":308,"children":309},{},[310],{"type":48,"value":311},"        .name = \"habits\",\n",{"type":42,"tag":229,"props":313,"children":315},{"class":231,"line":314},10,[316],{"type":42,"tag":229,"props":317,"children":318},{},[319],{"type":48,"value":320},"        .scene = shell_scene,             \u002F\u002F one window, one gpu_surface view\n",{"type":42,"tag":229,"props":322,"children":324},{"class":231,"line":323},11,[325],{"type":42,"tag":229,"props":326,"children":327},{},[328],{"type":48,"value":329},"        .canvas_label = \"habits-canvas\",  \u002F\u002F must match the ShellView label\n",{"type":42,"tag":229,"props":331,"children":333},{"class":231,"line":332},12,[334],{"type":42,"tag":229,"props":335,"children":336},{},[337],{"type":48,"value":338},"        .update = update,\n",{"type":42,"tag":229,"props":340,"children":342},{"class":231,"line":341},13,[343],{"type":42,"tag":229,"props":344,"children":345},{},[346],{"type":48,"value":347},"        .markup = .{\n",{"type":42,"tag":229,"props":349,"children":351},{"class":231,"line":350},14,[352],{"type":42,"tag":229,"props":353,"children":354},{},[355],{"type":48,"value":356},"            .source = @embedFile(\"habits.native\"),\n",{"type":42,"tag":229,"props":358,"children":360},{"class":231,"line":359},15,[361],{"type":42,"tag":229,"props":362,"children":363},{},[364],{"type":48,"value":365},"            .watch_path = \"src\u002Fhabits.native\", \u002F\u002F dev hot reload; omit in release\n",{"type":42,"tag":229,"props":367,"children":369},{"class":231,"line":368},16,[370],{"type":42,"tag":229,"props":371,"children":372},{},[373],{"type":48,"value":374},"            .io = init.io,\n",{"type":42,"tag":229,"props":376,"children":378},{"class":231,"line":377},17,[379],{"type":42,"tag":229,"props":380,"children":381},{},[382],{"type":48,"value":383},"        },\n",{"type":42,"tag":229,"props":385,"children":387},{"class":231,"line":386},18,[388],{"type":42,"tag":229,"props":389,"children":390},{},[391],{"type":48,"value":392},"    });\n",{"type":42,"tag":229,"props":394,"children":396},{"class":231,"line":395},19,[397],{"type":42,"tag":229,"props":398,"children":399},{},[400],{"type":48,"value":401},"    defer app_state.destroy();\n",{"type":42,"tag":229,"props":403,"children":405},{"class":231,"line":404},20,[406],{"type":42,"tag":229,"props":407,"children":408},{},[409],{"type":48,"value":410},"    app_state.model = initialModel(); \u002F\u002F boot state: assign through the pointer\n",{"type":42,"tag":229,"props":412,"children":414},{"class":231,"line":413},21,[415],{"type":42,"tag":229,"props":416,"children":417},{},[418],{"type":48,"value":419},"    try runner.runWithOptions(app_state.app(), .{ ... }, init);\n",{"type":42,"tag":229,"props":421,"children":423},{"class":231,"line":422},22,[424],{"type":42,"tag":229,"props":425,"children":426},{},[427],{"type":48,"value":428},"}\n",{"type":42,"tag":51,"props":430,"children":431},{},[432,434,440,442,448,450,455,457,463,465,471],{"type":48,"value":433},"(",{"type":42,"tag":65,"props":435,"children":437},{"className":436},[],[438],{"type":48,"value":439},"create",{"type":48,"value":441}," requires every Model field to carry a default; the model starts as ",{"type":42,"tag":65,"props":443,"children":445},{"className":444},[],[446],{"type":48,"value":447},".{}",{"type":48,"value":449}," and boot state is assigned through the returned pointer. Tests that instantiate the app per fixture should use ",{"type":42,"tag":65,"props":451,"children":453},{"className":452},[],[454],{"type":48,"value":439},{"type":48,"value":456},"\u002F",{"type":42,"tag":65,"props":458,"children":460},{"className":459},[],[461],{"type":48,"value":462},"destroy",{"type":48,"value":464}," too — a runtime-built Model passed to ",{"type":42,"tag":65,"props":466,"children":468},{"className":467},[],[469],{"type":48,"value":470},"init",{"type":48,"value":472}," by value crashes the test stack once models get large.)",{"type":42,"tag":51,"props":474,"children":475},{},[476,478,484,486,492,494,499,501,507],{"type":48,"value":477},"The runtime owns the loop: install on first GPU frame, presentation, resize, pointer\u002Fkeyboard dispatch into ",{"type":42,"tag":65,"props":479,"children":481},{"className":480},[],[482],{"type":48,"value":483},"update",{"type":48,"value":485}," + rebuild. With ",{"type":42,"tag":65,"props":487,"children":489},{"className":488},[],[490],{"type":48,"value":491},"watch_path",{"type":48,"value":493}," set, editing the ",{"type":42,"tag":65,"props":495,"children":497},{"className":496},[],[498],{"type":48,"value":147},{"type":48,"value":500}," file while the app runs hot-reloads the view within ~2s, preserving model state and widget ids; parse failures keep the last good view and set ",{"type":42,"tag":65,"props":502,"children":504},{"className":503},[],[505],{"type":48,"value":506},"app_state.markup_diagnostic",{"type":48,"value":508}," (line\u002Fcolumn\u002Fmessage).",{"type":42,"tag":51,"props":510,"children":511},{},[512,518,520,526,528,533,535,541],{"type":42,"tag":513,"props":514,"children":515},"strong",{},[516],{"type":48,"value":517},"Release: compile the markup at comptime.",{"type":48,"value":519}," ",{"type":42,"tag":65,"props":521,"children":523},{"className":522},[],[524],{"type":48,"value":525},"canvas.CompiledMarkupView(Model, Msg, source).build",{"type":48,"value":527}," parses the ",{"type":42,"tag":65,"props":529,"children":531},{"className":530},[],[532],{"type":48,"value":147},{"type":48,"value":534}," source entirely at compile time and produces the identical tree (same ids, handlers, dispatch) with no parser in the binary; markup or binding mistakes become compile errors with line\u002Fcolumn. Hand it to ",{"type":42,"tag":65,"props":536,"children":538},{"className":537},[],[539],{"type":48,"value":540},".view",{"type":48,"value":542},", and gate the runtime engine per build mode:",{"type":42,"tag":219,"props":544,"children":546},{"className":221,"code":545,"language":18,"meta":223,"style":223},"const dev = @import(\"builtin\").mode == .Debug;\nconst App = native_sdk.UiAppWithFeatures(Model, Msg, .{ .runtime_markup = dev });\nconst CompiledView = canvas.CompiledMarkupView(Model, Msg, @embedFile(\"habits.native\"));\n\u002F\u002F options:\n.view = CompiledView.build,\n.markup = if (dev) .{ .source = ..., .watch_path = \"src\u002Fhabits.native\", .io = init.io } else null,\n",[547],{"type":42,"tag":65,"props":548,"children":549},{"__ignoreMap":223},[550,558,566,574,582,590],{"type":42,"tag":229,"props":551,"children":552},{"class":231,"line":232},[553],{"type":42,"tag":229,"props":554,"children":555},{},[556],{"type":48,"value":557},"const dev = @import(\"builtin\").mode == .Debug;\n",{"type":42,"tag":229,"props":559,"children":560},{"class":231,"line":241},[561],{"type":42,"tag":229,"props":562,"children":563},{},[564],{"type":48,"value":565},"const App = native_sdk.UiAppWithFeatures(Model, Msg, .{ .runtime_markup = dev });\n",{"type":42,"tag":229,"props":567,"children":568},{"class":231,"line":251},[569],{"type":42,"tag":229,"props":570,"children":571},{},[572],{"type":48,"value":573},"const CompiledView = canvas.CompiledMarkupView(Model, Msg, @embedFile(\"habits.native\"));\n",{"type":42,"tag":229,"props":575,"children":576},{"class":231,"line":260},[577],{"type":42,"tag":229,"props":578,"children":579},{},[580],{"type":48,"value":581},"\u002F\u002F options:\n",{"type":42,"tag":229,"props":583,"children":584},{"class":231,"line":269},[585],{"type":42,"tag":229,"props":586,"children":587},{},[588],{"type":48,"value":589},".view = CompiledView.build,\n",{"type":42,"tag":229,"props":591,"children":592},{"class":231,"line":278},[593],{"type":42,"tag":229,"props":594,"children":595},{},[596],{"type":48,"value":597},".markup = if (dev) .{ .source = ..., .watch_path = \"src\u002Fhabits.native\", .io = init.io } else null,\n",{"type":42,"tag":51,"props":599,"children":600},{},[601,603,609],{"type":48,"value":602},"With both set (dev), the compiled view renders until the watched file first changes, then the interpreter hot-reloads it. See ",{"type":42,"tag":65,"props":604,"children":606},{"className":605},[],[607],{"type":48,"value":608},"examples\u002Fhabits",{"type":48,"value":610}," for the full pattern.",{"type":42,"tag":612,"props":613,"children":615},"h3",{"id":614},"webview-panes-canvas-live-web-content-in-one-window",[616],{"type":48,"value":617},"Webview panes: canvas + live web content in one window",{"type":42,"tag":51,"props":619,"children":620},{},[621,623,629],{"type":48,"value":622},"Declare the webview in the scene next to the gpu_surface (parent it to the canvas view), reserve its region with an empty panel carrying a semantics label, and let ",{"type":42,"tag":65,"props":624,"children":626},{"className":625},[],[627],{"type":48,"value":628},"Options.web_panes",{"type":48,"value":630}," snap the webview to that widget's layout frame while the model drives navigation:",{"type":42,"tag":219,"props":632,"children":634},{"className":221,"code":633,"language":18,"meta":223,"style":223},"const shell_views = [_]native_sdk.ShellView{\n    .{ .label = \"app-canvas\", .kind = .gpu_surface, .fill = true, .gpu_backend = .metal },\n    .{ .label = \"preview\", .kind = .webview, .parent = \"app-canvas\", .url = \"https:\u002F\u002Fexample.com\u002F\", .x = 240, .y = 76, .width = 704, .height = 548 },\n};\n\u002F\u002F view: ui.panel(.{ .grow = 1, .semantics = .{ .label = \"preview-pane\" } }, .{})\nfn panes(model: *const Model, out: []App.WebViewPane) usize {\n    out[0] = .{ .label = \"preview\", .anchor = \"preview-pane\", .url = model.url(), .reload_token = model.reload_token };\n    return 1;\n}\n\u002F\u002F options: .web_panes = panes,\n",[635],{"type":42,"tag":65,"props":636,"children":637},{"__ignoreMap":223},[638,646,654,662,670,678,686,694,702,709],{"type":42,"tag":229,"props":639,"children":640},{"class":231,"line":232},[641],{"type":42,"tag":229,"props":642,"children":643},{},[644],{"type":48,"value":645},"const shell_views = [_]native_sdk.ShellView{\n",{"type":42,"tag":229,"props":647,"children":648},{"class":231,"line":241},[649],{"type":42,"tag":229,"props":650,"children":651},{},[652],{"type":48,"value":653},"    .{ .label = \"app-canvas\", .kind = .gpu_surface, .fill = true, .gpu_backend = .metal },\n",{"type":42,"tag":229,"props":655,"children":656},{"class":231,"line":251},[657],{"type":42,"tag":229,"props":658,"children":659},{},[660],{"type":48,"value":661},"    .{ .label = \"preview\", .kind = .webview, .parent = \"app-canvas\", .url = \"https:\u002F\u002Fexample.com\u002F\", .x = 240, .y = 76, .width = 704, .height = 548 },\n",{"type":42,"tag":229,"props":663,"children":664},{"class":231,"line":260},[665],{"type":42,"tag":229,"props":666,"children":667},{},[668],{"type":48,"value":669},"};\n",{"type":42,"tag":229,"props":671,"children":672},{"class":231,"line":269},[673],{"type":42,"tag":229,"props":674,"children":675},{},[676],{"type":48,"value":677},"\u002F\u002F view: ui.panel(.{ .grow = 1, .semantics = .{ .label = \"preview-pane\" } }, .{})\n",{"type":42,"tag":229,"props":679,"children":680},{"class":231,"line":278},[681],{"type":42,"tag":229,"props":682,"children":683},{},[684],{"type":48,"value":685},"fn panes(model: *const Model, out: []App.WebViewPane) usize {\n",{"type":42,"tag":229,"props":687,"children":688},{"class":231,"line":287},[689],{"type":42,"tag":229,"props":690,"children":691},{},[692],{"type":48,"value":693},"    out[0] = .{ .label = \"preview\", .anchor = \"preview-pane\", .url = model.url(), .reload_token = model.reload_token };\n",{"type":42,"tag":229,"props":695,"children":696},{"class":231,"line":296},[697],{"type":42,"tag":229,"props":698,"children":699},{},[700],{"type":48,"value":701},"    return 1;\n",{"type":42,"tag":229,"props":703,"children":704},{"class":231,"line":305},[705],{"type":42,"tag":229,"props":706,"children":707},{},[708],{"type":48,"value":428},{"type":42,"tag":229,"props":710,"children":711},{"class":231,"line":314},[712],{"type":42,"tag":229,"props":713,"children":714},{},[715],{"type":48,"value":716},"\u002F\u002F options: .web_panes = panes,\n",{"type":42,"tag":51,"props":718,"children":719},{},[720,722,728,730,736,738,744,746,752],{"type":48,"value":721},"URL changes navigate; bumping ",{"type":42,"tag":65,"props":723,"children":725},{"className":724},[],[726],{"type":48,"value":727},"reload_token",{"type":48,"value":729}," reloads the same URL (the CenterPane\u002FPreview-tab shape). Pane URLs must pass ",{"type":42,"tag":65,"props":731,"children":733},{"className":732},[],[734],{"type":48,"value":735},"security.navigation.allowed_origins",{"type":48,"value":737},". Panes reconcile against the runtime's live webview state on every rebuild and presented frame, so shell relayouts cannot detach them. ",{"type":42,"tag":65,"props":739,"children":741},{"className":740},[],[742],{"type":48,"value":743},"examples\u002Fcanvas-preview",{"type":48,"value":745}," is the live reference; ",{"type":42,"tag":65,"props":747,"children":749},{"className":748},[],[750],{"type":48,"value":751},"zig build test-canvas-preview-smoke",{"type":48,"value":753}," verifies it.",{"type":42,"tag":612,"props":755,"children":757},{"id":756},"menu-bar-extra-status-item",[758],{"type":48,"value":759},"Menu-bar extra (status item)",{"type":42,"tag":51,"props":761,"children":762},{},[763,769,771,777,779,785,787,793],{"type":42,"tag":65,"props":764,"children":766},{"className":765},[],[767],{"type":48,"value":768},"Options.status_item",{"type":48,"value":770}," installs a macOS ",{"type":42,"tag":65,"props":772,"children":774},{"className":773},[],[775],{"type":48,"value":776},"NSStatusItem",{"type":48,"value":778}," once, on the installing frame; its menu items dispatch commands through the same ",{"type":42,"tag":65,"props":780,"children":782},{"className":781},[],[783],{"type":48,"value":784},"on_command",{"type":48,"value":786}," mapping the toolbar and menus use (source ",{"type":42,"tag":65,"props":788,"children":790},{"className":789},[],[791],{"type":48,"value":792},".tray",{"type":48,"value":794},"):",{"type":42,"tag":219,"props":796,"children":798},{"className":221,"code":797,"language":18,"meta":223,"style":223},".status_item = .{ .title = \"ZN\", .tooltip = \"My App\", .items = &.{\n    .{ .id = 1, .label = \"Refresh\", .command = \"app.refresh\" },\n    .{ .separator = true },\n    .{ .id = 2, .label = \"Quit\", .command = \"app.quit\" },\n} },\n",[799],{"type":42,"tag":65,"props":800,"children":801},{"__ignoreMap":223},[802,810,818,826,834],{"type":42,"tag":229,"props":803,"children":804},{"class":231,"line":232},[805],{"type":42,"tag":229,"props":806,"children":807},{},[808],{"type":48,"value":809},".status_item = .{ .title = \"ZN\", .tooltip = \"My App\", .items = &.{\n",{"type":42,"tag":229,"props":811,"children":812},{"class":231,"line":241},[813],{"type":42,"tag":229,"props":814,"children":815},{},[816],{"type":48,"value":817},"    .{ .id = 1, .label = \"Refresh\", .command = \"app.refresh\" },\n",{"type":42,"tag":229,"props":819,"children":820},{"class":231,"line":251},[821],{"type":42,"tag":229,"props":822,"children":823},{},[824],{"type":48,"value":825},"    .{ .separator = true },\n",{"type":42,"tag":229,"props":827,"children":828},{"class":231,"line":260},[829],{"type":42,"tag":229,"props":830,"children":831},{},[832],{"type":48,"value":833},"    .{ .id = 2, .label = \"Quit\", .command = \"app.quit\" },\n",{"type":42,"tag":229,"props":835,"children":836},{"class":231,"line":269},[837],{"type":42,"tag":229,"props":838,"children":839},{},[840],{"type":48,"value":841},"} },\n",{"type":42,"tag":51,"props":843,"children":844},{},[845,847,853,855,861,863,869,871,877,879,884],{"type":48,"value":846},"For a LIVE menu-bar extra (an open-count badge in the title, a latest-items dropdown), add ",{"type":42,"tag":65,"props":848,"children":850},{"className":849},[],[851],{"type":48,"value":852},"Options.status_item_fn",{"type":48,"value":854}," — the ",{"type":42,"tag":65,"props":856,"children":858},{"className":857},[],[859],{"type":48,"value":860},"web_panes",{"type":48,"value":862}," pattern: consulted on install and after every rebuild, re-applied only when its output changed (title and menu patch independently; the static ",{"type":42,"tag":65,"props":864,"children":866},{"className":865},[],[867],{"type":48,"value":868},"status_item",{"type":48,"value":870}," keeps icon\u002Ftooltip). Format derived strings into the provided scratch; item ",{"type":42,"tag":65,"props":872,"children":874},{"className":873},[],[875],{"type":48,"value":876},"command",{"type":48,"value":878},"s dispatch through ",{"type":42,"tag":65,"props":880,"children":882},{"className":881},[],[883],{"type":48,"value":784},{"type":48,"value":885}," exactly like static items:",{"type":42,"tag":219,"props":887,"children":889},{"className":221,"code":888,"language":18,"meta":223,"style":223},"fn statusItem(model: *const Model, scratch: *App.StatusItemScratch) App.StatusItemState {\n    const title = std.fmt.bufPrint(&scratch.title_buffer, \"ZN {d}\", .{model.open_count}) catch \"ZN\";\n    scratch.items[0] = .{ .id = 1, .label = \"Refresh\", .command = \"app.refresh\" };\n    var count: usize = 1;\n    for (model.latest(), 0..) |issue, i| { \u002F\u002F per-row commands: map \"issue.select.N\" in on_command\n        scratch.items[count] = .{ .id = @intCast(10 + i), .label = issue.title, .command = issue.select_command };\n        count += 1;\n    }\n    return .{ .title = title, .items = scratch.items[0..count] };\n}\n\u002F\u002F options: .status_item_fn = statusItem,\n",[890],{"type":42,"tag":65,"props":891,"children":892},{"__ignoreMap":223},[893,901,909,917,925,933,941,949,957,965,972],{"type":42,"tag":229,"props":894,"children":895},{"class":231,"line":232},[896],{"type":42,"tag":229,"props":897,"children":898},{},[899],{"type":48,"value":900},"fn statusItem(model: *const Model, scratch: *App.StatusItemScratch) App.StatusItemState {\n",{"type":42,"tag":229,"props":902,"children":903},{"class":231,"line":241},[904],{"type":42,"tag":229,"props":905,"children":906},{},[907],{"type":48,"value":908},"    const title = std.fmt.bufPrint(&scratch.title_buffer, \"ZN {d}\", .{model.open_count}) catch \"ZN\";\n",{"type":42,"tag":229,"props":910,"children":911},{"class":231,"line":251},[912],{"type":42,"tag":229,"props":913,"children":914},{},[915],{"type":48,"value":916},"    scratch.items[0] = .{ .id = 1, .label = \"Refresh\", .command = \"app.refresh\" };\n",{"type":42,"tag":229,"props":918,"children":919},{"class":231,"line":260},[920],{"type":42,"tag":229,"props":921,"children":922},{},[923],{"type":48,"value":924},"    var count: usize = 1;\n",{"type":42,"tag":229,"props":926,"children":927},{"class":231,"line":269},[928],{"type":42,"tag":229,"props":929,"children":930},{},[931],{"type":48,"value":932},"    for (model.latest(), 0..) |issue, i| { \u002F\u002F per-row commands: map \"issue.select.N\" in on_command\n",{"type":42,"tag":229,"props":934,"children":935},{"class":231,"line":278},[936],{"type":42,"tag":229,"props":937,"children":938},{},[939],{"type":48,"value":940},"        scratch.items[count] = .{ .id = @intCast(10 + i), .label = issue.title, .command = issue.select_command };\n",{"type":42,"tag":229,"props":942,"children":943},{"class":231,"line":287},[944],{"type":42,"tag":229,"props":945,"children":946},{},[947],{"type":48,"value":948},"        count += 1;\n",{"type":42,"tag":229,"props":950,"children":951},{"class":231,"line":296},[952],{"type":42,"tag":229,"props":953,"children":954},{},[955],{"type":48,"value":956},"    }\n",{"type":42,"tag":229,"props":958,"children":959},{"class":231,"line":305},[960],{"type":42,"tag":229,"props":961,"children":962},{},[963],{"type":48,"value":964},"    return .{ .title = title, .items = scratch.items[0..count] };\n",{"type":42,"tag":229,"props":966,"children":967},{"class":231,"line":314},[968],{"type":42,"tag":229,"props":969,"children":970},{},[971],{"type":48,"value":428},{"type":42,"tag":229,"props":973,"children":974},{"class":231,"line":323},[975],{"type":42,"tag":229,"props":976,"children":977},{},[978],{"type":48,"value":979},"\u002F\u002F options: .status_item_fn = statusItem,\n",{"type":42,"tag":51,"props":981,"children":982},{},[983,985,990],{"type":48,"value":984},"Title updates retitle the live ",{"type":42,"tag":65,"props":986,"children":988},{"className":987},[],[989],{"type":48,"value":776},{"type":48,"value":991}," button without re-creating it; platforms without a tray-title seam keep menu updates and log the title gap once.",{"type":42,"tag":612,"props":993,"children":995},{"id":994},"native-scrolling-macos",[996],{"type":48,"value":997},"Native scrolling (macOS)",{"type":42,"tag":51,"props":999,"children":1000},{},[1001,1003,1009,1011,1017,1019,1025,1027,1033,1035,1041,1043,1049],{"type":48,"value":1002},"Zero app code: on macOS every non-virtualized ",{"type":42,"tag":65,"props":1004,"children":1006},{"className":1005},[],[1007],{"type":48,"value":1008},"scroll",{"type":48,"value":1010}," region — and every windowed virtual list (",{"type":42,"tag":65,"props":1012,"children":1014},{"className":1013},[],[1015],{"type":48,"value":1016},"ui.virtualList",{"type":48,"value":1018},"), whose driver content size is the full virtual extent — is driven by an invisible ",{"type":42,"tag":65,"props":1020,"children":1022},{"className":1021},[],[1023],{"type":48,"value":1024},"NSScrollView",{"type":48,"value":1026}," — OS momentum and the system overlay scrollbar — while the engine renders the content. ",{"type":42,"tag":65,"props":1028,"children":1030},{"className":1029},[],[1031],{"type":48,"value":1032},"widget.value",{"type":48,"value":1034}," stays the offset of record, so the rebuild reconcile rule (\"user offset survives rebuilds until the source offset changes\"), automation snapshot offsets (",{"type":42,"tag":65,"props":1036,"children":1038},{"className":1037},[],[1039],{"type":48,"value":1040},"scroll=[offset=..]",{"type":48,"value":1042},"), and ",{"type":42,"tag":65,"props":1044,"children":1046},{"className":1045},[],[1047],{"type":48,"value":1048},"Options.sync",{"type":48,"value":1050}," all work exactly as before; the engine-drawn scrollbar simply stops painting for natively driven regions. Programmatic scrolls still work: change the source offset (or scroll via keyboard\u002Fautomation) and the runtime pushes it into the native scroller. GTK\u002FWin32 and mobile embeds keep the engine's wheel physics unchanged. Nested-scroll saturation handoff (inner region exhausted, outer continues) is per-region native today: the inner region stops at its edge like a standalone scroller.",{"type":42,"tag":51,"props":1052,"children":1053},{},[1054,1059,1061,1067,1069,1074,1076,1082,1084,1090,1092,1098,1100,1106,1108,1114],{"type":42,"tag":513,"props":1055,"children":1056},{},[1057],{"type":48,"value":1058},"Overscroll is off by default, per region, on both paths.",{"type":48,"value":1060}," Scroll regions pin at their content edges — the native scroller gets non-elastic edges, the engine's wheel\u002Fkinetic physics clamp, and kinetic motion stops cleanly at the boundary. Bouncing is a per-region opt-in: ",{"type":42,"tag":65,"props":1062,"children":1064},{"className":1063},[],[1065],{"type":48,"value":1066},"overscroll=\"rubber_band\"",{"type":48,"value":1068}," in markup (the ",{"type":42,"tag":65,"props":1070,"children":1072},{"className":1071},[],[1073],{"type":48,"value":1008},{"type":48,"value":1075}," element only — the validator rejects it elsewhere with a teaching error) or ",{"type":42,"tag":65,"props":1077,"children":1079},{"className":1078},[],[1080],{"type":48,"value":1081},"ElementOptions.overscroll = .rubber_band",{"type":48,"value":1083}," in Zig views. The ",{"type":42,"tag":65,"props":1085,"children":1087},{"className":1086},[],[1088],{"type":48,"value":1089},"ScrollPhysics.overscroll",{"type":48,"value":1091}," design token (",{"type":42,"tag":65,"props":1093,"children":1095},{"className":1094},[],[1096],{"type":48,"value":1097},"ScrollPhysicsOverrides",{"type":48,"value":1099}," in a theme) flips the app-wide default; per-region values override it, and ",{"type":42,"tag":65,"props":1101,"children":1103},{"className":1102},[],[1104],{"type":48,"value":1105},".none",{"type":48,"value":1107}," pins a region regardless of the token. The rubber-band shape — excursion bound, resistance, spring-back rate — stays themable through the ",{"type":42,"tag":65,"props":1109,"children":1111},{"className":1110},[],[1112],{"type":48,"value":1113},"rubberband_*",{"type":48,"value":1115}," physics tokens.",{"type":42,"tag":612,"props":1117,"children":1119},{"id":1118},"context-menus-one-declared-menu-platform-decided-presentation",[1120],{"type":48,"value":1121},"Context menus: one declared menu, platform-decided presentation",{"type":42,"tag":51,"props":1123,"children":1124},{},[1125,1127,1133,1135,1141,1143,1149,1151,1156],{"type":48,"value":1126},"Authors write ONE menu; the platform decides how it presents. The default is the real OS context menu at the pointer — ",{"type":42,"tag":65,"props":1128,"children":1130},{"className":1129},[],[1131],{"type":48,"value":1132},"NSMenu",{"type":48,"value":1134}," on macOS, ",{"type":42,"tag":65,"props":1136,"children":1138},{"className":1137},[],[1139],{"type":48,"value":1140},"TrackPopupMenu",{"type":48,"value":1142}," on Windows, ",{"type":42,"tag":65,"props":1144,"children":1146},{"className":1145},[],[1147],{"type":48,"value":1148},"GtkPopoverMenu",{"type":48,"value":1150}," on Linux — and the selection dispatches the item's typed ",{"type":42,"tag":65,"props":1152,"children":1154},{"className":1153},[],[1155],{"type":48,"value":97},{"type":48,"value":1157},". On hosts without a native menu presenter (the mobile toolkit hosts and embed hosts today), the SAME declared items present automatically as an anchored canvas surface at the click point, with the standard anchored-surface behavior (Escape and outside-click dismiss, late z-pass, window clipping). Never two authored menus, never a canvas imitation where the OS menu exists.",{"type":42,"tag":51,"props":1159,"children":1160},{},[1161,1163,1169,1171,1177,1178,1184,1186,1192,1194,1199,1201,1207,1209,1215,1217,1223,1224,1230,1231,1237,1239,1244],{"type":48,"value":1162},"Markup declares the menu as a ",{"type":42,"tag":65,"props":1164,"children":1166},{"className":1165},[],[1167],{"type":48,"value":1168},"\u003Ccontext-menu>",{"type":48,"value":1170}," element — a DIRECT child of the pressable element whose right-click it answers (a hit target, or an element with a bound ",{"type":42,"tag":65,"props":1172,"children":1174},{"className":1173},[],[1175],{"type":48,"value":1176},"on-press",{"type":48,"value":456},{"type":42,"tag":65,"props":1179,"children":1181},{"className":1180},[],[1182],{"type":48,"value":1183},"on-hold",{"type":48,"value":1185},"). It is metadata, not content: it renders nothing in the row's flow. Children are ",{"type":42,"tag":65,"props":1187,"children":1189},{"className":1188},[],[1190],{"type":48,"value":1191},"menu-item",{"type":48,"value":1193},"s (",{"type":42,"tag":65,"props":1195,"children":1197},{"className":1196},[],[1198],{"type":48,"value":1176},{"type":48,"value":1200}," required, ",{"type":42,"tag":65,"props":1202,"children":1204},{"className":1203},[],[1205],{"type":48,"value":1206},"disabled",{"type":48,"value":1208}," optional, the text content is the label) and bare ",{"type":42,"tag":65,"props":1210,"children":1212},{"className":1211},[],[1213],{"type":48,"value":1214},"\u003Cseparator\u002F>",{"type":48,"value":1216},"s, with ",{"type":42,"tag":65,"props":1218,"children":1220},{"className":1219},[],[1221],{"type":48,"value":1222},"if",{"type":48,"value":456},{"type":42,"tag":65,"props":1225,"children":1227},{"className":1226},[],[1228],{"type":48,"value":1229},"else",{"type":48,"value":456},{"type":42,"tag":65,"props":1232,"children":1234},{"className":1233},[],[1235],{"type":48,"value":1236},"for",{"type":48,"value":1238}," around them to swap or repeat items — a menu whose items all evaporate at runtime simply declares no menu (the All Notes row pattern). Conditional MENUS are spelled as conditional ITEMS: the ",{"type":42,"tag":65,"props":1240,"children":1242},{"className":1241},[],[1243],{"type":48,"value":1168},{"type":48,"value":1245}," itself takes no attributes and cannot sit behind a structure tag. No submenus: the platform channel carries flat items (label, enabled, separator) only.",{"type":42,"tag":219,"props":1247,"children":1251},{"className":1248,"code":1249,"language":1250,"meta":223,"style":223},"language-html shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Clist-item on-press=\"open_note:{n.id}\" label=\"{n.title}\">\n  \u003Ctext grow=\"1\">{n.title}\u003C\u002Ftext>\n  \u003Ccontext-menu>\n    \u003Cif test=\"{n.deleted}\">\n      \u003Cmenu-item on-press=\"restore_note:{n.id}\">Restore\u003C\u002Fmenu-item>\n      \u003Cmenu-item on-press=\"purge_note:{n.id}\">Delete Permanently\u003C\u002Fmenu-item>\n    \u003C\u002Fif>\n    \u003Celse>\n      \u003Cmenu-item on-press=\"copy_note_id:{n.id}\">Copy\u003C\u002Fmenu-item>\n      \u003Cmenu-item on-press=\"trash_note:{n.id}\">Delete\u003C\u002Fmenu-item>\n    \u003C\u002Felse>\n  \u003C\u002Fcontext-menu>\n\u003C\u002Flist-item>\n","html",[1252],{"type":42,"tag":65,"props":1253,"children":1254},{"__ignoreMap":223},[1255,1323,1380,1396,1434,1488,1541,1557,1572,1625,1678,1693,1709],{"type":42,"tag":229,"props":1256,"children":1257},{"class":231,"line":232},[1258,1264,1270,1276,1281,1286,1292,1296,1301,1305,1309,1314,1318],{"type":42,"tag":229,"props":1259,"children":1261},{"style":1260},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[1262],{"type":48,"value":1263},"\u003C",{"type":42,"tag":229,"props":1265,"children":1267},{"style":1266},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[1268],{"type":48,"value":1269},"list-item",{"type":42,"tag":229,"props":1271,"children":1273},{"style":1272},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[1274],{"type":48,"value":1275}," on-press",{"type":42,"tag":229,"props":1277,"children":1278},{"style":1260},[1279],{"type":48,"value":1280},"=",{"type":42,"tag":229,"props":1282,"children":1283},{"style":1260},[1284],{"type":48,"value":1285},"\"",{"type":42,"tag":229,"props":1287,"children":1289},{"style":1288},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[1290],{"type":48,"value":1291},"open_note:{n.id}",{"type":42,"tag":229,"props":1293,"children":1294},{"style":1260},[1295],{"type":48,"value":1285},{"type":42,"tag":229,"props":1297,"children":1298},{"style":1272},[1299],{"type":48,"value":1300}," label",{"type":42,"tag":229,"props":1302,"children":1303},{"style":1260},[1304],{"type":48,"value":1280},{"type":42,"tag":229,"props":1306,"children":1307},{"style":1260},[1308],{"type":48,"value":1285},{"type":42,"tag":229,"props":1310,"children":1311},{"style":1288},[1312],{"type":48,"value":1313},"{n.title}",{"type":42,"tag":229,"props":1315,"children":1316},{"style":1260},[1317],{"type":48,"value":1285},{"type":42,"tag":229,"props":1319,"children":1320},{"style":1260},[1321],{"type":48,"value":1322},">\n",{"type":42,"tag":229,"props":1324,"children":1325},{"class":231,"line":241},[1326,1331,1335,1340,1344,1348,1353,1357,1362,1367,1372,1376],{"type":42,"tag":229,"props":1327,"children":1328},{"style":1260},[1329],{"type":48,"value":1330},"  \u003C",{"type":42,"tag":229,"props":1332,"children":1333},{"style":1266},[1334],{"type":48,"value":48},{"type":42,"tag":229,"props":1336,"children":1337},{"style":1272},[1338],{"type":48,"value":1339}," grow",{"type":42,"tag":229,"props":1341,"children":1342},{"style":1260},[1343],{"type":48,"value":1280},{"type":42,"tag":229,"props":1345,"children":1346},{"style":1260},[1347],{"type":48,"value":1285},{"type":42,"tag":229,"props":1349,"children":1350},{"style":1288},[1351],{"type":48,"value":1352},"1",{"type":42,"tag":229,"props":1354,"children":1355},{"style":1260},[1356],{"type":48,"value":1285},{"type":42,"tag":229,"props":1358,"children":1359},{"style":1260},[1360],{"type":48,"value":1361},">",{"type":42,"tag":229,"props":1363,"children":1365},{"style":1364},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[1366],{"type":48,"value":1313},{"type":42,"tag":229,"props":1368,"children":1369},{"style":1260},[1370],{"type":48,"value":1371},"\u003C\u002F",{"type":42,"tag":229,"props":1373,"children":1374},{"style":1266},[1375],{"type":48,"value":48},{"type":42,"tag":229,"props":1377,"children":1378},{"style":1260},[1379],{"type":48,"value":1322},{"type":42,"tag":229,"props":1381,"children":1382},{"class":231,"line":251},[1383,1387,1392],{"type":42,"tag":229,"props":1384,"children":1385},{"style":1260},[1386],{"type":48,"value":1330},{"type":42,"tag":229,"props":1388,"children":1389},{"style":1266},[1390],{"type":48,"value":1391},"context-menu",{"type":42,"tag":229,"props":1393,"children":1394},{"style":1260},[1395],{"type":48,"value":1322},{"type":42,"tag":229,"props":1397,"children":1398},{"class":231,"line":260},[1399,1404,1408,1413,1417,1421,1426,1430],{"type":42,"tag":229,"props":1400,"children":1401},{"style":1260},[1402],{"type":48,"value":1403},"    \u003C",{"type":42,"tag":229,"props":1405,"children":1406},{"style":1266},[1407],{"type":48,"value":1222},{"type":42,"tag":229,"props":1409,"children":1410},{"style":1272},[1411],{"type":48,"value":1412}," test",{"type":42,"tag":229,"props":1414,"children":1415},{"style":1260},[1416],{"type":48,"value":1280},{"type":42,"tag":229,"props":1418,"children":1419},{"style":1260},[1420],{"type":48,"value":1285},{"type":42,"tag":229,"props":1422,"children":1423},{"style":1288},[1424],{"type":48,"value":1425},"{n.deleted}",{"type":42,"tag":229,"props":1427,"children":1428},{"style":1260},[1429],{"type":48,"value":1285},{"type":42,"tag":229,"props":1431,"children":1432},{"style":1260},[1433],{"type":48,"value":1322},{"type":42,"tag":229,"props":1435,"children":1436},{"class":231,"line":269},[1437,1442,1446,1450,1454,1458,1463,1467,1471,1476,1480,1484],{"type":42,"tag":229,"props":1438,"children":1439},{"style":1260},[1440],{"type":48,"value":1441},"      \u003C",{"type":42,"tag":229,"props":1443,"children":1444},{"style":1266},[1445],{"type":48,"value":1191},{"type":42,"tag":229,"props":1447,"children":1448},{"style":1272},[1449],{"type":48,"value":1275},{"type":42,"tag":229,"props":1451,"children":1452},{"style":1260},[1453],{"type":48,"value":1280},{"type":42,"tag":229,"props":1455,"children":1456},{"style":1260},[1457],{"type":48,"value":1285},{"type":42,"tag":229,"props":1459,"children":1460},{"style":1288},[1461],{"type":48,"value":1462},"restore_note:{n.id}",{"type":42,"tag":229,"props":1464,"children":1465},{"style":1260},[1466],{"type":48,"value":1285},{"type":42,"tag":229,"props":1468,"children":1469},{"style":1260},[1470],{"type":48,"value":1361},{"type":42,"tag":229,"props":1472,"children":1473},{"style":1364},[1474],{"type":48,"value":1475},"Restore",{"type":42,"tag":229,"props":1477,"children":1478},{"style":1260},[1479],{"type":48,"value":1371},{"type":42,"tag":229,"props":1481,"children":1482},{"style":1266},[1483],{"type":48,"value":1191},{"type":42,"tag":229,"props":1485,"children":1486},{"style":1260},[1487],{"type":48,"value":1322},{"type":42,"tag":229,"props":1489,"children":1490},{"class":231,"line":278},[1491,1495,1499,1503,1507,1511,1516,1520,1524,1529,1533,1537],{"type":42,"tag":229,"props":1492,"children":1493},{"style":1260},[1494],{"type":48,"value":1441},{"type":42,"tag":229,"props":1496,"children":1497},{"style":1266},[1498],{"type":48,"value":1191},{"type":42,"tag":229,"props":1500,"children":1501},{"style":1272},[1502],{"type":48,"value":1275},{"type":42,"tag":229,"props":1504,"children":1505},{"style":1260},[1506],{"type":48,"value":1280},{"type":42,"tag":229,"props":1508,"children":1509},{"style":1260},[1510],{"type":48,"value":1285},{"type":42,"tag":229,"props":1512,"children":1513},{"style":1288},[1514],{"type":48,"value":1515},"purge_note:{n.id}",{"type":42,"tag":229,"props":1517,"children":1518},{"style":1260},[1519],{"type":48,"value":1285},{"type":42,"tag":229,"props":1521,"children":1522},{"style":1260},[1523],{"type":48,"value":1361},{"type":42,"tag":229,"props":1525,"children":1526},{"style":1364},[1527],{"type":48,"value":1528},"Delete Permanently",{"type":42,"tag":229,"props":1530,"children":1531},{"style":1260},[1532],{"type":48,"value":1371},{"type":42,"tag":229,"props":1534,"children":1535},{"style":1266},[1536],{"type":48,"value":1191},{"type":42,"tag":229,"props":1538,"children":1539},{"style":1260},[1540],{"type":48,"value":1322},{"type":42,"tag":229,"props":1542,"children":1543},{"class":231,"line":287},[1544,1549,1553],{"type":42,"tag":229,"props":1545,"children":1546},{"style":1260},[1547],{"type":48,"value":1548},"    \u003C\u002F",{"type":42,"tag":229,"props":1550,"children":1551},{"style":1266},[1552],{"type":48,"value":1222},{"type":42,"tag":229,"props":1554,"children":1555},{"style":1260},[1556],{"type":48,"value":1322},{"type":42,"tag":229,"props":1558,"children":1559},{"class":231,"line":296},[1560,1564,1568],{"type":42,"tag":229,"props":1561,"children":1562},{"style":1260},[1563],{"type":48,"value":1403},{"type":42,"tag":229,"props":1565,"children":1566},{"style":1266},[1567],{"type":48,"value":1229},{"type":42,"tag":229,"props":1569,"children":1570},{"style":1260},[1571],{"type":48,"value":1322},{"type":42,"tag":229,"props":1573,"children":1574},{"class":231,"line":305},[1575,1579,1583,1587,1591,1595,1600,1604,1608,1613,1617,1621],{"type":42,"tag":229,"props":1576,"children":1577},{"style":1260},[1578],{"type":48,"value":1441},{"type":42,"tag":229,"props":1580,"children":1581},{"style":1266},[1582],{"type":48,"value":1191},{"type":42,"tag":229,"props":1584,"children":1585},{"style":1272},[1586],{"type":48,"value":1275},{"type":42,"tag":229,"props":1588,"children":1589},{"style":1260},[1590],{"type":48,"value":1280},{"type":42,"tag":229,"props":1592,"children":1593},{"style":1260},[1594],{"type":48,"value":1285},{"type":42,"tag":229,"props":1596,"children":1597},{"style":1288},[1598],{"type":48,"value":1599},"copy_note_id:{n.id}",{"type":42,"tag":229,"props":1601,"children":1602},{"style":1260},[1603],{"type":48,"value":1285},{"type":42,"tag":229,"props":1605,"children":1606},{"style":1260},[1607],{"type":48,"value":1361},{"type":42,"tag":229,"props":1609,"children":1610},{"style":1364},[1611],{"type":48,"value":1612},"Copy",{"type":42,"tag":229,"props":1614,"children":1615},{"style":1260},[1616],{"type":48,"value":1371},{"type":42,"tag":229,"props":1618,"children":1619},{"style":1266},[1620],{"type":48,"value":1191},{"type":42,"tag":229,"props":1622,"children":1623},{"style":1260},[1624],{"type":48,"value":1322},{"type":42,"tag":229,"props":1626,"children":1627},{"class":231,"line":314},[1628,1632,1636,1640,1644,1648,1653,1657,1661,1666,1670,1674],{"type":42,"tag":229,"props":1629,"children":1630},{"style":1260},[1631],{"type":48,"value":1441},{"type":42,"tag":229,"props":1633,"children":1634},{"style":1266},[1635],{"type":48,"value":1191},{"type":42,"tag":229,"props":1637,"children":1638},{"style":1272},[1639],{"type":48,"value":1275},{"type":42,"tag":229,"props":1641,"children":1642},{"style":1260},[1643],{"type":48,"value":1280},{"type":42,"tag":229,"props":1645,"children":1646},{"style":1260},[1647],{"type":48,"value":1285},{"type":42,"tag":229,"props":1649,"children":1650},{"style":1288},[1651],{"type":48,"value":1652},"trash_note:{n.id}",{"type":42,"tag":229,"props":1654,"children":1655},{"style":1260},[1656],{"type":48,"value":1285},{"type":42,"tag":229,"props":1658,"children":1659},{"style":1260},[1660],{"type":48,"value":1361},{"type":42,"tag":229,"props":1662,"children":1663},{"style":1364},[1664],{"type":48,"value":1665},"Delete",{"type":42,"tag":229,"props":1667,"children":1668},{"style":1260},[1669],{"type":48,"value":1371},{"type":42,"tag":229,"props":1671,"children":1672},{"style":1266},[1673],{"type":48,"value":1191},{"type":42,"tag":229,"props":1675,"children":1676},{"style":1260},[1677],{"type":48,"value":1322},{"type":42,"tag":229,"props":1679,"children":1680},{"class":231,"line":323},[1681,1685,1689],{"type":42,"tag":229,"props":1682,"children":1683},{"style":1260},[1684],{"type":48,"value":1548},{"type":42,"tag":229,"props":1686,"children":1687},{"style":1266},[1688],{"type":48,"value":1229},{"type":42,"tag":229,"props":1690,"children":1691},{"style":1260},[1692],{"type":48,"value":1322},{"type":42,"tag":229,"props":1694,"children":1695},{"class":231,"line":332},[1696,1701,1705],{"type":42,"tag":229,"props":1697,"children":1698},{"style":1260},[1699],{"type":48,"value":1700},"  \u003C\u002F",{"type":42,"tag":229,"props":1702,"children":1703},{"style":1266},[1704],{"type":48,"value":1391},{"type":42,"tag":229,"props":1706,"children":1707},{"style":1260},[1708],{"type":48,"value":1322},{"type":42,"tag":229,"props":1710,"children":1711},{"class":231,"line":341},[1712,1716,1720],{"type":42,"tag":229,"props":1713,"children":1714},{"style":1260},[1715],{"type":48,"value":1371},{"type":42,"tag":229,"props":1717,"children":1718},{"style":1266},[1719],{"type":48,"value":1269},{"type":42,"tag":229,"props":1721,"children":1722},{"style":1260},[1723],{"type":48,"value":1322},{"type":42,"tag":51,"props":1725,"children":1726},{},[1727,1729,1735],{"type":48,"value":1728},"The Zig builder's mirror is ",{"type":42,"tag":65,"props":1730,"children":1732},{"className":1731},[],[1733],{"type":48,"value":1734},"ElementOptions.context_menu",{"type":48,"value":1736}," — per-widget items in the chrome-menu shape with typed messages:",{"type":42,"tag":219,"props":1738,"children":1740},{"className":221,"code":1739,"language":18,"meta":223,"style":223},"ui.listItem(.{\n    .on_press = Msg{ .select = entry.index },\n    .context_menu = &.{\n        .{ .label = \"Open Section\", .msg = Msg{ .select = entry.index } },\n        .{ .separator = true },\n        .{ .label = \"Refresh Dashboard\", .msg = .refresh },\n    },\n}, entry.title)\n",[1741],{"type":42,"tag":65,"props":1742,"children":1743},{"__ignoreMap":223},[1744,1752,1760,1768,1776,1784,1792,1800],{"type":42,"tag":229,"props":1745,"children":1746},{"class":231,"line":232},[1747],{"type":42,"tag":229,"props":1748,"children":1749},{},[1750],{"type":48,"value":1751},"ui.listItem(.{\n",{"type":42,"tag":229,"props":1753,"children":1754},{"class":231,"line":241},[1755],{"type":42,"tag":229,"props":1756,"children":1757},{},[1758],{"type":48,"value":1759},"    .on_press = Msg{ .select = entry.index },\n",{"type":42,"tag":229,"props":1761,"children":1762},{"class":231,"line":251},[1763],{"type":42,"tag":229,"props":1764,"children":1765},{},[1766],{"type":48,"value":1767},"    .context_menu = &.{\n",{"type":42,"tag":229,"props":1769,"children":1770},{"class":231,"line":260},[1771],{"type":42,"tag":229,"props":1772,"children":1773},{},[1774],{"type":48,"value":1775},"        .{ .label = \"Open Section\", .msg = Msg{ .select = entry.index } },\n",{"type":42,"tag":229,"props":1777,"children":1778},{"class":231,"line":269},[1779],{"type":42,"tag":229,"props":1780,"children":1781},{},[1782],{"type":48,"value":1783},"        .{ .separator = true },\n",{"type":42,"tag":229,"props":1785,"children":1786},{"class":231,"line":278},[1787],{"type":42,"tag":229,"props":1788,"children":1789},{},[1790],{"type":48,"value":1791},"        .{ .label = \"Refresh Dashboard\", .msg = .refresh },\n",{"type":42,"tag":229,"props":1793,"children":1794},{"class":231,"line":287},[1795],{"type":42,"tag":229,"props":1796,"children":1797},{},[1798],{"type":48,"value":1799},"    },\n",{"type":42,"tag":229,"props":1801,"children":1802},{"class":231,"line":296},[1803],{"type":42,"tag":229,"props":1804,"children":1805},{},[1806],{"type":48,"value":1807},"}, entry.title)\n",{"type":42,"tag":51,"props":1809,"children":1810},{},[1811,1813,1819,1821,1827,1829,1835,1837,1843],{"type":48,"value":1812},"The deepest declaring widget on the hit route wins; disabled items and separators are fine (",{"type":42,"tag":65,"props":1814,"children":1816},{"className":1815},[],[1817],{"type":48,"value":1818},"enabled = false",{"type":48,"value":1820},", ",{"type":42,"tag":65,"props":1822,"children":1824},{"className":1823},[],[1825],{"type":48,"value":1826},".separator = true",{"type":48,"value":1828},"). Zero-code defaults need no declaration: editable text fields present the standard Cut \u002F Copy \u002F Paste \u002F Select All menu wired to the existing clipboard actions, and a selected static text presents Copy (these defaults are presenter-only — without an OS menu they degrade to the keyboard clipboard paths). Touch long-press is design-noted for the mobile embeds: the iOS host's under-slop ",{"type":42,"tag":65,"props":1830,"children":1832},{"className":1831},[],[1833],{"type":48,"value":1834},"Pending",{"type":48,"value":1836}," touch state is the timer seam, pending a secondary-button leg in the embed ABI and ",{"type":42,"tag":65,"props":1838,"children":1840},{"className":1839},[],[1841],{"type":48,"value":1842},"UIEditMenuInteraction",{"type":48,"value":1844}," presentation.",{"type":42,"tag":51,"props":1846,"children":1847},{},[1848,1850,1856,1858,1864,1866,1872,1874,1880,1882,1888,1890,1896,1898,1904,1905,1911],{"type":48,"value":1849},"Automation drives the native path honestly: snapshots list every widget's declared items in invocation order (",{"type":42,"tag":65,"props":1851,"children":1853},{"className":1852},[],[1854],{"type":48,"value":1855},"context_menu=[\"Rename\",\"Delete\"]",{"type":48,"value":1857},", separators keep their slots, disabled items say so), ",{"type":42,"tag":65,"props":1859,"children":1861},{"className":1860},[],[1862],{"type":48,"value":1863},"widget-context-press \u003Cview> \u003Cid>",{"type":48,"value":1865}," performs the real secondary click (presenting the menu), and ",{"type":42,"tag":65,"props":1867,"children":1869},{"className":1868},[],[1870],{"type":48,"value":1871},"widget-context-menu \u003Cview> \u003Cid> \u003Citem-index>",{"type":48,"value":1873}," invokes an item — the selection dispatches as the same ",{"type":42,"tag":65,"props":1875,"children":1877},{"className":1876},[],[1878],{"type":48,"value":1879},"context_menu_action",{"type":48,"value":1881}," platform event a real pick produces (so it journals and replays), because the OS menu's tracking loop cannot be driven programmatically. Dead invocations fail by name (undeclared menu, index out of range, separator slot, disabled item). ",{"type":42,"tag":65,"props":1883,"children":1885},{"className":1884},[],[1886],{"type":48,"value":1887},"examples\u002Fnotes",{"type":48,"value":1889}," row menus and ",{"type":42,"tag":65,"props":1891,"children":1893},{"className":1892},[],[1894],{"type":48,"value":1895},"examples\u002Fgpu-dashboard",{"type":48,"value":1897}," nav rows carry live menus; ",{"type":42,"tag":65,"props":1899,"children":1901},{"className":1900},[],[1902],{"type":48,"value":1903},"zig build test-example-notes",{"type":48,"value":1820},{"type":42,"tag":65,"props":1906,"children":1908},{"className":1907},[],[1909],{"type":48,"value":1910},"test-example-gpu-dashboard",{"type":48,"value":1912},", and the runtime context-menu suite verify dispatch, the fallback surface, and the verb.",{"type":42,"tag":212,"props":1914,"children":1916},{"id":1915},"elements",[1917],{"type":48,"value":1918},"Elements",{"type":42,"tag":1920,"props":1921,"children":1922},"table",{},[1923,1947],{"type":42,"tag":1924,"props":1925,"children":1926},"thead",{},[1927],{"type":42,"tag":1928,"props":1929,"children":1930},"tr",{},[1931,1937,1942],{"type":42,"tag":1932,"props":1933,"children":1934},"th",{},[1935],{"type":48,"value":1936},"Markup",{"type":42,"tag":1932,"props":1938,"children":1939},{},[1940],{"type":48,"value":1941},"Widget",{"type":42,"tag":1932,"props":1943,"children":1944},{},[1945],{"type":48,"value":1946},"Notes",{"type":42,"tag":1948,"props":1949,"children":1950},"tbody",{},[1951,1981,2038,2066,2095,2152,2193,2236,2278,2374,2425,2453,2512,2563,2705,2784,2946,3061,3160,3182,3243,3290,3376,3441,3518,3555,3609,3695,3848,3933],{"type":42,"tag":1928,"props":1952,"children":1953},{},[1954,1971,1976],{"type":42,"tag":1955,"props":1956,"children":1957},"td",{},[1958,1964,1965],{"type":42,"tag":65,"props":1959,"children":1961},{"className":1960},[],[1962],{"type":48,"value":1963},"row",{"type":48,"value":1820},{"type":42,"tag":65,"props":1966,"children":1968},{"className":1967},[],[1969],{"type":48,"value":1970},"column",{"type":42,"tag":1955,"props":1972,"children":1973},{},[1974],{"type":48,"value":1975},"flex containers",{"type":42,"tag":1955,"props":1977,"children":1978},{},[1979],{"type":48,"value":1980},"main axis horizontal \u002F vertical",{"type":42,"tag":1928,"props":1982,"children":1983},{},[1984,2007,2012],{"type":42,"tag":1955,"props":1985,"children":1986},{},[1987,1993,1994,2000,2001],{"type":42,"tag":65,"props":1988,"children":1990},{"className":1989},[],[1991],{"type":48,"value":1992},"stack",{"type":48,"value":1820},{"type":42,"tag":65,"props":1995,"children":1997},{"className":1996},[],[1998],{"type":48,"value":1999},"panel",{"type":48,"value":1820},{"type":42,"tag":65,"props":2002,"children":2004},{"className":2003},[],[2005],{"type":48,"value":2006},"card",{"type":42,"tag":1955,"props":2008,"children":2009},{},[2010],{"type":48,"value":2011},"overlay containers",{"type":42,"tag":1955,"props":2013,"children":2014},{},[2015,2017,2023,2025,2030,2031,2036],{"type":48,"value":2016},"children stack on top of each other — ",{"type":42,"tag":65,"props":2018,"children":2020},{"className":2019},[],[2021],{"type":48,"value":2022},"gap",{"type":48,"value":2024}," can never space them and is a validation error (put a ",{"type":42,"tag":65,"props":2026,"children":2028},{"className":2027},[],[2029],{"type":48,"value":1970},{"type":48,"value":456},{"type":42,"tag":65,"props":2032,"children":2034},{"className":2033},[],[2035],{"type":48,"value":1963},{"type":48,"value":2037}," inside for flow)",{"type":42,"tag":1928,"props":2039,"children":2040},{},[2041,2049,2054],{"type":42,"tag":1955,"props":2042,"children":2043},{},[2044],{"type":42,"tag":65,"props":2045,"children":2047},{"className":2046},[],[2048],{"type":48,"value":1008},{"type":42,"tag":1955,"props":2050,"children":2051},{},[2052],{"type":48,"value":2053},"scroll_view",{"type":42,"tag":1955,"props":2055,"children":2056},{},[2057,2059,2064],{"type":48,"value":2058},"wrap multiple children in a ",{"type":42,"tag":65,"props":2060,"children":2062},{"className":2061},[],[2063],{"type":48,"value":1970},{"type":48,"value":2065}," inside it",{"type":42,"tag":1928,"props":2067,"children":2068},{},[2069,2085,2090],{"type":42,"tag":1955,"props":2070,"children":2071},{},[2072,2078,2079],{"type":42,"tag":65,"props":2073,"children":2075},{"className":2074},[],[2076],{"type":48,"value":2077},"list",{"type":48,"value":1820},{"type":42,"tag":65,"props":2080,"children":2082},{"className":2081},[],[2083],{"type":48,"value":2084},"grid",{"type":42,"tag":1955,"props":2086,"children":2087},{},[2088],{"type":48,"value":2089},"list, grid",{"type":42,"tag":1955,"props":2091,"children":2092},{},[2093],{"type":48,"value":2094},"vertical stack \u002F cell grid",{"type":42,"tag":1928,"props":2096,"children":2097},{},[2098,2142,2147],{"type":42,"tag":1955,"props":2099,"children":2100},{},[2101,2107,2108,2114,2115,2121,2122,2128,2129,2135,2136],{"type":42,"tag":65,"props":2102,"children":2104},{"className":2103},[],[2105],{"type":48,"value":2106},"tabs",{"type":48,"value":1820},{"type":42,"tag":65,"props":2109,"children":2111},{"className":2110},[],[2112],{"type":48,"value":2113},"toggle-group",{"type":48,"value":1820},{"type":42,"tag":65,"props":2116,"children":2118},{"className":2117},[],[2119],{"type":48,"value":2120},"button-group",{"type":48,"value":1820},{"type":42,"tag":65,"props":2123,"children":2125},{"className":2124},[],[2126],{"type":48,"value":2127},"radio-group",{"type":48,"value":1820},{"type":42,"tag":65,"props":2130,"children":2132},{"className":2131},[],[2133],{"type":48,"value":2134},"breadcrumb",{"type":48,"value":1820},{"type":42,"tag":65,"props":2137,"children":2139},{"className":2138},[],[2140],{"type":48,"value":2141},"pagination",{"type":42,"tag":1955,"props":2143,"children":2144},{},[2145],{"type":48,"value":2146},"row containers",{"type":42,"tag":1955,"props":2148,"children":2149},{},[2150],{"type":48,"value":2151},"children flow horizontally (tab buttons, toggle-buttons, radios, ...)",{"type":42,"tag":1928,"props":2153,"children":2154},{},[2155,2178,2183],{"type":42,"tag":1955,"props":2156,"children":2157},{},[2158,2163,2165,2171,2172],{"type":42,"tag":65,"props":2159,"children":2161},{"className":2160},[],[2162],{"type":48,"value":1920},{"type":48,"value":2164}," > ",{"type":42,"tag":65,"props":2166,"children":2168},{"className":2167},[],[2169],{"type":48,"value":2170},"table-row",{"type":48,"value":2164},{"type":42,"tag":65,"props":2173,"children":2175},{"className":2174},[],[2176],{"type":48,"value":2177},"table-cell",{"type":42,"tag":1955,"props":2179,"children":2180},{},[2181],{"type":48,"value":2182},"table, data_row, data_cell",{"type":42,"tag":1955,"props":2184,"children":2185},{},[2186,2188],{"type":48,"value":2187},"rows only inside a table, cells only inside a row (for\u002Fif wrappers are fine); cells are text leaves, dispatch with ",{"type":42,"tag":65,"props":2189,"children":2191},{"className":2190},[],[2192],{"type":48,"value":1176},{"type":42,"tag":1928,"props":2194,"children":2195},{},[2196,2205,2210],{"type":42,"tag":1955,"props":2197,"children":2198},{},[2199],{"type":42,"tag":65,"props":2200,"children":2202},{"className":2201},[],[2203],{"type":48,"value":2204},"dropdown-menu",{"type":42,"tag":1955,"props":2206,"children":2207},{},[2208],{"type":48,"value":2209},"dropdown_menu",{"type":42,"tag":1955,"props":2211,"children":2212},{},[2213,2215,2220,2222,2228,2230],{"type":48,"value":2214},"vertical menu surface; children are ",{"type":42,"tag":65,"props":2216,"children":2218},{"className":2217},[],[2219],{"type":48,"value":1191},{"type":48,"value":2221},"s. ",{"type":42,"tag":65,"props":2223,"children":2225},{"className":2224},[],[2226],{"type":48,"value":2227},"anchor=\"below|above\"",{"type":48,"value":2229}," floats it against its PARENT's frame (see Pickers): late z-pass above the whole tree, window-clipped, auto-flipping at the window edges, zero flow space. Pair with ",{"type":42,"tag":65,"props":2231,"children":2233},{"className":2232},[],[2234],{"type":48,"value":2235},"on-dismiss",{"type":42,"tag":1928,"props":2237,"children":2238},{},[2239,2248,2252],{"type":42,"tag":1955,"props":2240,"children":2241},{},[2242],{"type":42,"tag":65,"props":2243,"children":2245},{"className":2244},[],[2246],{"type":48,"value":2247},"accordion",{"type":42,"tag":1955,"props":2249,"children":2250},{},[2251],{"type":48,"value":2247},{"type":42,"tag":1955,"props":2253,"children":2254},{},[2255,2257,2262,2264,2270,2272],{"type":48,"value":2256},"header via ",{"type":42,"tag":65,"props":2258,"children":2260},{"className":2259},[],[2261],{"type":48,"value":48},{"type":48,"value":2263}," attr; children show while ",{"type":42,"tag":65,"props":2265,"children":2267},{"className":2266},[],[2268],{"type":48,"value":2269},"selected",{"type":48,"value":2271},", dispatch ",{"type":42,"tag":65,"props":2273,"children":2275},{"className":2274},[],[2276],{"type":48,"value":2277},"on-toggle",{"type":42,"tag":1928,"props":2279,"children":2280},{},[2281,2297,2302],{"type":42,"tag":1955,"props":2282,"children":2283},{},[2284,2290,2291],{"type":42,"tag":65,"props":2285,"children":2287},{"className":2286},[],[2288],{"type":48,"value":2289},"alert",{"type":48,"value":1820},{"type":42,"tag":65,"props":2292,"children":2294},{"className":2293},[],[2295],{"type":48,"value":2296},"bubble",{"type":42,"tag":1955,"props":2298,"children":2299},{},[2300],{"type":48,"value":2301},"surfaces",{"type":42,"tag":1955,"props":2303,"children":2304},{},[2305,2310,2312,2317,2319,2324,2326,2332,2334,2340,2342,2348,2350,2356,2358,2364,2366,2372],{"type":42,"tag":65,"props":2306,"children":2308},{"className":2307},[],[2309],{"type":48,"value":2289},{"type":48,"value":2311}," title via ",{"type":42,"tag":65,"props":2313,"children":2315},{"className":2314},[],[2316],{"type":48,"value":48},{"type":48,"value":2318}," attr; children stack inside. ",{"type":42,"tag":65,"props":2320,"children":2322},{"className":2321},[],[2323],{"type":48,"value":2296},{"type":48,"value":2325}," hugs its message up to 80% of the thread (",{"type":42,"tag":65,"props":2327,"children":2329},{"className":2328},[],[2330],{"type":48,"value":2331},"ghost",{"type":48,"value":2333}," exempt; explicit ",{"type":42,"tag":65,"props":2335,"children":2337},{"className":2336},[],[2338],{"type":48,"value":2339},"width",{"type":48,"value":2341}," wins) and takes one ",{"type":42,"tag":65,"props":2343,"children":2345},{"className":2344},[],[2346],{"type":48,"value":2347},"\u003Creactions>",{"type":48,"value":2349}," child — the reaction pill straddling its bottom edge, one text run, dock via ",{"type":42,"tag":65,"props":2351,"children":2353},{"className":2352},[],[2354],{"type":48,"value":2355},"text-alignment",{"type":48,"value":2357}," (default ",{"type":42,"tag":65,"props":2359,"children":2361},{"className":2360},[],[2362],{"type":48,"value":2363},"end",{"type":48,"value":2365},"); ",{"type":42,"tag":65,"props":2367,"children":2369},{"className":2368},[],[2370],{"type":48,"value":2371},"text=",{"type":48,"value":2373}," on bubble itself is a teaching error (that channel belongs to the pill). Grouped runs are spacing, not vocabulary: 8 gap within a sender's run, 32 between turns",{"type":42,"tag":1928,"props":2375,"children":2376},{},[2377,2400,2405],{"type":42,"tag":1955,"props":2378,"children":2379},{},[2380,2386,2387,2393,2394],{"type":42,"tag":65,"props":2381,"children":2383},{"className":2382},[],[2384],{"type":48,"value":2385},"dialog",{"type":48,"value":1820},{"type":42,"tag":65,"props":2388,"children":2390},{"className":2389},[],[2391],{"type":48,"value":2392},"drawer",{"type":48,"value":1820},{"type":42,"tag":65,"props":2395,"children":2397},{"className":2396},[],[2398],{"type":48,"value":2399},"sheet",{"type":42,"tag":1955,"props":2401,"children":2402},{},[2403],{"type":48,"value":2404},"modal surfaces",{"type":42,"tag":1955,"props":2406,"children":2407},{},[2408,2410,2415,2417,2423],{"type":48,"value":2409},"rendered in place — title via ",{"type":42,"tag":65,"props":2411,"children":2413},{"className":2412},[],[2414],{"type":48,"value":48},{"type":48,"value":2416}," attr, wrap in ",{"type":42,"tag":65,"props":2418,"children":2420},{"className":2419},[],[2421],{"type":48,"value":2422},"\u003Cif>",{"type":48,"value":2424}," to show conditionally",{"type":42,"tag":1928,"props":2426,"children":2427},{},[2428,2437,2441],{"type":42,"tag":1955,"props":2429,"children":2430},{},[2431],{"type":42,"tag":65,"props":2432,"children":2434},{"className":2433},[],[2435],{"type":48,"value":2436},"resizable",{"type":42,"tag":1955,"props":2438,"children":2439},{},[2440],{"type":48,"value":2436},{"type":42,"tag":1955,"props":2442,"children":2443},{},[2444,2446,2451],{"type":48,"value":2445},"engine-managed drag handle; ",{"type":42,"tag":65,"props":2447,"children":2449},{"className":2448},[],[2450],{"type":48,"value":2339},{"type":48,"value":2452}," sets the initial width",{"type":42,"tag":1928,"props":2454,"children":2455},{},[2456,2465,2469],{"type":42,"tag":1955,"props":2457,"children":2458},{},[2459],{"type":42,"tag":65,"props":2460,"children":2462},{"className":2461},[],[2463],{"type":48,"value":2464},"split",{"type":42,"tag":1955,"props":2466,"children":2467},{},[2468],{"type":48,"value":2464},{"type":42,"tag":1955,"props":2470,"children":2471},{},[2472,2474,2480,2482,2488,2490,2495,2497,2503,2505,2510],{"type":48,"value":2473},"two-pane horizontal splitter: exactly two element children (nest splits for more panes), the engine synthesizes the draggable divider between them. ",{"type":42,"tag":65,"props":2475,"children":2477},{"className":2476},[],[2478],{"type":48,"value":2479},"value",{"type":48,"value":2481}," binds the model-owned first-pane fraction (0 lays out at 0.5), ",{"type":42,"tag":65,"props":2483,"children":2485},{"className":2484},[],[2486],{"type":48,"value":2487},"on-resize",{"type":48,"value":2489}," names an f32 Msg variant dispatched with every applied fraction (echo it back through ",{"type":42,"tag":65,"props":2491,"children":2493},{"className":2492},[],[2494],{"type":48,"value":2479},{"type":48,"value":2496}," — see Splitters), ",{"type":42,"tag":65,"props":2498,"children":2500},{"className":2499},[],[2501],{"type":48,"value":2502},"min-width",{"type":48,"value":2504}," on the panes bounds the drag, ",{"type":42,"tag":65,"props":2506,"children":2508},{"className":2507},[],[2509],{"type":48,"value":2022},{"type":48,"value":2511}," sets the divider band thickness. The divider is focusable: Left\u002FRight (Shift for bigger steps) adjust, Home\u002FEnd jump to the clamp edges",{"type":42,"tag":1928,"props":2513,"children":2514},{},[2515,2524,2528],{"type":42,"tag":1955,"props":2516,"children":2517},{},[2518],{"type":42,"tag":65,"props":2519,"children":2521},{"className":2520},[],[2522],{"type":48,"value":2523},"tree",{"type":42,"tag":1955,"props":2525,"children":2526},{},[2527],{"type":48,"value":2523},{"type":42,"tag":1955,"props":2529,"children":2530},{},[2531,2533,2539,2541,2546,2548,2554,2556,2561],{"type":48,"value":2532},"disclosure-tree container (vertical flow): descendant rows carrying ",{"type":42,"tag":65,"props":2534,"children":2536},{"className":2535},[],[2537],{"type":48,"value":2538},"role=\"treeitem\"",{"type":48,"value":2540}," — at ANY nesting depth — form one roving keyboard focus set with the ARIA tree keymap. Up\u002FDown walk visible rows (selection follows focus through each row's ",{"type":42,"tag":65,"props":2542,"children":2544},{"className":2543},[],[2545],{"type":48,"value":1176},{"type":48,"value":2547},"), Left collapses an expanded row or moves to the parent row, Right expands a collapsed row or moves to the first child row, Home\u002FEnd jump to the edges, Enter\u002FSpace activate. Expandable rows bind ",{"type":42,"tag":65,"props":2549,"children":2551},{"className":2550},[],[2552],{"type":48,"value":2553},"expanded",{"type":48,"value":2555}," and ",{"type":42,"tag":65,"props":2557,"children":2559},{"className":2558},[],[2560],{"type":48,"value":2277},{"type":48,"value":2562},"; the model owns selection and expansion (collapsed children are simply not rendered)",{"type":42,"tag":1928,"props":2564,"children":2565},{},[2566,2588,2593],{"type":42,"tag":1955,"props":2567,"children":2568},{},[2569,2574,2575,2581,2582],{"type":42,"tag":65,"props":2570,"children":2572},{"className":2571},[],[2573],{"type":48,"value":48},{"type":48,"value":1820},{"type":42,"tag":65,"props":2576,"children":2578},{"className":2577},[],[2579],{"type":48,"value":2580},"badge",{"type":48,"value":1820},{"type":42,"tag":65,"props":2583,"children":2585},{"className":2584},[],[2586],{"type":48,"value":2587},"tooltip",{"type":42,"tag":1955,"props":2589,"children":2590},{},[2591],{"type":48,"value":2592},"text leaves",{"type":42,"tag":1955,"props":2594,"children":2595},{},[2596,2598,2604,2606,2611,2613,2619,2621,2627,2629,2635,2637,2643,2645,2650,2652,2658,2659,2665,2667,2672,2673,2679,2681,2687,2689,2695,2697,2703],{"type":48,"value":2597},"text content, ",{"type":42,"tag":65,"props":2599,"children":2601},{"className":2600},[],[2602],{"type":48,"value":2603},"{}",{"type":48,"value":2605}," interpolation allowed; ",{"type":42,"tag":65,"props":2607,"children":2609},{"className":2608},[],[2610],{"type":48,"value":48},{"type":48,"value":2612}," line policy via ",{"type":42,"tag":65,"props":2614,"children":2616},{"className":2615},[],[2617],{"type":48,"value":2618},"wrap",{"type":48,"value":2620}," (",{"type":42,"tag":65,"props":2622,"children":2624},{"className":2623},[],[2625],{"type":48,"value":2626},"\"true\"",{"type":48,"value":2628}," word-wraps; ",{"type":42,"tag":65,"props":2630,"children":2632},{"className":2631},[],[2633],{"type":48,"value":2634},"\"false\"",{"type":48,"value":2636},"\u002Funset paint one honest line, overflow eliding by default — ",{"type":42,"tag":65,"props":2638,"children":2640},{"className":2639},[],[2641],{"type":48,"value":2642},"overflow=\"clip\"",{"type":48,"value":2644}," opts out), and ",{"type":42,"tag":65,"props":2646,"children":2648},{"className":2647},[],[2649],{"type":48,"value":48},{"type":48,"value":2651}," alone takes the typography rungs ",{"type":42,"tag":65,"props":2653,"children":2655},{"className":2654},[],[2656],{"type":48,"value":2657},"size=\"heading\"",{"type":48,"value":456},{"type":42,"tag":65,"props":2660,"children":2662},{"className":2661},[],[2663],{"type":48,"value":2664},"size=\"display\"",{"type":48,"value":2666}," (themable token steps above title — section headings, hero stats, timer numerals). ",{"type":42,"tag":65,"props":2668,"children":2670},{"className":2669},[],[2671],{"type":48,"value":2587},{"type":48,"value":157},{"type":42,"tag":65,"props":2674,"children":2676},{"className":2675},[],[2677],{"type":48,"value":2678},"anchor=\"above|below\"",{"type":48,"value":2680}," floats against its parent (the stack wrapping trigger + tooltip, the dropdown pattern) and the RUNTIME owns its visibility — hover intent on the trigger: shows after ",{"type":42,"tag":65,"props":2682,"children":2684},{"className":2683},[],[2685],{"type":48,"value":2686},"tooltip-delay",{"type":48,"value":2688}," ms (default 600; ",{"type":42,"tag":65,"props":2690,"children":2692},{"className":2691},[],[2693],{"type":48,"value":2694},"\"0\"",{"type":48,"value":2696}," = instant) and immediately on keyboard focus; hides on leave, focus departure, Escape, or a press of the trigger (a press also closes the warm window), and a shared 400ms warm window after a pointer-hovered tooltip hides on leave (the only hide that warms) shows the next trigger's tooltip instantly; the model never hears hover. These are shadcn\u002Fui's defaults (Base UI). Without ",{"type":42,"tag":65,"props":2698,"children":2700},{"className":2699},[],[2701],{"type":48,"value":2702},"anchor",{"type":48,"value":2704}," it stays a static leaf that paints whenever the view renders it",{"type":42,"tag":1928,"props":2706,"children":2707},{},[2708,2722,2727],{"type":42,"tag":1955,"props":2709,"children":2710},{},[2711,2716,2717],{"type":42,"tag":65,"props":2712,"children":2714},{"className":2713},[],[2715],{"type":48,"value":48},{"type":48,"value":2164},{"type":42,"tag":65,"props":2718,"children":2720},{"className":2719},[],[2721],{"type":48,"value":229},{"type":42,"tag":1955,"props":2723,"children":2724},{},[2725],{"type":48,"value":2726},"inline styled runs",{"type":42,"tag":1955,"props":2728,"children":2729},{},[2730,2732,2738,2739,2745,2746,2752,2753,2759,2761,2767,2768,2774,2776,2782],{"type":48,"value":2731},"mixed-style text in ONE wrapped paragraph: span children style runs with ",{"type":42,"tag":65,"props":2733,"children":2735},{"className":2734},[],[2736],{"type":48,"value":2737},"weight=\"regular|medium|bold\"",{"type":48,"value":1820},{"type":42,"tag":65,"props":2740,"children":2742},{"className":2741},[],[2743],{"type":48,"value":2744},"mono",{"type":48,"value":1820},{"type":42,"tag":65,"props":2747,"children":2749},{"className":2748},[],[2750],{"type":48,"value":2751},"italic",{"type":48,"value":1820},{"type":42,"tag":65,"props":2754,"children":2756},{"className":2755},[],[2757],{"type":48,"value":2758},"scale",{"type":48,"value":2760}," (a positive multiplier on the paragraph's base size — inline headings, hero stats), ",{"type":42,"tag":65,"props":2762,"children":2764},{"className":2763},[],[2765],{"type":48,"value":2766},"underline",{"type":48,"value":1820},{"type":42,"tag":65,"props":2769,"children":2771},{"className":2770},[],[2772],{"type":48,"value":2773},"foreground",{"type":48,"value":2775}," (token name); ",{"type":42,"tag":65,"props":2777,"children":2779},{"className":2778},[],[2780],{"type":48,"value":2781},"{bindings}",{"type":48,"value":2783}," interpolate inside spans; whitespace between runs collapses to a single space (none = the runs abut); spans do not nest, take no events, and the paragraph announces as one text run — see \"Rich text\"",{"type":42,"tag":1928,"props":2785,"children":2786},{},[2787,2843,2848],{"type":42,"tag":1955,"props":2788,"children":2789},{},[2790,2796,2797,2803,2804,2809,2810,2815,2816,2822,2823,2829,2830,2836,2837],{"type":42,"tag":65,"props":2791,"children":2793},{"className":2792},[],[2794],{"type":48,"value":2795},"button",{"type":48,"value":1820},{"type":42,"tag":65,"props":2798,"children":2800},{"className":2799},[],[2801],{"type":48,"value":2802},"toggle-button",{"type":48,"value":1820},{"type":42,"tag":65,"props":2805,"children":2807},{"className":2806},[],[2808],{"type":48,"value":1269},{"type":48,"value":1820},{"type":42,"tag":65,"props":2811,"children":2813},{"className":2812},[],[2814],{"type":48,"value":1191},{"type":48,"value":1820},{"type":42,"tag":65,"props":2817,"children":2819},{"className":2818},[],[2820],{"type":48,"value":2821},"toggle",{"type":48,"value":1820},{"type":42,"tag":65,"props":2824,"children":2826},{"className":2825},[],[2827],{"type":48,"value":2828},"switch",{"type":48,"value":1820},{"type":42,"tag":65,"props":2831,"children":2833},{"className":2832},[],[2834],{"type":48,"value":2835},"select",{"type":48,"value":1820},{"type":42,"tag":65,"props":2838,"children":2840},{"className":2839},[],[2841],{"type":48,"value":2842},"avatar",{"type":42,"tag":1955,"props":2844,"children":2845},{},[2846],{"type":48,"value":2847},"text-bearing controls",{"type":42,"tag":1955,"props":2849,"children":2850},{},[2851,2853,2858,2859,2864,2865,2870,2872,2877,2879,2885,2887,2893,2895,2900,2902,2907,2909,2914,2916,2922,2924,2929,2931,2936,2938,2944],{"type":48,"value":2852},"label is the text content; ",{"type":42,"tag":65,"props":2854,"children":2856},{"className":2855},[],[2857],{"type":48,"value":2795},{"type":48,"value":1820},{"type":42,"tag":65,"props":2860,"children":2862},{"className":2861},[],[2863],{"type":48,"value":2802},{"type":48,"value":1820},{"type":42,"tag":65,"props":2866,"children":2868},{"className":2867},[],[2869],{"type":48,"value":1269},{"type":48,"value":2871},", and ",{"type":42,"tag":65,"props":2873,"children":2875},{"className":2874},[],[2876],{"type":48,"value":1191},{"type":48,"value":2878}," also take ",{"type":42,"tag":65,"props":2880,"children":2882},{"className":2881},[],[2883],{"type":48,"value":2884},"icon=\"save\"",{"type":48,"value":2886}," — a vector icon drawn inline (buttons\u002Ftoggle-buttons before the label, icon-only when the content is empty: add a ",{"type":42,"tag":65,"props":2888,"children":2890},{"className":2889},[],[2891],{"type":48,"value":2892},"label",{"type":48,"value":2894},"; list\u002Fmenu items as a leading slot), ONE hit target whose icon follows the element's enabled\u002Fdisabled tint (no overlay stacking, no duplicated ",{"type":42,"tag":65,"props":2896,"children":2898},{"className":2897},[],[2899],{"type":48,"value":1176},{"type":48,"value":2901},"); tab strips are ",{"type":42,"tag":65,"props":2903,"children":2905},{"className":2904},[],[2906],{"type":48,"value":2802},{"type":48,"value":2908}," children, so tabs get icons this way; ",{"type":42,"tag":65,"props":2910,"children":2912},{"className":2911},[],[2913],{"type":48,"value":2835},{"type":48,"value":2915}," shows ",{"type":42,"tag":65,"props":2917,"children":2919},{"className":2918},[],[2920],{"type":48,"value":2921},"placeholder",{"type":48,"value":2923}," while empty and dispatches ",{"type":42,"tag":65,"props":2925,"children":2927},{"className":2926},[],[2928],{"type":48,"value":1176},{"type":48,"value":2930},"; ",{"type":42,"tag":65,"props":2932,"children":2934},{"className":2933},[],[2935],{"type":48,"value":2842},{"type":48,"value":2937}," renders initials, or a runtime image via ",{"type":42,"tag":65,"props":2939,"children":2941},{"className":2940},[],[2942],{"type":48,"value":2943},"image=\"{binding}\"",{"type":48,"value":2945}," (see the Images section)",{"type":42,"tag":1928,"props":2947,"children":2948},{},[2949,2979,2984],{"type":42,"tag":1955,"props":2950,"children":2951},{},[2952,2958,2959,2965,2966,2972,2973],{"type":42,"tag":65,"props":2953,"children":2955},{"className":2954},[],[2956],{"type":48,"value":2957},"checkbox",{"type":48,"value":1820},{"type":42,"tag":65,"props":2960,"children":2962},{"className":2961},[],[2963],{"type":48,"value":2964},"radio",{"type":48,"value":1820},{"type":42,"tag":65,"props":2967,"children":2969},{"className":2968},[],[2970],{"type":48,"value":2971},"slider",{"type":48,"value":1820},{"type":42,"tag":65,"props":2974,"children":2976},{"className":2975},[],[2977],{"type":48,"value":2978},"progress",{"type":42,"tag":1955,"props":2980,"children":2981},{},[2982],{"type":48,"value":2983},"value controls",{"type":42,"tag":1955,"props":2985,"children":2986},{},[2987,2993,2994,2999,3001,3007,3009,3015,3017,3022,3024,3029,3031,3036,3038,3044,3046,3051,3053,3059],{"type":42,"tag":65,"props":2988,"children":2990},{"className":2989},[],[2991],{"type":48,"value":2992},"checked",{"type":48,"value":1820},{"type":42,"tag":65,"props":2995,"children":2997},{"className":2996},[],[2998],{"type":48,"value":2479},{"type":48,"value":3000}," (a 0..1 fraction on slider and progress; progress clamps out-of-range values at render, never an error); the checkbox\u002Fradio label rides ",{"type":42,"tag":65,"props":3002,"children":3004},{"className":3003},[],[3005],{"type":48,"value":3006},"text=\"...\"",{"type":48,"value":3008}," — these are not text-bearing elements, so text content is a teaching error (",{"type":42,"tag":65,"props":3010,"children":3012},{"className":3011},[],[3013],{"type":48,"value":3014},"label=",{"type":48,"value":3016}," alone names one for accessibility without a visible label); a slider's ",{"type":42,"tag":65,"props":3018,"children":3020},{"className":3019},[],[3021],{"type":48,"value":2479},{"type":48,"value":3023}," follows the source when it MOVES (model-driven progress renders every rebuild) and keeps the user's drag while the source replays the same value — use ",{"type":42,"tag":65,"props":3025,"children":3027},{"className":3026},[],[3028],{"type":48,"value":2971},{"type":48,"value":3030}," for seek bars, ",{"type":42,"tag":65,"props":3032,"children":3034},{"className":3033},[],[3035],{"type":48,"value":2978},{"type":48,"value":3037}," for display-only; a markup slider's ",{"type":42,"tag":65,"props":3039,"children":3041},{"className":3040},[],[3042],{"type":48,"value":3043},"on-change",{"type":48,"value":3045}," dispatches a PLAIN Msg with no value payload — mirror the applied value into the model with ",{"type":42,"tag":65,"props":3047,"children":3049},{"className":3048},[],[3050],{"type":48,"value":1048},{"type":48,"value":3052}," (the Zig builder's ",{"type":42,"tag":65,"props":3054,"children":3056},{"className":3055},[],[3057],{"type":48,"value":3058},"on_value = Ui.valueMsg(.tag)",{"type":48,"value":3060}," does deliver the applied f32)",{"type":42,"tag":1928,"props":3062,"children":3063},{},[3064,3101,3106],{"type":42,"tag":1955,"props":3065,"children":3066},{},[3067,3073,3074,3080,3081,3087,3088,3094,3095],{"type":42,"tag":65,"props":3068,"children":3070},{"className":3069},[],[3071],{"type":48,"value":3072},"text-field",{"type":48,"value":1820},{"type":42,"tag":65,"props":3075,"children":3077},{"className":3076},[],[3078],{"type":48,"value":3079},"input",{"type":48,"value":1820},{"type":42,"tag":65,"props":3082,"children":3084},{"className":3083},[],[3085],{"type":48,"value":3086},"search-field",{"type":48,"value":1820},{"type":42,"tag":65,"props":3089,"children":3091},{"className":3090},[],[3092],{"type":48,"value":3093},"combobox",{"type":48,"value":1820},{"type":42,"tag":65,"props":3096,"children":3098},{"className":3097},[],[3099],{"type":48,"value":3100},"textarea",{"type":42,"tag":1955,"props":3102,"children":3103},{},[3104],{"type":48,"value":3105},"text entry",{"type":42,"tag":1955,"props":3107,"children":3108},{},[3109,3114,3116,3122,3124,3130,3132,3137,3139,3144,3146,3151,3153,3158],{"type":42,"tag":65,"props":3110,"children":3112},{"className":3111},[],[3113],{"type":48,"value":2921},{"type":48,"value":3115},"; edits via ",{"type":42,"tag":65,"props":3117,"children":3119},{"className":3118},[],[3120],{"type":48,"value":3121},"on-input",{"type":48,"value":3123},", enter via ",{"type":42,"tag":65,"props":3125,"children":3127},{"className":3126},[],[3128],{"type":48,"value":3129},"on-submit",{"type":48,"value":3131}," on single-line kinds; in a ",{"type":42,"tag":65,"props":3133,"children":3135},{"className":3134},[],[3136],{"type":48,"value":3100},{"type":48,"value":3138},", Enter (and Shift+Enter) inserts a newline and ",{"type":42,"tag":65,"props":3140,"children":3142},{"className":3141},[],[3143],{"type":48,"value":3129},{"type":48,"value":3145}," dispatches on primary+Enter (cmd on macOS, ctrl elsewhere); ",{"type":42,"tag":65,"props":3147,"children":3149},{"className":3148},[],[3150],{"type":48,"value":3086},{"type":48,"value":3152}," carries a built-in trailing clear affordance whenever it holds text (press the x, or Escape while focused — both clear through the text-edit path, so ",{"type":42,"tag":65,"props":3154,"children":3156},{"className":3155},[],[3157],{"type":48,"value":3121},{"type":48,"value":3159}," hears it; no attribute, no external Clear button needed)",{"type":42,"tag":1928,"props":3161,"children":3162},{},[3163,3172,3177],{"type":42,"tag":1955,"props":3164,"children":3165},{},[3166],{"type":42,"tag":65,"props":3167,"children":3169},{"className":3168},[],[3170],{"type":48,"value":3171},"status-bar",{"type":42,"tag":1955,"props":3173,"children":3174},{},[3175],{"type":48,"value":3176},"status bar",{"type":42,"tag":1955,"props":3178,"children":3179},{},[3180],{"type":48,"value":3181},"text leaf: content only, no children",{"type":42,"tag":1928,"props":3183,"children":3184},{},[3185,3201,3206],{"type":42,"tag":1955,"props":3186,"children":3187},{},[3188,3194,3195],{"type":42,"tag":65,"props":3189,"children":3191},{"className":3190},[],[3192],{"type":48,"value":3193},"separator",{"type":48,"value":1820},{"type":42,"tag":65,"props":3196,"children":3198},{"className":3197},[],[3199],{"type":48,"value":3200},"spacer",{"type":42,"tag":1955,"props":3202,"children":3203},{},[3204],{"type":48,"value":3205},"separator, flexible space",{"type":42,"tag":1955,"props":3207,"children":3208},{},[3209,3214,3216,3221,3223,3228,3230,3235,3237],{"type":42,"tag":65,"props":3210,"children":3212},{"className":3211},[],[3213],{"type":48,"value":3193},{"type":48,"value":3215}," is axis-aware: a horizontal rule in a ",{"type":42,"tag":65,"props":3217,"children":3219},{"className":3218},[],[3220],{"type":48,"value":1970},{"type":48,"value":3222},", a thin vertical divider in a ",{"type":42,"tag":65,"props":3224,"children":3226},{"className":3225},[],[3227],{"type":48,"value":1963},{"type":48,"value":3229},"; give ",{"type":42,"tag":65,"props":3231,"children":3233},{"className":3232},[],[3234],{"type":48,"value":3200},{"type":48,"value":3236}," a ",{"type":42,"tag":65,"props":3238,"children":3240},{"className":3239},[],[3241],{"type":48,"value":3242},"grow",{"type":42,"tag":1928,"props":3244,"children":3245},{},[3246,3262,3267],{"type":42,"tag":1955,"props":3247,"children":3248},{},[3249,3255,3256],{"type":42,"tag":65,"props":3250,"children":3252},{"className":3251},[],[3253],{"type":48,"value":3254},"skeleton",{"type":48,"value":1820},{"type":42,"tag":65,"props":3257,"children":3259},{"className":3258},[],[3260],{"type":48,"value":3261},"spinner",{"type":42,"tag":1955,"props":3263,"children":3264},{},[3265],{"type":48,"value":3266},"loading leaves",{"type":42,"tag":1955,"props":3268,"children":3269},{},[3270,3272,3277,3278,3283,3284],{"type":48,"value":3271},"size ",{"type":42,"tag":65,"props":3273,"children":3275},{"className":3274},[],[3276],{"type":48,"value":3254},{"type":48,"value":157},{"type":42,"tag":65,"props":3279,"children":3281},{"className":3280},[],[3282],{"type":48,"value":2339},{"type":48,"value":456},{"type":42,"tag":65,"props":3285,"children":3287},{"className":3286},[],[3288],{"type":48,"value":3289},"height",{"type":42,"tag":1928,"props":3291,"children":3292},{},[3293,3302,3307],{"type":42,"tag":1955,"props":3294,"children":3295},{},[3296],{"type":42,"tag":65,"props":3297,"children":3299},{"className":3298},[],[3300],{"type":48,"value":3301},"icon",{"type":42,"tag":1955,"props":3303,"children":3304},{},[3305],{"type":48,"value":3306},"vector icon leaf",{"type":42,"tag":1955,"props":3308,"children":3309},{},[3310,3316,3318,3324,3326,3332,3334,3340,3342,3348,3350,3356,3358,3363,3365,3370,3371],{"type":42,"tag":65,"props":3311,"children":3313},{"className":3312},[],[3314],{"type":48,"value":3315},"name",{"type":48,"value":3317}," picks the icon: a bare literal is a curated built-in stroke icon (compile-checked; 49 names: search, plus, x, x-circle, check, check-circle, chevron-up\u002Fdown\u002Fleft\u002Fright, arrow-up\u002Fdown\u002Fright, menu, panel-left, panel-right, settings, terminal, wrench, trash, edit, copy, external-link, play, pause, skip-back\u002Fforward, shuffle, repeat, music, volume, info, alert, download, save, folder, folder-open, file-text, sun, moon, eye, clock, git-pull-request, git-merge, git-branch, circle-dot, archive, refresh-cw, send); ",{"type":42,"tag":65,"props":3319,"children":3321},{"className":3320},[],[3322],{"type":48,"value":3323},"app:\u003Cname>",{"type":48,"value":3325}," reaches an icon the app registered at boot with ",{"type":42,"tag":65,"props":3327,"children":3329},{"className":3328},[],[3330],{"type":48,"value":3331},"canvas.icons.registerAppIcons",{"type":48,"value":3333}," (declare the table as ",{"type":42,"tag":65,"props":3335,"children":3337},{"className":3336},[],[3338],{"type":48,"value":3339},"pub const app_icons",{"type":48,"value":3341}," on the app root so ",{"type":42,"tag":65,"props":3343,"children":3345},{"className":3344},[],[3346],{"type":48,"value":3347},"native check",{"type":48,"value":3349}," verifies the name against the model contract), and one ",{"type":42,"tag":65,"props":3351,"children":3353},{"className":3352},[],[3354],{"type":48,"value":3355},"{binding}",{"type":48,"value":3357}," defers the choice to model data - an unknown resolved name draws the missing-icon fallback (a slashed circle) with a Debug warning naming the value, never a silent gap; tint with ",{"type":42,"tag":65,"props":3359,"children":3361},{"className":3360},[],[3362],{"type":48,"value":2773},{"type":48,"value":3364},", size with ",{"type":42,"tag":65,"props":3366,"children":3368},{"className":3367},[],[3369],{"type":48,"value":2339},{"type":48,"value":456},{"type":42,"tag":65,"props":3372,"children":3374},{"className":3373},[],[3375],{"type":48,"value":3289},{"type":42,"tag":1928,"props":3377,"children":3378},{},[3379,3388,3393],{"type":42,"tag":1955,"props":3380,"children":3381},{},[3382],{"type":42,"tag":65,"props":3383,"children":3385},{"className":3384},[],[3386],{"type":48,"value":3387},"media-surface",{"type":42,"tag":1955,"props":3389,"children":3390},{},[3391],{"type":48,"value":3392},"media surface leaf",{"type":42,"tag":1955,"props":3394,"children":3395},{},[3396,3398,3404,3406,3412,3414,3419,3420,3425,3427,3432,3434,3439],{"type":48,"value":3397},"composites a texture produced OUTSIDE the widget tree (video decoder, camera, an external renderer like mpv) into the layout like any widget — clipped, z-ordered, rounded. ",{"type":42,"tag":65,"props":3399,"children":3401},{"className":3400},[],[3402],{"type":48,"value":3403},"surface=\"{binding}\"",{"type":48,"value":3405}," (required) binds the model-owned u64 surface id a Zig-tier producer targets (",{"type":42,"tag":65,"props":3407,"children":3409},{"className":3408},[],[3410],{"type":48,"value":3411},"runtime.acquireMediaSurfaceProducer",{"type":48,"value":3413}," pushes RGBA8 frames, latest-wins, paced by the presented-frame clock; 0 = unbound, draws nothing; usable ids are nonzero values below the reserved bit 63). No intrinsic size — give it ",{"type":42,"tag":65,"props":3415,"children":3417},{"className":3416},[],[3418],{"type":48,"value":2339},{"type":48,"value":456},{"type":42,"tag":65,"props":3421,"children":3423},{"className":3422},[],[3424],{"type":48,"value":3289},{"type":48,"value":3426}," or ",{"type":42,"tag":65,"props":3428,"children":3430},{"className":3429},[],[3431],{"type":48,"value":3242},{"type":48,"value":3433},"; display-only (presses fall through); ",{"type":42,"tag":65,"props":3435,"children":3437},{"className":3436},[],[3438],{"type":48,"value":2892},{"type":48,"value":3440}," it (pictorial content). Texture contents are presentation chrome: goldens, reference screenshots, and session replay show the deterministic id-derived placeholder, never producer frames",{"type":42,"tag":1928,"props":3442,"children":3443},{},[3444,3453,3458],{"type":42,"tag":1955,"props":3445,"children":3446},{},[3447],{"type":42,"tag":65,"props":3448,"children":3450},{"className":3449},[],[3451],{"type":48,"value":3452},"image",{"type":42,"tag":1955,"props":3454,"children":3455},{},[3456],{"type":48,"value":3457},"runtime image leaf",{"type":42,"tag":1955,"props":3459,"children":3460},{},[3461,3463,3469,3471,3477,3478,3484,3486,3491,3493,3498,3499,3504,3505,3510,3511,3516],{"type":48,"value":3462},"draws a RUNTIME-REGISTERED image by its model-owned u64 ImageId — the id ",{"type":42,"tag":65,"props":3464,"children":3466},{"className":3465},[],[3467],{"type":48,"value":3468},"Cmd.imageLoad",{"type":48,"value":3470}," (TS) or ",{"type":42,"tag":65,"props":3472,"children":3474},{"className":3473},[],[3475],{"type":48,"value":3476},"fx.loadImage",{"type":48,"value":456},{"type":42,"tag":65,"props":3479,"children":3481},{"className":3480},[],[3482],{"type":48,"value":3483},"fx.registerImageBytes",{"type":48,"value":3485}," (Zig) registered pixels under. ",{"type":42,"tag":65,"props":3487,"children":3489},{"className":3488},[],[3490],{"type":48,"value":2943},{"type":48,"value":3492}," (required) binds a model field\u002Ffn; ids are model data, never markup literals, and 0 draws nothing (store the id only when the load reports loaded — see the Images section). No intrinsic size — give it ",{"type":42,"tag":65,"props":3494,"children":3496},{"className":3495},[],[3497],{"type":48,"value":2339},{"type":48,"value":456},{"type":42,"tag":65,"props":3500,"children":3502},{"className":3501},[],[3503],{"type":48,"value":3289},{"type":48,"value":3426},{"type":42,"tag":65,"props":3506,"children":3508},{"className":3507},[],[3509],{"type":48,"value":3242},{"type":48,"value":3433},{"type":42,"tag":65,"props":3512,"children":3514},{"className":3513},[],[3515],{"type":48,"value":2892},{"type":48,"value":3517}," it (pictorial content)",{"type":42,"tag":1928,"props":3519,"children":3520},{},[3521,3530,3535],{"type":42,"tag":1955,"props":3522,"children":3523},{},[3524],{"type":42,"tag":65,"props":3525,"children":3527},{"className":3526},[],[3528],{"type":48,"value":3529},"markdown",{"type":42,"tag":1955,"props":3531,"children":3532},{},[3533],{"type":48,"value":3534},"rendered markdown subtree",{"type":42,"tag":1955,"props":3536,"children":3537},{},[3538,3540,3546,3548,3553],{"type":48,"value":3539},"leaf; ",{"type":42,"tag":65,"props":3541,"children":3543},{"className":3542},[],[3544],{"type":48,"value":3545},"source",{"type":48,"value":3547}," is one ",{"type":42,"tag":65,"props":3549,"children":3551},{"className":3550},[],[3552],{"type":48,"value":3355},{"type":48,"value":3554}," — see \"Markdown in markup\"",{"type":42,"tag":1928,"props":3556,"children":3557},{},[3558,3574,3579],{"type":42,"tag":1955,"props":3559,"children":3560},{},[3561,3567,3568],{"type":42,"tag":65,"props":3562,"children":3564},{"className":3563},[],[3565],{"type":48,"value":3566},"stepper",{"type":48,"value":2164},{"type":42,"tag":65,"props":3569,"children":3571},{"className":3570},[],[3572],{"type":48,"value":3573},"step",{"type":42,"tag":1955,"props":3575,"children":3576},{},[3577],{"type":48,"value":3578},"composite stage track",{"type":42,"tag":1955,"props":3580,"children":3581},{},[3582,3588,3590,3596,3597,3603,3604],{"type":42,"tag":65,"props":3583,"children":3585},{"className":3584},[],[3586],{"type":48,"value":3587},"active=\"{index}\"",{"type":48,"value":3589}," (required) derives each step's completed\u002Factive\u002Fpending state; steps are text leaves (no attributes) joined by connectors; stepper also takes ",{"type":42,"tag":65,"props":3591,"children":3593},{"className":3592},[],[3594],{"type":48,"value":3595},"key",{"type":48,"value":1820},{"type":42,"tag":65,"props":3598,"children":3600},{"className":3599},[],[3601],{"type":48,"value":3602},"global-key",{"type":48,"value":1820},{"type":42,"tag":65,"props":3605,"children":3607},{"className":3606},[],[3608],{"type":48,"value":2892},{"type":42,"tag":1928,"props":3610,"children":3611},{},[3612,3628,3633],{"type":42,"tag":1955,"props":3613,"children":3614},{},[3615,3621,3622],{"type":42,"tag":65,"props":3616,"children":3618},{"className":3617},[],[3619],{"type":48,"value":3620},"timeline",{"type":48,"value":2164},{"type":42,"tag":65,"props":3623,"children":3625},{"className":3624},[],[3626],{"type":48,"value":3627},"timeline-item",{"type":42,"tag":1955,"props":3629,"children":3630},{},[3631],{"type":48,"value":3632},"composite ledger list",{"type":42,"tag":1955,"props":3634,"children":3635},{},[3636,3638,3644,3646,3652,3653,3659,3660,3666,3667,3673,3674,3680,3682,3687,3688,3693],{"type":48,"value":3637},"items only inside a timeline (for\u002Fif fine); items are leaves — ",{"type":42,"tag":65,"props":3639,"children":3641},{"className":3640},[],[3642],{"type":48,"value":3643},"title",{"type":48,"value":3645}," (required), ",{"type":42,"tag":65,"props":3647,"children":3649},{"className":3648},[],[3650],{"type":48,"value":3651},"description",{"type":48,"value":1820},{"type":42,"tag":65,"props":3654,"children":3656},{"className":3655},[],[3657],{"type":48,"value":3658},"meta",{"type":48,"value":1820},{"type":42,"tag":65,"props":3661,"children":3663},{"className":3662},[],[3664],{"type":48,"value":3665},"indicator",{"type":48,"value":1820},{"type":42,"tag":65,"props":3668,"children":3670},{"className":3669},[],[3671],{"type":48,"value":3672},"variant",{"type":48,"value":1820},{"type":42,"tag":65,"props":3675,"children":3677},{"className":3676},[],[3678],{"type":48,"value":3679},"connector=\"false\"",{"type":48,"value":3681}," on the last item, ",{"type":42,"tag":65,"props":3683,"children":3685},{"className":3684},[],[3686],{"type":48,"value":2269},{"type":48,"value":2930},{"type":42,"tag":65,"props":3689,"children":3691},{"className":3690},[],[3692],{"type":48,"value":1176},{"type":48,"value":3694}," makes the whole item pressable with a trailing chevron",{"type":42,"tag":1928,"props":3696,"children":3697},{},[3698,3714,3719],{"type":42,"tag":1955,"props":3699,"children":3700},{},[3701,3707,3708],{"type":42,"tag":65,"props":3702,"children":3704},{"className":3703},[],[3705],{"type":48,"value":3706},"chart",{"type":48,"value":2164},{"type":42,"tag":65,"props":3709,"children":3711},{"className":3710},[],[3712],{"type":48,"value":3713},"series",{"type":42,"tag":1955,"props":3715,"children":3716},{},[3717],{"type":48,"value":3718},"composite data chart",{"type":42,"tag":1955,"props":3720,"children":3721},{},[3722,3724,3730,3732,3738,3740,3746,3748,3753,3754,3760,3761,3767,3769,3775,3777,3782,3784,3790,3791,3797,3798,3804,3805,3811,3812,3818,3819,3825,3826,3832,3833,3839,3841,3846],{"type":48,"value":3723},"series only inside a chart, and only series (the set is static — data varies through bindings); each series is a leaf — ",{"type":42,"tag":65,"props":3725,"children":3727},{"className":3726},[],[3728],{"type":48,"value":3729},"values=\"{binding}\"",{"type":48,"value":3731}," (required) names a model ",{"type":42,"tag":65,"props":3733,"children":3735},{"className":3734},[],[3736],{"type":48,"value":3737},"[]const f32",{"type":48,"value":3739}," iterable, ",{"type":42,"tag":65,"props":3741,"children":3743},{"className":3742},[],[3744],{"type":48,"value":3745},"kind",{"type":48,"value":3747}," is ",{"type":42,"tag":65,"props":3749,"children":3751},{"className":3750},[],[3752],{"type":48,"value":231},{"type":48,"value":456},{"type":42,"tag":65,"props":3755,"children":3757},{"className":3756},[],[3758],{"type":48,"value":3759},"area",{"type":48,"value":456},{"type":42,"tag":65,"props":3762,"children":3764},{"className":3763},[],[3765],{"type":48,"value":3766},"bar",{"type":48,"value":3768}," (literal), ",{"type":42,"tag":65,"props":3770,"children":3772},{"className":3771},[],[3773],{"type":48,"value":3774},"color",{"type":48,"value":3776}," a token name, ",{"type":42,"tag":65,"props":3778,"children":3780},{"className":3779},[],[3781],{"type":48,"value":2892},{"type":48,"value":3783}," the semantics name; chart takes ",{"type":42,"tag":65,"props":3785,"children":3787},{"className":3786},[],[3788],{"type":48,"value":3789},"y-min",{"type":48,"value":1820},{"type":42,"tag":65,"props":3792,"children":3794},{"className":3793},[],[3795],{"type":48,"value":3796},"y-max",{"type":48,"value":1820},{"type":42,"tag":65,"props":3799,"children":3801},{"className":3800},[],[3802],{"type":48,"value":3803},"grid-lines",{"type":48,"value":1820},{"type":42,"tag":65,"props":3806,"children":3808},{"className":3807},[],[3809],{"type":48,"value":3810},"baseline",{"type":48,"value":1820},{"type":42,"tag":65,"props":3813,"children":3815},{"className":3814},[],[3816],{"type":48,"value":3817},"x-labels",{"type":48,"value":1820},{"type":42,"tag":65,"props":3820,"children":3822},{"className":3821},[],[3823],{"type":48,"value":3824},"y-labels",{"type":48,"value":1820},{"type":42,"tag":65,"props":3827,"children":3829},{"className":3828},[],[3830],{"type":48,"value":3831},"hover-details",{"type":48,"value":1820},{"type":42,"tag":65,"props":3834,"children":3836},{"className":3835},[],[3837],{"type":48,"value":3838},"stroke-width",{"type":48,"value":3840},", box options, ",{"type":42,"tag":65,"props":3842,"children":3844},{"className":3843},[],[3845],{"type":48,"value":2892},{"type":48,"value":3847}," — see \"Charts\"",{"type":42,"tag":1928,"props":3849,"children":3850},{},[3851,3859,3864],{"type":42,"tag":1955,"props":3852,"children":3853},{},[3854],{"type":42,"tag":65,"props":3855,"children":3857},{"className":3856},[],[3858],{"type":48,"value":1391},{"type":42,"tag":1955,"props":3860,"children":3861},{},[3862],{"type":48,"value":3863},"consumed by its parent",{"type":42,"tag":1955,"props":3865,"children":3866},{},[3867,3869,3874,3875,3880,3882,3887,3888,3893,3894,3899,3901,3906,3908,3913,3914,3919,3920,3925,3926,3931],{"type":48,"value":3868},"right-click menu on its DIRECT parent (a hit target or an element with ",{"type":42,"tag":65,"props":3870,"children":3872},{"className":3871},[],[3873],{"type":48,"value":1176},{"type":48,"value":456},{"type":42,"tag":65,"props":3876,"children":3878},{"className":3877},[],[3879],{"type":48,"value":1183},{"type":48,"value":3881},"); metadata, never a flow child. Children: ",{"type":42,"tag":65,"props":3883,"children":3885},{"className":3884},[],[3886],{"type":48,"value":1191},{"type":48,"value":1193},{"type":42,"tag":65,"props":3889,"children":3891},{"className":3890},[],[3892],{"type":48,"value":1176},{"type":48,"value":1200},{"type":42,"tag":65,"props":3895,"children":3897},{"className":3896},[],[3898],{"type":48,"value":1206},{"type":48,"value":3900}," optional, no ",{"type":42,"tag":65,"props":3902,"children":3904},{"className":3903},[],[3905],{"type":48,"value":3301},{"type":48,"value":3907},") and bare ",{"type":42,"tag":65,"props":3909,"children":3911},{"className":3910},[],[3912],{"type":48,"value":3193},{"type":48,"value":1216},{"type":42,"tag":65,"props":3915,"children":3917},{"className":3916},[],[3918],{"type":48,"value":1222},{"type":48,"value":456},{"type":42,"tag":65,"props":3921,"children":3923},{"className":3922},[],[3924],{"type":48,"value":1229},{"type":48,"value":456},{"type":42,"tag":65,"props":3927,"children":3929},{"className":3928},[],[3930],{"type":48,"value":1236},{"type":48,"value":3932}," around them. Attribute-less; presents natively where the host has a menu presenter, as an anchored surface elsewhere — see \"Context menus\"",{"type":42,"tag":1928,"props":3934,"children":3935},{},[3936,3959,3964],{"type":42,"tag":1955,"props":3937,"children":3938},{},[3939,3945,3946,3951,3953],{"type":42,"tag":65,"props":3940,"children":3942},{"className":3941},[],[3943],{"type":48,"value":3944},"input-group",{"type":48,"value":2164},{"type":42,"tag":65,"props":3947,"children":3949},{"className":3948},[],[3950],{"type":48,"value":3100},{"type":48,"value":3952}," + ",{"type":42,"tag":65,"props":3954,"children":3956},{"className":3955},[],[3957],{"type":48,"value":3958},"input-group-actions",{"type":42,"tag":1955,"props":3960,"children":3961},{},[3962],{"type":48,"value":3963},"composite grouped input",{"type":42,"tag":1955,"props":3965,"children":3966},{},[3967,3969,3974,3976,3981,3983,3988,3989,3994,3995,4000,4001,4006,4007,4013,4015,4020,4021,4026,4027,4032,4033,4038,4039,4044,4045,4050,4051,4056,4058,4063,4065,4070,4071,4076,4077,4082,4084,4090,4092,4098,4099,4105],{"type":48,"value":3968},"the composer shape: ONE bordered field wrapping exactly one ",{"type":42,"tag":65,"props":3970,"children":3972},{"className":3971},[],[3973],{"type":48,"value":3100},{"type":48,"value":3975}," (first — document order is focus order) plus an optional ",{"type":42,"tag":65,"props":3977,"children":3979},{"className":3978},[],[3980],{"type":48,"value":3958},{"type":48,"value":3982}," row of controls inside the same border. The group wears the focus ring for its focused descendant and the textarea's own chrome dissolves automatically, so the whole group reads as one field; the textarea keeps its full behavior (",{"type":42,"tag":65,"props":3984,"children":3986},{"className":3985},[],[3987],{"type":48,"value":48},{"type":48,"value":1820},{"type":42,"tag":65,"props":3990,"children":3992},{"className":3991},[],[3993],{"type":48,"value":2921},{"type":48,"value":1820},{"type":42,"tag":65,"props":3996,"children":3998},{"className":3997},[],[3999],{"type":48,"value":3121},{"type":48,"value":1820},{"type":42,"tag":65,"props":4002,"children":4004},{"className":4003},[],[4005],{"type":48,"value":3129},{"type":48,"value":1820},{"type":42,"tag":65,"props":4008,"children":4010},{"className":4009},[],[4011],{"type":48,"value":4012},"autofocus",{"type":48,"value":4014},"). Group takes ",{"type":42,"tag":65,"props":4016,"children":4018},{"className":4017},[],[4019],{"type":48,"value":2892},{"type":48,"value":1820},{"type":42,"tag":65,"props":4022,"children":4024},{"className":4023},[],[4025],{"type":48,"value":2339},{"type":48,"value":1820},{"type":42,"tag":65,"props":4028,"children":4030},{"className":4029},[],[4031],{"type":48,"value":3289},{"type":48,"value":1820},{"type":42,"tag":65,"props":4034,"children":4036},{"className":4035},[],[4037],{"type":48,"value":2502},{"type":48,"value":1820},{"type":42,"tag":65,"props":4040,"children":4042},{"className":4041},[],[4043],{"type":48,"value":3242},{"type":48,"value":1820},{"type":42,"tag":65,"props":4046,"children":4048},{"className":4047},[],[4049],{"type":48,"value":3595},{"type":48,"value":1820},{"type":42,"tag":65,"props":4052,"children":4054},{"className":4053},[],[4055],{"type":48,"value":3602},{"type":48,"value":4057},"; the actions row takes ",{"type":42,"tag":65,"props":4059,"children":4061},{"className":4060},[],[4062],{"type":48,"value":2022},{"type":48,"value":4064}," and holds ordinary elements (",{"type":42,"tag":65,"props":4066,"children":4068},{"className":4067},[],[4069],{"type":48,"value":1222},{"type":48,"value":456},{"type":42,"tag":65,"props":4072,"children":4074},{"className":4073},[],[4075],{"type":48,"value":1229},{"type":48,"value":456},{"type":42,"tag":65,"props":4078,"children":4080},{"className":4079},[],[4081],{"type":48,"value":1236},{"type":48,"value":4083}," work — swap send for stop while streaming) — put a ",{"type":42,"tag":65,"props":4085,"children":4087},{"className":4086},[],[4088],{"type":48,"value":4089},"\u003Cspacer grow=\"1\"\u002F>",{"type":48,"value":4091}," between leading and trailing controls (",{"type":42,"tag":65,"props":4093,"children":4095},{"className":4094},[],[4096],{"type":48,"value":4097},"Ui.inputGroup",{"type":48,"value":456},{"type":42,"tag":65,"props":4100,"children":4102},{"className":4101},[],[4103],{"type":48,"value":4104},"Ui.inputGroupActions",{"type":48,"value":4106}," are the Zig-view equivalents)",{"type":42,"tag":51,"props":4108,"children":4109},{},[4110,4112,4118,4120,4126,4127,4133,4135,4141,4143,4149,4150,4156,4158,4164,4166,4171,4172,4177,4179,4185,4187,4193,4195,4201,4202,4208,4210,4216,4218,4224,4226,4232,4234,4240,4242,4248,4250,4256,4258,4264,4265,4271,4273,4279,4280,4286,4288,4294,4295,4301,4303,4309,4311,4316,4318,4324],{"type":48,"value":4111},"Not markup-expressible (deliberately — write these as Zig view functions with ",{"type":42,"tag":65,"props":4113,"children":4115},{"className":4114},[],[4116],{"type":48,"value":4117},"canvas.Ui",{"type":48,"value":4119},"): ",{"type":42,"tag":65,"props":4121,"children":4123},{"className":4122},[],[4124],{"type":48,"value":4125},"icon_button",{"type":48,"value":2620},{"type":42,"tag":65,"props":4128,"children":4130},{"className":4129},[],[4131],{"type":48,"value":4132},"\u003Cbutton icon=\"...\">",{"type":48,"value":4134}," with empty content is the declarative icon button), ",{"type":42,"tag":65,"props":4136,"children":4138},{"className":4137},[],[4139],{"type":48,"value":4140},"data_grid",{"type":48,"value":4142}," (per-column cell templates), ",{"type":42,"tag":65,"props":4144,"children":4146},{"className":4145},[],[4147],{"type":48,"value":4148},"popover",{"type":48,"value":456},{"type":42,"tag":65,"props":4151,"children":4153},{"className":4152},[],[4154],{"type":48,"value":4155},"menu_surface",{"type":48,"value":4157}," (anchored to runtime geometry), ",{"type":42,"tag":65,"props":4159,"children":4161},{"className":4160},[],[4162],{"type":48,"value":4163},"segmented_control",{"type":48,"value":4165}," (use ",{"type":42,"tag":65,"props":4167,"children":4169},{"className":4168},[],[4170],{"type":48,"value":2106},{"type":48,"value":456},{"type":42,"tag":65,"props":4173,"children":4175},{"className":4174},[],[4176],{"type":48,"value":2113},{"type":48,"value":4178},": ",{"type":42,"tag":65,"props":4180,"children":4182},{"className":4181},[],[4183],{"type":48,"value":4184},"\u003Cbutton>",{"type":48,"value":4186}," children of ",{"type":42,"tag":65,"props":4188,"children":4190},{"className":4189},[],[4191],{"type":48,"value":4192},"\u003Ctabs>",{"type":48,"value":4194}," lower to segmented triggers automatically, so the active tab lifts per the house treatment). Charts ARE expressible: ",{"type":42,"tag":65,"props":4196,"children":4198},{"className":4197},[],[4199],{"type":48,"value":4200},"\u003Cchart>",{"type":48,"value":157},{"type":42,"tag":65,"props":4203,"children":4205},{"className":4204},[],[4206],{"type":48,"value":4207},"\u003Cseries values=\"{binding}\">",{"type":48,"value":4209}," children binding model f32 iterables — see the Charts section (",{"type":42,"tag":65,"props":4211,"children":4213},{"className":4212},[],[4214],{"type":48,"value":4215},".band",{"type":48,"value":4217}," series and dynamic series composition stay with ",{"type":42,"tag":65,"props":4219,"children":4221},{"className":4220},[],[4222],{"type":48,"value":4223},"ui.chart",{"type":48,"value":4225},"). Built-in vector icons ARE expressible: ",{"type":42,"tag":65,"props":4227,"children":4229},{"className":4228},[],[4230],{"type":48,"value":4231},"\u003Cicon name=\"search\"\u002F>",{"type":48,"value":4233}," (closed, compile-checked name set; ",{"type":42,"tag":65,"props":4235,"children":4237},{"className":4236},[],[4238],{"type":48,"value":4239},"Ui.icon",{"type":48,"value":4241}," is the Zig-view equivalent). App-authored icons: ",{"type":42,"tag":65,"props":4243,"children":4245},{"className":4244},[],[4246],{"type":48,"value":4247},"canvas.svg_icon.parseComptime(@embedFile(\"icons\u002Flogo.svg\"))",{"type":48,"value":4249}," parses any SVG in the common 24x24 stroke-icon dialect at comptime; register the parsed table once at boot with ",{"type":42,"tag":65,"props":4251,"children":4253},{"className":4252},[],[4254],{"type":48,"value":4255},"canvas.icons.registerAppIcons(&table)",{"type":48,"value":4257}," and draw by name via ",{"type":42,"tag":65,"props":4259,"children":4261},{"className":4260},[],[4262],{"type":48,"value":4263},"ui.appIcon(.{...}, \"logo\")",{"type":48,"value":3426},{"type":42,"tag":65,"props":4266,"children":4268},{"className":4267},[],[4269],{"type":48,"value":4270},"ElementOptions.icon",{"type":48,"value":4272}," — registered names render exactly like built-ins on every draw path. Markup ",{"type":42,"tag":65,"props":4274,"children":4276},{"className":4275},[],[4277],{"type":48,"value":4278},"\u003Cicon>",{"type":48,"value":456},{"type":42,"tag":65,"props":4281,"children":4283},{"className":4282},[],[4284],{"type":48,"value":4285},"\u003Cbutton icon>",{"type":48,"value":4287}," stay built-in-only (the compiled engine validates names at comptime, where runtime registrations cannot exist — engine parity). Runtime images ARE expressible: ",{"type":42,"tag":65,"props":4289,"children":4291},{"className":4290},[],[4292],{"type":48,"value":4293},"\u003Cimage image=\"{cover}\" width=\"120\" height=\"80\" label=\"Cover art\"\u002F>",{"type":48,"value":2555},{"type":42,"tag":65,"props":4296,"children":4298},{"className":4297},[],[4299],{"type":48,"value":4300},"\u003Cavatar image=\"{user_image}\">CT\u003C\u002Favatar>",{"type":48,"value":4302}," bind a ",{"type":42,"tag":65,"props":4304,"children":4306},{"className":4305},[],[4307],{"type":48,"value":4308},"u64",{"type":48,"value":4310}," ImageId model field\u002Ffn (the id is just model data; 0 draws nothing \u002F keeps the initials fallback) — see the Images section; the ",{"type":42,"tag":65,"props":4312,"children":4314},{"className":4313},[],[4315],{"type":48,"value":3452},{"type":48,"value":4317}," binding is required on the leaf (an unbound ",{"type":42,"tag":65,"props":4319,"children":4321},{"className":4320},[],[4322],{"type":48,"value":4323},"\u003Cimage>",{"type":48,"value":4325}," is dead markup) and stays avatar+image scoped.",{"type":42,"tag":212,"props":4327,"children":4329},{"id":4328},"attributes",[4330],{"type":48,"value":4331},"Attributes",{"type":42,"tag":51,"props":4333,"children":4334},{},[4335,4337,4342,4344,4349,4350,4355,4356,4361,4362,4367,4368,4373,4374,4379,4380,4385,4386,4391,4392,4397,4399,4404,4406,4411,4412,4417,4419,4424,4426,4432,4434,4439,4440,4445,4446,4451,4453,4458,4460,4465,4467,4472,4474,4479,4481,4486,4487,4492,4494,4500,4502,4508,4510,4516,4518,4523,4524,4529,4531,4537,4539,4545,4547,4552,4554,4560,4561,4566,4568,4573,4575,4581,4583,4589,4590,4596,4597,4602,4603,4608,4609,4614,4616,4622,4623,4629,4631,4637,4639,4644,4645,4651,4652,4657,4658,4664,4666,4672,4673,4678,4680,4685,4686,4691,4693,4698,4700,4705,4707,4713,4714,4719,4721,4727,4729,4734,4736,4742,4744,4750,4752,4757,4759,4765,4767,4772,4774,4780,4782,4788,4790,4795,4796,4801,4802,4807,4808,4813,4814,4819,4820,4825,4826,4831,4832,4837,4838,4843,4844,4849,4851,4856,4858,4863,4865,4870,4872,4877,4879,4885,4887,4893,4895,4900,4902,4908,4910,4915,4916,4922,4924,4930,4932,4938,4940,4946,4948,4953,4955,4960,4962,4967,4969,4974,4976,4981,4983,4988,4990,4995,4996,5001,5003,5009,5011,5017,5019,5025,5027,5032,5034,5039,5041,5047,5049,5055,5057,5063,5064,5070,5072,5077,5079,5085],{"type":48,"value":4336},"Layout: ",{"type":42,"tag":65,"props":4338,"children":4340},{"className":4339},[],[4341],{"type":48,"value":2022},{"type":48,"value":4343}," (flow containers only — stacking containers ",{"type":42,"tag":65,"props":4345,"children":4347},{"className":4346},[],[4348],{"type":48,"value":1992},{"type":48,"value":456},{"type":42,"tag":65,"props":4351,"children":4353},{"className":4352},[],[4354],{"type":48,"value":1999},{"type":48,"value":456},{"type":42,"tag":65,"props":4357,"children":4359},{"className":4358},[],[4360],{"type":48,"value":2006},{"type":48,"value":456},{"type":42,"tag":65,"props":4363,"children":4365},{"className":4364},[],[4366],{"type":48,"value":2289},{"type":48,"value":456},{"type":42,"tag":65,"props":4369,"children":4371},{"className":4370},[],[4372],{"type":48,"value":2296},{"type":48,"value":456},{"type":42,"tag":65,"props":4375,"children":4377},{"className":4376},[],[4378],{"type":48,"value":2385},{"type":48,"value":456},{"type":42,"tag":65,"props":4381,"children":4383},{"className":4382},[],[4384],{"type":48,"value":2392},{"type":48,"value":456},{"type":42,"tag":65,"props":4387,"children":4389},{"className":4388},[],[4390],{"type":48,"value":2399},{"type":48,"value":456},{"type":42,"tag":65,"props":4393,"children":4395},{"className":4394},[],[4396],{"type":48,"value":2436},{"type":48,"value":4398}," layer their children, so ",{"type":42,"tag":65,"props":4400,"children":4402},{"className":4401},[],[4403],{"type":48,"value":2022},{"type":48,"value":4405}," there is a validation error, not silence: wrap the children in a ",{"type":42,"tag":65,"props":4407,"children":4409},{"className":4408},[],[4410],{"type":48,"value":1970},{"type":48,"value":456},{"type":42,"tag":65,"props":4413,"children":4415},{"className":4414},[],[4416],{"type":48,"value":1963},{"type":48,"value":4418}," inside; on ",{"type":42,"tag":65,"props":4420,"children":4422},{"className":4421},[],[4423],{"type":48,"value":2464},{"type":48,"value":4425}," it sets the divider band thickness), ",{"type":42,"tag":65,"props":4427,"children":4429},{"className":4428},[],[4430],{"type":48,"value":4431},"padding",{"type":48,"value":4433}," (uniform), ",{"type":42,"tag":65,"props":4435,"children":4437},{"className":4436},[],[4438],{"type":48,"value":3242},{"type":48,"value":1820},{"type":42,"tag":65,"props":4441,"children":4443},{"className":4442},[],[4444],{"type":48,"value":2339},{"type":48,"value":1820},{"type":42,"tag":65,"props":4447,"children":4449},{"className":4448},[],[4450],{"type":48,"value":3289},{"type":48,"value":4452}," (definite: the element is exactly that size — intrinsic content neither shrinks nor silently overflows it; ",{"type":42,"tag":65,"props":4454,"children":4456},{"className":4455},[],[4457],{"type":48,"value":2436},{"type":48,"value":4459}," treats ",{"type":42,"tag":65,"props":4461,"children":4463},{"className":4462},[],[4464],{"type":48,"value":2339},{"type":48,"value":4466}," as the initial width), ",{"type":42,"tag":65,"props":4468,"children":4470},{"className":4469},[],[4471],{"type":48,"value":2502},{"type":48,"value":4473}," (a floor WITHOUT ",{"type":42,"tag":65,"props":4475,"children":4477},{"className":4476},[],[4478],{"type":48,"value":2339},{"type":48,"value":4480},"'s definite max — the element may grow past it but never shrink below; on split panes it bounds the divider drag), ",{"type":42,"tag":65,"props":4482,"children":4484},{"className":4483},[],[4485],{"type":48,"value":2618},{"type":48,"value":2620},{"type":42,"tag":65,"props":4488,"children":4490},{"className":4489},[],[4491],{"type":48,"value":48},{"type":48,"value":4493}," only: ",{"type":42,"tag":65,"props":4495,"children":4497},{"className":4496},[],[4498],{"type":48,"value":4499},"wrap=\"true\"",{"type":48,"value":4501}," word-wraps at the width the element receives and reserves the wrapped height in columns; ",{"type":42,"tag":65,"props":4503,"children":4505},{"className":4504},[],[4506],{"type":48,"value":4507},"wrap=\"false\"",{"type":48,"value":4509}," and unset are honest single-line — one line whose overflow follows ",{"type":42,"tag":65,"props":4511,"children":4513},{"className":4512},[],[4514],{"type":48,"value":4515},"overflow",{"type":48,"value":4517},"), ",{"type":42,"tag":65,"props":4519,"children":4521},{"className":4520},[],[4522],{"type":48,"value":4515},{"type":48,"value":2620},{"type":42,"tag":65,"props":4525,"children":4527},{"className":4526},[],[4528],{"type":48,"value":48},{"type":48,"value":4530}," only, a teaching error elsewhere: what a single line does with content that does not fit — ",{"type":42,"tag":65,"props":4532,"children":4534},{"className":4533},[],[4535],{"type":48,"value":4536},"ellipsis",{"type":48,"value":4538},", the default, elides behind a trailing … measured with the same metrics paint uses, right for width-constrained list-row titles; ",{"type":42,"tag":65,"props":4540,"children":4542},{"className":4541},[],[4543],{"type":48,"value":4544},"clip",{"type":48,"value":4546}," hard-cuts at the frame for fixed-format content like a duration column where \"1…\" beats nothing; there is deliberately no overflow-visible), ",{"type":42,"tag":65,"props":4548,"children":4550},{"className":4549},[],[4551],{"type":48,"value":2355},{"type":48,"value":4553}," (start|center|end — text leaves, status bars, surface titles; controls that own their label placement ignore it), ",{"type":42,"tag":65,"props":4555,"children":4557},{"className":4556},[],[4558],{"type":48,"value":4559},"columns",{"type":48,"value":2620},{"type":42,"tag":65,"props":4562,"children":4564},{"className":4563},[],[4565],{"type":48,"value":2084},{"type":48,"value":4567}," only: fixed column count, omit for the derived near-square grid; a teaching error elsewhere), ",{"type":42,"tag":65,"props":4569,"children":4571},{"className":4570},[],[4572],{"type":48,"value":113},{"type":48,"value":4574}," (start|center|end|space_between), ",{"type":42,"tag":65,"props":4576,"children":4578},{"className":4577},[],[4579],{"type":48,"value":4580},"cross",{"type":48,"value":4582}," (stretch|start|center|end), ",{"type":42,"tag":65,"props":4584,"children":4586},{"className":4585},[],[4587],{"type":48,"value":4588},"virtualized",{"type":48,"value":1820},{"type":42,"tag":65,"props":4591,"children":4593},{"className":4592},[],[4594],{"type":48,"value":4595},"virtual-item-extent",{"type":48,"value":1820},{"type":42,"tag":65,"props":4598,"children":4600},{"className":4599},[],[4601],{"type":48,"value":2702},{"type":48,"value":2620},{"type":42,"tag":65,"props":4604,"children":4606},{"className":4605},[],[4607],{"type":48,"value":2204},{"type":48,"value":2555},{"type":42,"tag":65,"props":4610,"children":4612},{"className":4611},[],[4613],{"type":48,"value":2587},{"type":48,"value":4615},", literal ",{"type":42,"tag":65,"props":4617,"children":4619},{"className":4618},[],[4620],{"type":48,"value":4621},"below",{"type":48,"value":456},{"type":42,"tag":65,"props":4624,"children":4626},{"className":4625},[],[4627],{"type":48,"value":4628},"above",{"type":48,"value":4630},": floats the surface against its parent instead of the flow — auto-flips when the preferred side does not fit, height clamps to the chosen side, x clamps into the window; an anchored tooltip's visibility is runtime-owned hover intent, unlike the model-owned dropdown), ",{"type":42,"tag":65,"props":4632,"children":4634},{"className":4633},[],[4635],{"type":48,"value":4636},"anchor-alignment",{"type":48,"value":4638}," (with ",{"type":42,"tag":65,"props":4640,"children":4642},{"className":4641},[],[4643],{"type":48,"value":2702},{"type":48,"value":4178},{"type":42,"tag":65,"props":4646,"children":4648},{"className":4647},[],[4649],{"type":48,"value":4650},"start",{"type":48,"value":456},{"type":42,"tag":65,"props":4653,"children":4655},{"className":4654},[],[4656],{"type":48,"value":2363},{"type":48,"value":456},{"type":42,"tag":65,"props":4659,"children":4661},{"className":4660},[],[4662],{"type":48,"value":4663},"stretch",{"type":48,"value":4665}," — stretch also widens the surface to at least the anchor's width, the select-menu look), ",{"type":42,"tag":65,"props":4667,"children":4669},{"className":4668},[],[4670],{"type":48,"value":4671},"anchor-offset",{"type":48,"value":4638},{"type":42,"tag":65,"props":4674,"children":4676},{"className":4675},[],[4677],{"type":48,"value":2702},{"type":48,"value":4679},": literal gap in points, default 4), ",{"type":42,"tag":65,"props":4681,"children":4683},{"className":4682},[],[4684],{"type":48,"value":2686},{"type":48,"value":2620},{"type":42,"tag":65,"props":4687,"children":4689},{"className":4688},[],[4690],{"type":48,"value":2587},{"type":48,"value":4692}," only, beside ",{"type":42,"tag":65,"props":4694,"children":4696},{"className":4695},[],[4697],{"type":48,"value":2702},{"type":48,"value":4699}," — a teaching error elsewhere or without it: hover-intent show delay in ms, default 600, ",{"type":42,"tag":65,"props":4701,"children":4703},{"className":4702},[],[4704],{"type":48,"value":2694},{"type":48,"value":4706}," = instant; keyboard-focus reveals are always immediate), ",{"type":42,"tag":65,"props":4708,"children":4710},{"className":4709},[],[4711],{"type":48,"value":4712},"overscroll",{"type":48,"value":2620},{"type":42,"tag":65,"props":4715,"children":4717},{"className":4716},[],[4718],{"type":48,"value":1008},{"type":48,"value":4720}," only, a teaching error elsewhere: ",{"type":42,"tag":65,"props":4722,"children":4724},{"className":4723},[],[4725],{"type":48,"value":4726},"none",{"type":48,"value":4728}," pins the region at its content edges — the shipped default via the ",{"type":42,"tag":65,"props":4730,"children":4732},{"className":4731},[],[4733],{"type":48,"value":1089},{"type":48,"value":4735}," token — ",{"type":42,"tag":65,"props":4737,"children":4739},{"className":4738},[],[4740],{"type":48,"value":4741},"rubber_band",{"type":48,"value":4743}," lets it bounce past them on both the engine and native paths, ",{"type":42,"tag":65,"props":4745,"children":4747},{"className":4746},[],[4748],{"type":48,"value":4749},"default",{"type":48,"value":4751}," follows the token).\nAppearance\u002Fstate: ",{"type":42,"tag":65,"props":4753,"children":4755},{"className":4754},[],[4756],{"type":48,"value":3672},{"type":48,"value":4758}," (default|primary|secondary|outline|ghost|destructive), ",{"type":42,"tag":65,"props":4760,"children":4762},{"className":4761},[],[4763],{"type":48,"value":4764},"size",{"type":48,"value":4766}," (the control scale default|sm|lg|icon on every sized element; on ",{"type":42,"tag":65,"props":4768,"children":4770},{"className":4769},[],[4771],{"type":48,"value":48},{"type":48,"value":4773}," also the typography rungs heading|display — named typography token steps (",{"type":42,"tag":65,"props":4775,"children":4777},{"className":4776},[],[4778],{"type":48,"value":4779},"heading_size",{"type":48,"value":4781}," 28, ",{"type":42,"tag":65,"props":4783,"children":4785},{"className":4784},[],[4786],{"type":48,"value":4787},"display_size",{"type":48,"value":4789}," 48, themable like every token) for section headings and hero stats\u002Ftimer numerals. The two axes stay apart: heading\u002Fdisplay on a control is a teaching error naming text as their home, unknown values list the vocabulary, and numeric sizes are refused by design — retheme the typography tokens to move the whole scale), ",{"type":42,"tag":65,"props":4791,"children":4793},{"className":4792},[],[4794],{"type":48,"value":1206},{"type":48,"value":1820},{"type":42,"tag":65,"props":4797,"children":4799},{"className":4798},[],[4800],{"type":48,"value":2992},{"type":48,"value":1820},{"type":42,"tag":65,"props":4803,"children":4805},{"className":4804},[],[4806],{"type":48,"value":2269},{"type":48,"value":1820},{"type":42,"tag":65,"props":4809,"children":4811},{"className":4810},[],[4812],{"type":48,"value":2479},{"type":48,"value":1820},{"type":42,"tag":65,"props":4815,"children":4817},{"className":4816},[],[4818],{"type":48,"value":2921},{"type":48,"value":1820},{"type":42,"tag":65,"props":4821,"children":4823},{"className":4822},[],[4824],{"type":48,"value":3301},{"type":48,"value":2620},{"type":42,"tag":65,"props":4827,"children":4829},{"className":4828},[],[4830],{"type":48,"value":2795},{"type":48,"value":1820},{"type":42,"tag":65,"props":4833,"children":4835},{"className":4834},[],[4836],{"type":48,"value":2802},{"type":48,"value":1820},{"type":42,"tag":65,"props":4839,"children":4841},{"className":4840},[],[4842],{"type":48,"value":1269},{"type":48,"value":1820},{"type":42,"tag":65,"props":4845,"children":4847},{"className":4846},[],[4848],{"type":48,"value":1191},{"type":48,"value":4850},": vector icon drawn inline — buttons\u002Ftoggle-buttons before the label, list\u002Fmenu items as a leading slot; a teaching error anywhere else. A built-in name, ",{"type":42,"tag":65,"props":4852,"children":4854},{"className":4853},[],[4855],{"type":48,"value":3323},{"type":48,"value":4857},", or one ",{"type":42,"tag":65,"props":4859,"children":4861},{"className":4860},[],[4862],{"type":48,"value":3355},{"type":48,"value":4864}," resolving to such a name). ",{"type":42,"tag":513,"props":4866,"children":4867},{},[4868],{"type":48,"value":4869},"One size register per row",{"type":48,"value":4871},": every control class shares the control height at a given register (default 36, sm 31.5, lg 40.5 before density), so a toolbar\u002Ffilter row reads as one height exactly when every control in it carries the SAME ",{"type":42,"tag":65,"props":4873,"children":4875},{"className":4874},[],[4876],{"type":48,"value":4764},{"type":48,"value":4878}," — mixing ",{"type":42,"tag":65,"props":4880,"children":4882},{"className":4881},[],[4883],{"type":48,"value":4884},"size=\"sm\"",{"type":48,"value":4886}," buttons with a default field renders two heights in one row, and hand-sized pressable panels (",{"type":42,"tag":65,"props":4888,"children":4890},{"className":4889},[],[4891],{"type":48,"value":4892},"height=\"30\"",{"type":48,"value":4894},") never land on the scale; compose rows from real controls at one register.\nFocus: ",{"type":42,"tag":65,"props":4896,"children":4898},{"className":4897},[],[4899],{"type":48,"value":4012},{"type":48,"value":4901}," (focusable controls only — a teaching error elsewhere): moves keyboard focus to the element when it MOUNTS or when the bound value turns on, edge-triggered so holding it true never re-steals focus from the user. The TEA way to focus an editor on note-create (",{"type":42,"tag":65,"props":4903,"children":4905},{"className":4904},[],[4906],{"type":48,"value":4907},"\u003Ctext-field autofocus=\"{editing}\" ...>",{"type":48,"value":4909}," or mount the field under an ",{"type":42,"tag":65,"props":4911,"children":4913},{"className":4912},[],[4914],{"type":48,"value":2422},{"type":48,"value":157},{"type":42,"tag":65,"props":4917,"children":4919},{"className":4918},[],[4920],{"type":48,"value":4921},"autofocus=\"true\"",{"type":48,"value":4923},"; Zig views use ",{"type":42,"tag":65,"props":4925,"children":4927},{"className":4926},[],[4928],{"type":48,"value":4929},"ElementOptions.autofocus",{"type":48,"value":4931},") and to give keyboard-first apps their first focus without a click.\nSemantics: ",{"type":42,"tag":65,"props":4933,"children":4935},{"className":4934},[],[4936],{"type":48,"value":4937},"role",{"type":48,"value":4939}," (listitem, treeitem, button, ...; ",{"type":42,"tag":65,"props":4941,"children":4943},{"className":4942},[],[4944],{"type":48,"value":4945},"treeitem",{"type":48,"value":4947}," also makes the row part of its tree's roving keyboard focus set), ",{"type":42,"tag":65,"props":4949,"children":4951},{"className":4950},[],[4952],{"type":48,"value":2892},{"type":48,"value":4954}," (accessible name — it REPLACES the element's text content as the announced name, so snapshot greps and screen readers see the label, never the text; don't ",{"type":42,"tag":65,"props":4956,"children":4958},{"className":4957},[],[4959],{"type":48,"value":2892},{"type":48,"value":4961}," an element whose visible text you grep for), ",{"type":42,"tag":65,"props":4963,"children":4965},{"className":4964},[],[4966],{"type":48,"value":2553},{"type":48,"value":4968}," (tree rows: disclosure state, model-owned — omit on leaves). Accessible names are ENFORCED: an interactive control with no text content, no ",{"type":42,"tag":65,"props":4970,"children":4972},{"className":4971},[],[4973],{"type":48,"value":2371},{"type":48,"value":4975},", and no ",{"type":42,"tag":65,"props":4977,"children":4979},{"className":4978},[],[4980],{"type":48,"value":3014},{"type":48,"value":4982}," is a validation error (icon-only controls need ",{"type":42,"tag":65,"props":4984,"children":4986},{"className":4985},[],[4987],{"type":48,"value":2892},{"type":48,"value":4989},"; text-entry controls need ",{"type":42,"tag":65,"props":4991,"children":4993},{"className":4992},[],[4994],{"type":48,"value":2892},{"type":48,"value":3426},{"type":42,"tag":65,"props":4997,"children":4999},{"className":4998},[],[5000],{"type":48,"value":2921},{"type":48,"value":5002},"), unknown\u002Fmisused literal roles are errors (",{"type":42,"tag":65,"props":5004,"children":5006},{"className":5005},[],[5007],{"type":48,"value":5008},"role=\"tree\"",{"type":48,"value":5010}," on a text leaf can never hold rows), unnamed avatars and labels duplicating the text content are warnings (",{"type":42,"tag":65,"props":5012,"children":5014},{"className":5013},[],[5015],{"type":48,"value":5016},"label=\"\"",{"type":48,"value":5018}," marks an image decorative). Zig-built trees get the same discipline from ",{"type":42,"tag":65,"props":5020,"children":5022},{"className":5021},[],[5023],{"type":48,"value":5024},"canvas.expectA11yAuditSweepClean",{"type":48,"value":5026}," (missing names as the bridges would announce them, focusables clipped out of keyboard reach, identically labeled siblings) — adopt it next to the layout sweep.\nIdentity: ",{"type":42,"tag":65,"props":5028,"children":5030},{"className":5029},[],[5031],{"type":48,"value":3595},{"type":48,"value":5033}," (sibling-scoped), ",{"type":42,"tag":65,"props":5035,"children":5037},{"className":5036},[],[5038],{"type":48,"value":3602},{"type":48,"value":5040}," (parent-independent — use for items that move between containers, e.g. board cards; ids then survive reparenting).\nWindow chrome: ",{"type":42,"tag":65,"props":5042,"children":5044},{"className":5043},[],[5045],{"type":48,"value":5046},"window-drag=\"true\"",{"type":48,"value":5048}," (Zig: ",{"type":42,"tag":65,"props":5050,"children":5052},{"className":5051},[],[5053],{"type":48,"value":5054},".window_drag = true",{"type":48,"value":5056},") marks the element as a window-drag surface for hidden-titlebar windows — pressing its background or plain text\u002Ficons inside moves the WINDOW (drag starts only on actual movement), double-click zooms per the OS convention, and press-claiming children (buttons, fields) stay fully interactive via the ordinary press fall-through. macOS-only; elsewhere the press is dead space. See \"Hidden titlebar\" below.\nRender channel (Zig-only, no markup attributes): ",{"type":42,"tag":65,"props":5058,"children":5060},{"className":5059},[],[5061],{"type":48,"value":5062},"ElementOptions.opacity",{"type":48,"value":2555},{"type":42,"tag":65,"props":5065,"children":5067},{"className":5066},[],[5068],{"type":48,"value":5069},"ElementOptions.transform",{"type":48,"value":5071}," wrap the element's emitted commands without reflowing siblings — the defaults (1, identity) emit nothing, opacity 0 culls painting (pair with ",{"type":42,"tag":65,"props":5073,"children":5075},{"className":5074},[],[5076],{"type":48,"value":1206},{"type":48,"value":5078}," when fading interactive content), and a transform moves both rendering and pointer hit-testing while accessibility frames stay at the layout frame. Pair with ",{"type":42,"tag":65,"props":5080,"children":5082},{"className":5081},[],[5083],{"type":48,"value":5084},"UiApp.Options.animations",{"type":48,"value":5086}," for tweening.",{"type":42,"tag":51,"props":5088,"children":5089},{},[5090,5092,5098,5100,5106,5107,5113],{"type":48,"value":5091},"Numbers are plain (",{"type":42,"tag":65,"props":5093,"children":5095},{"className":5094},[],[5096],{"type":48,"value":5097},"gap=\"12\"",{"type":48,"value":5099},"), booleans are ",{"type":42,"tag":65,"props":5101,"children":5103},{"className":5102},[],[5104],{"type":48,"value":5105},"true",{"type":48,"value":456},{"type":42,"tag":65,"props":5108,"children":5110},{"className":5109},[],[5111],{"type":48,"value":5112},"false",{"type":48,"value":5114}," or a binding.",{"type":42,"tag":51,"props":5116,"children":5117},{},[5118,5120,5126,5128,5134,5136,5142,5144,5150],{"type":48,"value":5119},"When children's minimum sizes exceed their container, debug builds log a ",{"type":42,"tag":65,"props":5121,"children":5123},{"className":5122},[],[5124],{"type":48,"value":5125},"zero_canvas_layout",{"type":48,"value":5127}," diagnostic naming the container, axis, and overflow in pixels — flex overflow is never silent. In Zig views, ",{"type":42,"tag":65,"props":5129,"children":5131},{"className":5130},[],[5132],{"type":48,"value":5133},".gap",{"type":48,"value":5135}," on a stacking kind (",{"type":42,"tag":65,"props":5137,"children":5139},{"className":5138},[],[5140],{"type":48,"value":5141},"ui.panel(.{ .gap = 8 }, ...)",{"type":48,"value":5143},") logs a ",{"type":42,"tag":65,"props":5145,"children":5147},{"className":5146},[],[5148],{"type":48,"value":5149},"zero_canvas_ui",{"type":48,"value":5151}," warning in debug builds with the same lesson — it never fails the build.",{"type":42,"tag":612,"props":5153,"children":5155},{"id":5154},"chips-exclusive-selection-with-selected",[5156,5158],{"type":48,"value":5157},"Chips: exclusive selection with ",{"type":42,"tag":65,"props":5159,"children":5161},{"className":5160},[],[5162],{"type":48,"value":5163},"selected=",{"type":42,"tag":51,"props":5165,"children":5166},{},[5167,5169,5174,5176,5181,5183,5188,5190,5195],{"type":48,"value":5168},"The chip pattern — an exclusive group where the model owns which one is active — is a ",{"type":42,"tag":65,"props":5170,"children":5172},{"className":5171},[],[5173],{"type":48,"value":2113},{"type":48,"value":5175}," of ",{"type":42,"tag":65,"props":5177,"children":5179},{"className":5178},[],[5180],{"type":48,"value":2802},{"type":48,"value":5182},"s (or plain ",{"type":42,"tag":65,"props":5184,"children":5186},{"className":5185},[],[5187],{"type":48,"value":2795},{"type":48,"value":5189},"s) whose ",{"type":42,"tag":65,"props":5191,"children":5193},{"className":5192},[],[5194],{"type":48,"value":5163},{"type":48,"value":5196}," binds the model:",{"type":42,"tag":219,"props":5198,"children":5200},{"className":1248,"code":5199,"language":1250,"meta":223,"style":223},"\u003Ctoggle-group gap=\"2\" label=\"Theme\">\n  \u003Cfor each=\"theme_prefs\" as=\"p\">\n    \u003Ctoggle-button size=\"sm\" selected=\"{p == theme_pref}\" on-toggle=\"set_theme:{p}\">{p}\u003C\u002Ftoggle-button>\n  \u003C\u002Ffor>\n\u003C\u002Ftoggle-group>\n",[5201],{"type":42,"tag":65,"props":5202,"children":5203},{"__ignoreMap":223},[5204,5262,5320,5418,5433],{"type":42,"tag":229,"props":5205,"children":5206},{"class":231,"line":232},[5207,5211,5215,5220,5224,5228,5233,5237,5241,5245,5249,5254,5258],{"type":42,"tag":229,"props":5208,"children":5209},{"style":1260},[5210],{"type":48,"value":1263},{"type":42,"tag":229,"props":5212,"children":5213},{"style":1266},[5214],{"type":48,"value":2113},{"type":42,"tag":229,"props":5216,"children":5217},{"style":1272},[5218],{"type":48,"value":5219}," gap",{"type":42,"tag":229,"props":5221,"children":5222},{"style":1260},[5223],{"type":48,"value":1280},{"type":42,"tag":229,"props":5225,"children":5226},{"style":1260},[5227],{"type":48,"value":1285},{"type":42,"tag":229,"props":5229,"children":5230},{"style":1288},[5231],{"type":48,"value":5232},"2",{"type":42,"tag":229,"props":5234,"children":5235},{"style":1260},[5236],{"type":48,"value":1285},{"type":42,"tag":229,"props":5238,"children":5239},{"style":1272},[5240],{"type":48,"value":1300},{"type":42,"tag":229,"props":5242,"children":5243},{"style":1260},[5244],{"type":48,"value":1280},{"type":42,"tag":229,"props":5246,"children":5247},{"style":1260},[5248],{"type":48,"value":1285},{"type":42,"tag":229,"props":5250,"children":5251},{"style":1288},[5252],{"type":48,"value":5253},"Theme",{"type":42,"tag":229,"props":5255,"children":5256},{"style":1260},[5257],{"type":48,"value":1285},{"type":42,"tag":229,"props":5259,"children":5260},{"style":1260},[5261],{"type":48,"value":1322},{"type":42,"tag":229,"props":5263,"children":5264},{"class":231,"line":241},[5265,5269,5273,5278,5282,5286,5291,5295,5300,5304,5308,5312,5316],{"type":42,"tag":229,"props":5266,"children":5267},{"style":1260},[5268],{"type":48,"value":1330},{"type":42,"tag":229,"props":5270,"children":5271},{"style":1266},[5272],{"type":48,"value":1236},{"type":42,"tag":229,"props":5274,"children":5275},{"style":1272},[5276],{"type":48,"value":5277}," each",{"type":42,"tag":229,"props":5279,"children":5280},{"style":1260},[5281],{"type":48,"value":1280},{"type":42,"tag":229,"props":5283,"children":5284},{"style":1260},[5285],{"type":48,"value":1285},{"type":42,"tag":229,"props":5287,"children":5288},{"style":1288},[5289],{"type":48,"value":5290},"theme_prefs",{"type":42,"tag":229,"props":5292,"children":5293},{"style":1260},[5294],{"type":48,"value":1285},{"type":42,"tag":229,"props":5296,"children":5297},{"style":1272},[5298],{"type":48,"value":5299}," as",{"type":42,"tag":229,"props":5301,"children":5302},{"style":1260},[5303],{"type":48,"value":1280},{"type":42,"tag":229,"props":5305,"children":5306},{"style":1260},[5307],{"type":48,"value":1285},{"type":42,"tag":229,"props":5309,"children":5310},{"style":1288},[5311],{"type":48,"value":51},{"type":42,"tag":229,"props":5313,"children":5314},{"style":1260},[5315],{"type":48,"value":1285},{"type":42,"tag":229,"props":5317,"children":5318},{"style":1260},[5319],{"type":48,"value":1322},{"type":42,"tag":229,"props":5321,"children":5322},{"class":231,"line":251},[5323,5327,5331,5336,5340,5344,5349,5353,5358,5362,5366,5371,5375,5380,5384,5388,5393,5397,5401,5406,5410,5414],{"type":42,"tag":229,"props":5324,"children":5325},{"style":1260},[5326],{"type":48,"value":1403},{"type":42,"tag":229,"props":5328,"children":5329},{"style":1266},[5330],{"type":48,"value":2802},{"type":42,"tag":229,"props":5332,"children":5333},{"style":1272},[5334],{"type":48,"value":5335}," size",{"type":42,"tag":229,"props":5337,"children":5338},{"style":1260},[5339],{"type":48,"value":1280},{"type":42,"tag":229,"props":5341,"children":5342},{"style":1260},[5343],{"type":48,"value":1285},{"type":42,"tag":229,"props":5345,"children":5346},{"style":1288},[5347],{"type":48,"value":5348},"sm",{"type":42,"tag":229,"props":5350,"children":5351},{"style":1260},[5352],{"type":48,"value":1285},{"type":42,"tag":229,"props":5354,"children":5355},{"style":1272},[5356],{"type":48,"value":5357}," selected",{"type":42,"tag":229,"props":5359,"children":5360},{"style":1260},[5361],{"type":48,"value":1280},{"type":42,"tag":229,"props":5363,"children":5364},{"style":1260},[5365],{"type":48,"value":1285},{"type":42,"tag":229,"props":5367,"children":5368},{"style":1288},[5369],{"type":48,"value":5370},"{p == theme_pref}",{"type":42,"tag":229,"props":5372,"children":5373},{"style":1260},[5374],{"type":48,"value":1285},{"type":42,"tag":229,"props":5376,"children":5377},{"style":1272},[5378],{"type":48,"value":5379}," on-toggle",{"type":42,"tag":229,"props":5381,"children":5382},{"style":1260},[5383],{"type":48,"value":1280},{"type":42,"tag":229,"props":5385,"children":5386},{"style":1260},[5387],{"type":48,"value":1285},{"type":42,"tag":229,"props":5389,"children":5390},{"style":1288},[5391],{"type":48,"value":5392},"set_theme:{p}",{"type":42,"tag":229,"props":5394,"children":5395},{"style":1260},[5396],{"type":48,"value":1285},{"type":42,"tag":229,"props":5398,"children":5399},{"style":1260},[5400],{"type":48,"value":1361},{"type":42,"tag":229,"props":5402,"children":5403},{"style":1364},[5404],{"type":48,"value":5405},"{p}",{"type":42,"tag":229,"props":5407,"children":5408},{"style":1260},[5409],{"type":48,"value":1371},{"type":42,"tag":229,"props":5411,"children":5412},{"style":1266},[5413],{"type":48,"value":2802},{"type":42,"tag":229,"props":5415,"children":5416},{"style":1260},[5417],{"type":48,"value":1322},{"type":42,"tag":229,"props":5419,"children":5420},{"class":231,"line":260},[5421,5425,5429],{"type":42,"tag":229,"props":5422,"children":5423},{"style":1260},[5424],{"type":48,"value":1700},{"type":42,"tag":229,"props":5426,"children":5427},{"style":1266},[5428],{"type":48,"value":1236},{"type":42,"tag":229,"props":5430,"children":5431},{"style":1260},[5432],{"type":48,"value":1322},{"type":42,"tag":229,"props":5434,"children":5435},{"class":231,"line":269},[5436,5440,5444],{"type":42,"tag":229,"props":5437,"children":5438},{"style":1260},[5439],{"type":48,"value":1371},{"type":42,"tag":229,"props":5441,"children":5442},{"style":1266},[5443],{"type":48,"value":2113},{"type":42,"tag":229,"props":5445,"children":5446},{"style":1260},[5447],{"type":48,"value":1322},{"type":42,"tag":51,"props":5449,"children":5450},{},[5451,5453,5458,5460,5465,5467,5472,5474,5479,5481,5486,5487,5492,5494,5499,5500,5505,5507,5512,5514,5519,5521,5526],{"type":48,"value":5452},"A ",{"type":42,"tag":65,"props":5454,"children":5456},{"className":5455},[],[5457],{"type":48,"value":2802},{"type":48,"value":5459}," whose source asserts ",{"type":42,"tag":65,"props":5461,"children":5463},{"className":5462},[],[5464],{"type":48,"value":2269},{"type":48,"value":5466}," (this rebuild or the previous one) is model-driven: the source wins over the runtime's retained toggle on every rebuild, so exactly the model's selection is active — pressing a chip dispatches the Msg, the model moves the selection, and the old chip deactivates. Without a ",{"type":42,"tag":65,"props":5468,"children":5470},{"className":5469},[],[5471],{"type":48,"value":5163},{"type":48,"value":5473}," that ever asserts, a ",{"type":42,"tag":65,"props":5475,"children":5477},{"className":5476},[],[5478],{"type":48,"value":2802},{"type":48,"value":5480}," is uncontrolled: the runtime retains its pressed state across rebuilds (the multi-select formatting-bar case — bold\u002Fitalic chips with zero app wiring). ",{"type":42,"tag":65,"props":5482,"children":5484},{"className":5483},[],[5485],{"type":48,"value":2795},{"type":48,"value":157},{"type":42,"tag":65,"props":5488,"children":5490},{"className":5489},[],[5491],{"type":48,"value":5163},{"type":48,"value":5493}," is always model-driven (buttons never retain state) and dispatches ",{"type":42,"tag":65,"props":5495,"children":5497},{"className":5496},[],[5498],{"type":48,"value":1176},{"type":48,"value":2930},{"type":42,"tag":65,"props":5501,"children":5503},{"className":5502},[],[5504],{"type":48,"value":2802},{"type":48,"value":5506}," dispatches ",{"type":42,"tag":65,"props":5508,"children":5510},{"className":5509},[],[5511],{"type":48,"value":2277},{"type":48,"value":5513}," (its activation is the toggle intent — an ",{"type":42,"tag":65,"props":5515,"children":5517},{"className":5516},[],[5518],{"type":48,"value":1176},{"type":48,"value":5520}," there never fires). Model-driven chips need a handler that actually moves the model: a chip whose Msg is ignored keeps its retained press until the model asserts its ",{"type":42,"tag":65,"props":5522,"children":5524},{"className":5523},[],[5525],{"type":48,"value":5163},{"type":48,"value":123},{"type":42,"tag":612,"props":5528,"children":5530},{"id":5529},"pickers-select-is-the-trigger-compose-the-options-as-an-anchored-dropdown",[5531],{"type":48,"value":5532},"Pickers: select is the trigger — compose the options as an ANCHORED dropdown",{"type":42,"tag":51,"props":5534,"children":5535},{},[5536,5541,5542,5547,5549,5554,5556,5561,5563,5568,5570,5575,5577,5583,5585,5590,5592,5597,5598,5603,5605,5610,5612,5617,5619,5624],{"type":42,"tag":65,"props":5537,"children":5539},{"className":5538},[],[5540],{"type":48,"value":2835},{"type":48,"value":2555},{"type":42,"tag":65,"props":5543,"children":5545},{"className":5544},[],[5546],{"type":48,"value":3093},{"type":48,"value":5548}," are trigger controls, not complete pickers: ",{"type":42,"tag":65,"props":5550,"children":5552},{"className":5551},[],[5553],{"type":48,"value":2835},{"type":48,"value":5555}," renders the closed dropdown shape (current value as content, ",{"type":42,"tag":65,"props":5557,"children":5559},{"className":5558},[],[5560],{"type":48,"value":2921},{"type":48,"value":5562}," while empty, ",{"type":42,"tag":65,"props":5564,"children":5566},{"className":5565},[],[5567],{"type":48,"value":1176},{"type":48,"value":5569}," to open) and ",{"type":42,"tag":65,"props":5571,"children":5573},{"className":5572},[],[5574],{"type":48,"value":3093},{"type":48,"value":5576}," is a text entry with a menu chevron — neither owns an options list. There is no ",{"type":42,"tag":65,"props":5578,"children":5580},{"className":5579},[],[5581],{"type":48,"value":5582},"options=",{"type":48,"value":5584}," attribute (the closed grammar has no list-valued attributes — list-shaped vocabulary is element children, the way ",{"type":42,"tag":65,"props":5586,"children":5588},{"className":5587},[],[5589],{"type":48,"value":1168},{"type":48,"value":5591}," declares its items); the options ARE the composition — a ",{"type":42,"tag":65,"props":5593,"children":5595},{"className":5594},[],[5596],{"type":48,"value":2204},{"type":48,"value":5175},{"type":42,"tag":65,"props":5599,"children":5601},{"className":5600},[],[5602],{"type":48,"value":1191},{"type":48,"value":5604},"s under an ",{"type":42,"tag":65,"props":5606,"children":5608},{"className":5607},[],[5609],{"type":48,"value":1222},{"type":48,"value":5611},", beside the trigger inside a ",{"type":42,"tag":65,"props":5613,"children":5615},{"className":5614},[],[5616],{"type":48,"value":1992},{"type":48,"value":5618},", floated with ",{"type":42,"tag":65,"props":5620,"children":5622},{"className":5621},[],[5623],{"type":48,"value":2702},{"type":48,"value":5625},":",{"type":42,"tag":219,"props":5627,"children":5629},{"className":1248,"code":5628,"language":1250,"meta":223,"style":223},"\u003Cstack>\n  \u003Cselect placeholder=\"Pick a repo\" text=\"{current_repo}\" on-press=\"toggle_repo_picker\"\u002F>\n  \u003Cif test=\"{repo_picker_open}\">\n    \u003Cdropdown-menu anchor=\"below\" anchor-alignment=\"stretch\" on-dismiss=\"close_repo_picker\">\n      \u003Cfor each=\"repos\" key=\"name\" as=\"r\">\n        \u003Cmenu-item on-press=\"pick_repo:{r.name}\" selected=\"{r.name == current_repo}\">{r.name}\u003C\u002Fmenu-item>\n      \u003C\u002Ffor>\n    \u003C\u002Fdropdown-menu>\n  \u003C\u002Fif>\n\u003C\u002Fstack>\n",[5630],{"type":42,"tag":65,"props":5631,"children":5632},{"__ignoreMap":223},[5633,5648,5729,5765,5844,5922,5997,6013,6028,6043],{"type":42,"tag":229,"props":5634,"children":5635},{"class":231,"line":232},[5636,5640,5644],{"type":42,"tag":229,"props":5637,"children":5638},{"style":1260},[5639],{"type":48,"value":1263},{"type":42,"tag":229,"props":5641,"children":5642},{"style":1266},[5643],{"type":48,"value":1992},{"type":42,"tag":229,"props":5645,"children":5646},{"style":1260},[5647],{"type":48,"value":1322},{"type":42,"tag":229,"props":5649,"children":5650},{"class":231,"line":241},[5651,5655,5659,5664,5668,5672,5677,5681,5686,5690,5694,5699,5703,5707,5711,5715,5720,5724],{"type":42,"tag":229,"props":5652,"children":5653},{"style":1260},[5654],{"type":48,"value":1330},{"type":42,"tag":229,"props":5656,"children":5657},{"style":1266},[5658],{"type":48,"value":2835},{"type":42,"tag":229,"props":5660,"children":5661},{"style":1272},[5662],{"type":48,"value":5663}," placeholder",{"type":42,"tag":229,"props":5665,"children":5666},{"style":1260},[5667],{"type":48,"value":1280},{"type":42,"tag":229,"props":5669,"children":5670},{"style":1260},[5671],{"type":48,"value":1285},{"type":42,"tag":229,"props":5673,"children":5674},{"style":1288},[5675],{"type":48,"value":5676},"Pick a repo",{"type":42,"tag":229,"props":5678,"children":5679},{"style":1260},[5680],{"type":48,"value":1285},{"type":42,"tag":229,"props":5682,"children":5683},{"style":1272},[5684],{"type":48,"value":5685}," text",{"type":42,"tag":229,"props":5687,"children":5688},{"style":1260},[5689],{"type":48,"value":1280},{"type":42,"tag":229,"props":5691,"children":5692},{"style":1260},[5693],{"type":48,"value":1285},{"type":42,"tag":229,"props":5695,"children":5696},{"style":1288},[5697],{"type":48,"value":5698},"{current_repo}",{"type":42,"tag":229,"props":5700,"children":5701},{"style":1260},[5702],{"type":48,"value":1285},{"type":42,"tag":229,"props":5704,"children":5705},{"style":1272},[5706],{"type":48,"value":1275},{"type":42,"tag":229,"props":5708,"children":5709},{"style":1260},[5710],{"type":48,"value":1280},{"type":42,"tag":229,"props":5712,"children":5713},{"style":1260},[5714],{"type":48,"value":1285},{"type":42,"tag":229,"props":5716,"children":5717},{"style":1288},[5718],{"type":48,"value":5719},"toggle_repo_picker",{"type":42,"tag":229,"props":5721,"children":5722},{"style":1260},[5723],{"type":48,"value":1285},{"type":42,"tag":229,"props":5725,"children":5726},{"style":1260},[5727],{"type":48,"value":5728},"\u002F>\n",{"type":42,"tag":229,"props":5730,"children":5731},{"class":231,"line":251},[5732,5736,5740,5744,5748,5752,5757,5761],{"type":42,"tag":229,"props":5733,"children":5734},{"style":1260},[5735],{"type":48,"value":1330},{"type":42,"tag":229,"props":5737,"children":5738},{"style":1266},[5739],{"type":48,"value":1222},{"type":42,"tag":229,"props":5741,"children":5742},{"style":1272},[5743],{"type":48,"value":1412},{"type":42,"tag":229,"props":5745,"children":5746},{"style":1260},[5747],{"type":48,"value":1280},{"type":42,"tag":229,"props":5749,"children":5750},{"style":1260},[5751],{"type":48,"value":1285},{"type":42,"tag":229,"props":5753,"children":5754},{"style":1288},[5755],{"type":48,"value":5756},"{repo_picker_open}",{"type":42,"tag":229,"props":5758,"children":5759},{"style":1260},[5760],{"type":48,"value":1285},{"type":42,"tag":229,"props":5762,"children":5763},{"style":1260},[5764],{"type":48,"value":1322},{"type":42,"tag":229,"props":5766,"children":5767},{"class":231,"line":260},[5768,5772,5776,5781,5785,5789,5793,5797,5802,5806,5810,5814,5818,5823,5827,5831,5836,5840],{"type":42,"tag":229,"props":5769,"children":5770},{"style":1260},[5771],{"type":48,"value":1403},{"type":42,"tag":229,"props":5773,"children":5774},{"style":1266},[5775],{"type":48,"value":2204},{"type":42,"tag":229,"props":5777,"children":5778},{"style":1272},[5779],{"type":48,"value":5780}," anchor",{"type":42,"tag":229,"props":5782,"children":5783},{"style":1260},[5784],{"type":48,"value":1280},{"type":42,"tag":229,"props":5786,"children":5787},{"style":1260},[5788],{"type":48,"value":1285},{"type":42,"tag":229,"props":5790,"children":5791},{"style":1288},[5792],{"type":48,"value":4621},{"type":42,"tag":229,"props":5794,"children":5795},{"style":1260},[5796],{"type":48,"value":1285},{"type":42,"tag":229,"props":5798,"children":5799},{"style":1272},[5800],{"type":48,"value":5801}," anchor-alignment",{"type":42,"tag":229,"props":5803,"children":5804},{"style":1260},[5805],{"type":48,"value":1280},{"type":42,"tag":229,"props":5807,"children":5808},{"style":1260},[5809],{"type":48,"value":1285},{"type":42,"tag":229,"props":5811,"children":5812},{"style":1288},[5813],{"type":48,"value":4663},{"type":42,"tag":229,"props":5815,"children":5816},{"style":1260},[5817],{"type":48,"value":1285},{"type":42,"tag":229,"props":5819,"children":5820},{"style":1272},[5821],{"type":48,"value":5822}," on-dismiss",{"type":42,"tag":229,"props":5824,"children":5825},{"style":1260},[5826],{"type":48,"value":1280},{"type":42,"tag":229,"props":5828,"children":5829},{"style":1260},[5830],{"type":48,"value":1285},{"type":42,"tag":229,"props":5832,"children":5833},{"style":1288},[5834],{"type":48,"value":5835},"close_repo_picker",{"type":42,"tag":229,"props":5837,"children":5838},{"style":1260},[5839],{"type":48,"value":1285},{"type":42,"tag":229,"props":5841,"children":5842},{"style":1260},[5843],{"type":48,"value":1322},{"type":42,"tag":229,"props":5845,"children":5846},{"class":231,"line":269},[5847,5851,5855,5859,5863,5867,5872,5876,5881,5885,5889,5893,5897,5901,5905,5909,5914,5918],{"type":42,"tag":229,"props":5848,"children":5849},{"style":1260},[5850],{"type":48,"value":1441},{"type":42,"tag":229,"props":5852,"children":5853},{"style":1266},[5854],{"type":48,"value":1236},{"type":42,"tag":229,"props":5856,"children":5857},{"style":1272},[5858],{"type":48,"value":5277},{"type":42,"tag":229,"props":5860,"children":5861},{"style":1260},[5862],{"type":48,"value":1280},{"type":42,"tag":229,"props":5864,"children":5865},{"style":1260},[5866],{"type":48,"value":1285},{"type":42,"tag":229,"props":5868,"children":5869},{"style":1288},[5870],{"type":48,"value":5871},"repos",{"type":42,"tag":229,"props":5873,"children":5874},{"style":1260},[5875],{"type":48,"value":1285},{"type":42,"tag":229,"props":5877,"children":5878},{"style":1272},[5879],{"type":48,"value":5880}," key",{"type":42,"tag":229,"props":5882,"children":5883},{"style":1260},[5884],{"type":48,"value":1280},{"type":42,"tag":229,"props":5886,"children":5887},{"style":1260},[5888],{"type":48,"value":1285},{"type":42,"tag":229,"props":5890,"children":5891},{"style":1288},[5892],{"type":48,"value":3315},{"type":42,"tag":229,"props":5894,"children":5895},{"style":1260},[5896],{"type":48,"value":1285},{"type":42,"tag":229,"props":5898,"children":5899},{"style":1272},[5900],{"type":48,"value":5299},{"type":42,"tag":229,"props":5902,"children":5903},{"style":1260},[5904],{"type":48,"value":1280},{"type":42,"tag":229,"props":5906,"children":5907},{"style":1260},[5908],{"type":48,"value":1285},{"type":42,"tag":229,"props":5910,"children":5911},{"style":1288},[5912],{"type":48,"value":5913},"r",{"type":42,"tag":229,"props":5915,"children":5916},{"style":1260},[5917],{"type":48,"value":1285},{"type":42,"tag":229,"props":5919,"children":5920},{"style":1260},[5921],{"type":48,"value":1322},{"type":42,"tag":229,"props":5923,"children":5924},{"class":231,"line":278},[5925,5930,5934,5938,5942,5946,5951,5955,5959,5963,5967,5972,5976,5980,5985,5989,5993],{"type":42,"tag":229,"props":5926,"children":5927},{"style":1260},[5928],{"type":48,"value":5929},"        \u003C",{"type":42,"tag":229,"props":5931,"children":5932},{"style":1266},[5933],{"type":48,"value":1191},{"type":42,"tag":229,"props":5935,"children":5936},{"style":1272},[5937],{"type":48,"value":1275},{"type":42,"tag":229,"props":5939,"children":5940},{"style":1260},[5941],{"type":48,"value":1280},{"type":42,"tag":229,"props":5943,"children":5944},{"style":1260},[5945],{"type":48,"value":1285},{"type":42,"tag":229,"props":5947,"children":5948},{"style":1288},[5949],{"type":48,"value":5950},"pick_repo:{r.name}",{"type":42,"tag":229,"props":5952,"children":5953},{"style":1260},[5954],{"type":48,"value":1285},{"type":42,"tag":229,"props":5956,"children":5957},{"style":1272},[5958],{"type":48,"value":5357},{"type":42,"tag":229,"props":5960,"children":5961},{"style":1260},[5962],{"type":48,"value":1280},{"type":42,"tag":229,"props":5964,"children":5965},{"style":1260},[5966],{"type":48,"value":1285},{"type":42,"tag":229,"props":5968,"children":5969},{"style":1288},[5970],{"type":48,"value":5971},"{r.name == current_repo}",{"type":42,"tag":229,"props":5973,"children":5974},{"style":1260},[5975],{"type":48,"value":1285},{"type":42,"tag":229,"props":5977,"children":5978},{"style":1260},[5979],{"type":48,"value":1361},{"type":42,"tag":229,"props":5981,"children":5982},{"style":1364},[5983],{"type":48,"value":5984},"{r.name}",{"type":42,"tag":229,"props":5986,"children":5987},{"style":1260},[5988],{"type":48,"value":1371},{"type":42,"tag":229,"props":5990,"children":5991},{"style":1266},[5992],{"type":48,"value":1191},{"type":42,"tag":229,"props":5994,"children":5995},{"style":1260},[5996],{"type":48,"value":1322},{"type":42,"tag":229,"props":5998,"children":5999},{"class":231,"line":287},[6000,6005,6009],{"type":42,"tag":229,"props":6001,"children":6002},{"style":1260},[6003],{"type":48,"value":6004},"      \u003C\u002F",{"type":42,"tag":229,"props":6006,"children":6007},{"style":1266},[6008],{"type":48,"value":1236},{"type":42,"tag":229,"props":6010,"children":6011},{"style":1260},[6012],{"type":48,"value":1322},{"type":42,"tag":229,"props":6014,"children":6015},{"class":231,"line":296},[6016,6020,6024],{"type":42,"tag":229,"props":6017,"children":6018},{"style":1260},[6019],{"type":48,"value":1548},{"type":42,"tag":229,"props":6021,"children":6022},{"style":1266},[6023],{"type":48,"value":2204},{"type":42,"tag":229,"props":6025,"children":6026},{"style":1260},[6027],{"type":48,"value":1322},{"type":42,"tag":229,"props":6029,"children":6030},{"class":231,"line":305},[6031,6035,6039],{"type":42,"tag":229,"props":6032,"children":6033},{"style":1260},[6034],{"type":48,"value":1700},{"type":42,"tag":229,"props":6036,"children":6037},{"style":1266},[6038],{"type":48,"value":1222},{"type":42,"tag":229,"props":6040,"children":6041},{"style":1260},[6042],{"type":48,"value":1322},{"type":42,"tag":229,"props":6044,"children":6045},{"class":231,"line":314},[6046,6050,6054],{"type":42,"tag":229,"props":6047,"children":6048},{"style":1260},[6049],{"type":48,"value":1371},{"type":42,"tag":229,"props":6051,"children":6052},{"style":1266},[6053],{"type":48,"value":1992},{"type":42,"tag":229,"props":6055,"children":6056},{"style":1260},[6057],{"type":48,"value":1322},{"type":42,"tag":51,"props":6059,"children":6060},{},[6061],{"type":48,"value":6062},"How the pieces fit, all model-owned (TEA):",{"type":42,"tag":57,"props":6064,"children":6065},{},[6066,6089,6119,6156,6173,6183],{"type":42,"tag":61,"props":6067,"children":6068},{},[6069,6074,6075,6080,6082,6087],{"type":42,"tag":513,"props":6070,"children":6071},{},[6072],{"type":48,"value":6073},"Open state is the model's.",{"type":48,"value":519},{"type":42,"tag":65,"props":6076,"children":6078},{"className":6077},[],[6079],{"type":48,"value":5719},{"type":48,"value":6081}," flips the bool; the surface exists only while the ",{"type":42,"tag":65,"props":6083,"children":6085},{"className":6084},[],[6086],{"type":48,"value":1222},{"type":48,"value":6088}," renders it. There is no hidden engine open flag.",{"type":42,"tag":61,"props":6090,"children":6091},{},[6092,6102,6104,6109,6111,6117],{"type":42,"tag":513,"props":6093,"children":6094},{},[6095,6100],{"type":42,"tag":65,"props":6096,"children":6098},{"className":6097},[],[6099],{"type":48,"value":2702},{"type":48,"value":6101}," floats the menu.",{"type":48,"value":6103}," The dropdown positions against its PARENT's frame (the ",{"type":42,"tag":65,"props":6105,"children":6107},{"className":6106},[],[6108],{"type":48,"value":1992},{"type":48,"value":6110},", sized by the trigger): below it by default, flipping above when it doesn't fit and the other side has more room, height clamped to the chosen side, x clamped into the window. It consumes NO space in the flow (siblings never reflow), paints in a late z-pass above the whole tree, and escapes every ancestor scroll\u002Fclip region — window-clipped, not pane-clipped. ",{"type":42,"tag":65,"props":6112,"children":6114},{"className":6113},[],[6115],{"type":48,"value":6116},"anchor-alignment=\"stretch\"",{"type":48,"value":6118}," widens it to at least the trigger's width (the select look).",{"type":42,"tag":61,"props":6120,"children":6121},{},[6122,6132,6134,6139,6141,6147,6149,6154],{"type":42,"tag":513,"props":6123,"children":6124},{},[6125,6130],{"type":42,"tag":65,"props":6126,"children":6128},{"className":6127},[],[6129],{"type":48,"value":2235},{"type":48,"value":6131}," closes it model-side.",{"type":48,"value":6133}," Escape and a click outside the menu dismiss the surface and dispatch the Msg; ",{"type":42,"tag":65,"props":6135,"children":6137},{"className":6136},[],[6138],{"type":48,"value":5835},{"type":48,"value":6140}," clears the bool. Escape works even when the trigger took no focus (a plain-text crumb): with no relevant focus chain it dismisses the topmost mounted anchored surface. The engine hides the surface immediately (the optimistic echo), and the next rebuild's source tree is truth — a model that keeps ",{"type":42,"tag":65,"props":6142,"children":6144},{"className":6143},[],[6145],{"type":48,"value":6146},"open",{"type":48,"value":6148}," true gets it back. Clicking the TRIGGER while open never double-fires: the anchor region owns its surface's toggling, so only ",{"type":42,"tag":65,"props":6150,"children":6152},{"className":6151},[],[6153],{"type":48,"value":5719},{"type":48,"value":6155}," dispatches.",{"type":42,"tag":61,"props":6157,"children":6158},{},[6159,6164,6165,6171],{"type":42,"tag":513,"props":6160,"children":6161},{},[6162],{"type":48,"value":6163},"Items close on pick.",{"type":48,"value":519},{"type":42,"tag":65,"props":6166,"children":6168},{"className":6167},[],[6169],{"type":48,"value":6170},"pick_repo",{"type":48,"value":6172}," sets the value AND clears the open flag — a click inside the surface never dismisses.",{"type":42,"tag":61,"props":6174,"children":6175},{},[6176,6181],{"type":42,"tag":513,"props":6177,"children":6178},{},[6179],{"type":48,"value":6180},"Keyboard",{"type":48,"value":6182},": once focus is in the menu (tab into it), tab wraps inside the surface (the floating focus scope) and Enter\u002FSpace activate items; Escape dismisses from the trigger or the menu.",{"type":42,"tag":61,"props":6184,"children":6185},{},[6186,6191,6193,6199],{"type":42,"tag":513,"props":6187,"children":6188},{},[6189],{"type":48,"value":6190},"Automation sees everything",{"type":48,"value":6192},": the floating menu and its items appear in widget snapshots at their real frames and ",{"type":42,"tag":65,"props":6194,"children":6196},{"className":6195},[],[6197],{"type":48,"value":6198},"widget-click \u003Citem-id>",{"type":48,"value":6200}," works while it is open.",{"type":42,"tag":51,"props":6202,"children":6203},{},[6204,6209,6211,6216,6218,6223,6225,6231,6232,6238,6239,6245,6246,6252,6254,6259,6261,6266,6267,6272,6274,6280,6282,6288,6290,6296,6298,6303,6305,6311],{"type":42,"tag":65,"props":6205,"children":6207},{"className":6206},[],[6208],{"type":48,"value":3093},{"type":48,"value":6210}," composes the same way (the model filters the ",{"type":42,"tag":65,"props":6212,"children":6214},{"className":6213},[],[6215],{"type":48,"value":1236},{"type":48,"value":6217}," source as the user types via ",{"type":42,"tag":65,"props":6219,"children":6221},{"className":6220},[],[6222],{"type":48,"value":3121},{"type":48,"value":6224},"). The Zig mirror is ",{"type":42,"tag":65,"props":6226,"children":6228},{"className":6227},[],[6229],{"type":48,"value":6230},"ElementOptions.anchor",{"type":48,"value":456},{"type":42,"tag":65,"props":6233,"children":6235},{"className":6234},[],[6236],{"type":48,"value":6237},"anchor_alignment",{"type":48,"value":456},{"type":42,"tag":65,"props":6240,"children":6242},{"className":6241},[],[6243],{"type":48,"value":6244},"anchor_offset",{"type":48,"value":3952},{"type":42,"tag":65,"props":6247,"children":6249},{"className":6248},[],[6250],{"type":48,"value":6251},"on_dismiss",{"type":48,"value":6253}," on a ",{"type":42,"tag":65,"props":6255,"children":6257},{"className":6256},[],[6258],{"type":48,"value":2209},{"type":48,"value":6260}," (or ",{"type":42,"tag":65,"props":6262,"children":6264},{"className":6263},[],[6265],{"type":48,"value":4148},{"type":48,"value":456},{"type":42,"tag":65,"props":6268,"children":6270},{"className":6269},[],[6271],{"type":48,"value":4155},{"type":48,"value":6273},", which stay Zig-only) built with ",{"type":42,"tag":65,"props":6275,"children":6277},{"className":6276},[],[6278],{"type":48,"value":6279},"ui.eachCtx",{"type":48,"value":6281}," for the options. Budget: at most 16 anchored surfaces may be mounted per view (",{"type":42,"tag":65,"props":6283,"children":6285},{"className":6284},[],[6286],{"type":48,"value":6287},"max_canvas_widget_anchored_per_view",{"type":48,"value":6289},", loud ",{"type":42,"tag":65,"props":6291,"children":6293},{"className":6292},[],[6294],{"type":48,"value":6295},"error.WidgetAnchoredSurfaceLimitReached",{"type":48,"value":6297},") — an ",{"type":42,"tag":65,"props":6299,"children":6301},{"className":6300},[],[6302],{"type":48,"value":2702},{"type":48,"value":6304}," inside a ",{"type":42,"tag":65,"props":6306,"children":6308},{"className":6307},[],[6309],{"type":48,"value":6310},"\u003Cfor>",{"type":48,"value":6312}," body is almost always a mistake.",{"type":42,"tag":612,"props":6314,"children":6316},{"id":6315},"splitters-split-panes-with-a-model-owned-fraction",[6317],{"type":48,"value":6318},"Splitters: split panes with a model-owned fraction",{"type":42,"tag":51,"props":6320,"children":6321},{},[6322,6327,6329,6334,6336,6341],{"type":42,"tag":65,"props":6323,"children":6325},{"className":6324},[],[6326],{"type":48,"value":2464},{"type":48,"value":6328}," is the resizable two-pane seam: exactly two element children, and the engine synthesizes the draggable divider between them (resize cursor, focusable, ARIA separator whose value is the fraction). The fraction is MODEL-OWNED — the runtime applies each drag\u002Fkeyboard step as an optimistic echo, dispatches ",{"type":42,"tag":65,"props":6330,"children":6332},{"className":6331},[],[6333],{"type":48,"value":2487},{"type":48,"value":6335}," with the applied fraction, and the model echoes it back through ",{"type":42,"tag":65,"props":6337,"children":6339},{"className":6338},[],[6340],{"type":48,"value":2479},{"type":48,"value":6342}," so the next rebuild lays the panes exactly there:",{"type":42,"tag":219,"props":6344,"children":6346},{"className":1248,"code":6345,"language":1250,"meta":223,"style":223},"\u003Csplit value=\"{sidebar_split}\" on-resize=\"sidebar_resized\">\n  \u003Ccolumn min-width=\"150\">…sidebar…\u003C\u002Fcolumn>\n  \u003Csplit value=\"{list_split}\" on-resize=\"list_resized\">\n    \u003Ccolumn min-width=\"220\">…list…\u003C\u002Fcolumn>\n    \u003Ccolumn min-width=\"280\">…editor…\u003C\u002Fcolumn>\n  \u003C\u002Fsplit>\n\u003C\u002Fsplit>\n",[6347],{"type":42,"tag":65,"props":6348,"children":6349},{"__ignoreMap":223},[6350,6409,6463,6520,6573,6626,6641],{"type":42,"tag":229,"props":6351,"children":6352},{"class":231,"line":232},[6353,6357,6361,6366,6370,6374,6379,6383,6388,6392,6396,6401,6405],{"type":42,"tag":229,"props":6354,"children":6355},{"style":1260},[6356],{"type":48,"value":1263},{"type":42,"tag":229,"props":6358,"children":6359},{"style":1266},[6360],{"type":48,"value":2464},{"type":42,"tag":229,"props":6362,"children":6363},{"style":1272},[6364],{"type":48,"value":6365}," value",{"type":42,"tag":229,"props":6367,"children":6368},{"style":1260},[6369],{"type":48,"value":1280},{"type":42,"tag":229,"props":6371,"children":6372},{"style":1260},[6373],{"type":48,"value":1285},{"type":42,"tag":229,"props":6375,"children":6376},{"style":1288},[6377],{"type":48,"value":6378},"{sidebar_split}",{"type":42,"tag":229,"props":6380,"children":6381},{"style":1260},[6382],{"type":48,"value":1285},{"type":42,"tag":229,"props":6384,"children":6385},{"style":1272},[6386],{"type":48,"value":6387}," on-resize",{"type":42,"tag":229,"props":6389,"children":6390},{"style":1260},[6391],{"type":48,"value":1280},{"type":42,"tag":229,"props":6393,"children":6394},{"style":1260},[6395],{"type":48,"value":1285},{"type":42,"tag":229,"props":6397,"children":6398},{"style":1288},[6399],{"type":48,"value":6400},"sidebar_resized",{"type":42,"tag":229,"props":6402,"children":6403},{"style":1260},[6404],{"type":48,"value":1285},{"type":42,"tag":229,"props":6406,"children":6407},{"style":1260},[6408],{"type":48,"value":1322},{"type":42,"tag":229,"props":6410,"children":6411},{"class":231,"line":241},[6412,6416,6420,6425,6429,6433,6438,6442,6446,6451,6455,6459],{"type":42,"tag":229,"props":6413,"children":6414},{"style":1260},[6415],{"type":48,"value":1330},{"type":42,"tag":229,"props":6417,"children":6418},{"style":1266},[6419],{"type":48,"value":1970},{"type":42,"tag":229,"props":6421,"children":6422},{"style":1272},[6423],{"type":48,"value":6424}," min-width",{"type":42,"tag":229,"props":6426,"children":6427},{"style":1260},[6428],{"type":48,"value":1280},{"type":42,"tag":229,"props":6430,"children":6431},{"style":1260},[6432],{"type":48,"value":1285},{"type":42,"tag":229,"props":6434,"children":6435},{"style":1288},[6436],{"type":48,"value":6437},"150",{"type":42,"tag":229,"props":6439,"children":6440},{"style":1260},[6441],{"type":48,"value":1285},{"type":42,"tag":229,"props":6443,"children":6444},{"style":1260},[6445],{"type":48,"value":1361},{"type":42,"tag":229,"props":6447,"children":6448},{"style":1364},[6449],{"type":48,"value":6450},"…sidebar…",{"type":42,"tag":229,"props":6452,"children":6453},{"style":1260},[6454],{"type":48,"value":1371},{"type":42,"tag":229,"props":6456,"children":6457},{"style":1266},[6458],{"type":48,"value":1970},{"type":42,"tag":229,"props":6460,"children":6461},{"style":1260},[6462],{"type":48,"value":1322},{"type":42,"tag":229,"props":6464,"children":6465},{"class":231,"line":251},[6466,6470,6474,6478,6482,6486,6491,6495,6499,6503,6507,6512,6516],{"type":42,"tag":229,"props":6467,"children":6468},{"style":1260},[6469],{"type":48,"value":1330},{"type":42,"tag":229,"props":6471,"children":6472},{"style":1266},[6473],{"type":48,"value":2464},{"type":42,"tag":229,"props":6475,"children":6476},{"style":1272},[6477],{"type":48,"value":6365},{"type":42,"tag":229,"props":6479,"children":6480},{"style":1260},[6481],{"type":48,"value":1280},{"type":42,"tag":229,"props":6483,"children":6484},{"style":1260},[6485],{"type":48,"value":1285},{"type":42,"tag":229,"props":6487,"children":6488},{"style":1288},[6489],{"type":48,"value":6490},"{list_split}",{"type":42,"tag":229,"props":6492,"children":6493},{"style":1260},[6494],{"type":48,"value":1285},{"type":42,"tag":229,"props":6496,"children":6497},{"style":1272},[6498],{"type":48,"value":6387},{"type":42,"tag":229,"props":6500,"children":6501},{"style":1260},[6502],{"type":48,"value":1280},{"type":42,"tag":229,"props":6504,"children":6505},{"style":1260},[6506],{"type":48,"value":1285},{"type":42,"tag":229,"props":6508,"children":6509},{"style":1288},[6510],{"type":48,"value":6511},"list_resized",{"type":42,"tag":229,"props":6513,"children":6514},{"style":1260},[6515],{"type":48,"value":1285},{"type":42,"tag":229,"props":6517,"children":6518},{"style":1260},[6519],{"type":48,"value":1322},{"type":42,"tag":229,"props":6521,"children":6522},{"class":231,"line":260},[6523,6527,6531,6535,6539,6543,6548,6552,6556,6561,6565,6569],{"type":42,"tag":229,"props":6524,"children":6525},{"style":1260},[6526],{"type":48,"value":1403},{"type":42,"tag":229,"props":6528,"children":6529},{"style":1266},[6530],{"type":48,"value":1970},{"type":42,"tag":229,"props":6532,"children":6533},{"style":1272},[6534],{"type":48,"value":6424},{"type":42,"tag":229,"props":6536,"children":6537},{"style":1260},[6538],{"type":48,"value":1280},{"type":42,"tag":229,"props":6540,"children":6541},{"style":1260},[6542],{"type":48,"value":1285},{"type":42,"tag":229,"props":6544,"children":6545},{"style":1288},[6546],{"type":48,"value":6547},"220",{"type":42,"tag":229,"props":6549,"children":6550},{"style":1260},[6551],{"type":48,"value":1285},{"type":42,"tag":229,"props":6553,"children":6554},{"style":1260},[6555],{"type":48,"value":1361},{"type":42,"tag":229,"props":6557,"children":6558},{"style":1364},[6559],{"type":48,"value":6560},"…list…",{"type":42,"tag":229,"props":6562,"children":6563},{"style":1260},[6564],{"type":48,"value":1371},{"type":42,"tag":229,"props":6566,"children":6567},{"style":1266},[6568],{"type":48,"value":1970},{"type":42,"tag":229,"props":6570,"children":6571},{"style":1260},[6572],{"type":48,"value":1322},{"type":42,"tag":229,"props":6574,"children":6575},{"class":231,"line":269},[6576,6580,6584,6588,6592,6596,6601,6605,6609,6614,6618,6622],{"type":42,"tag":229,"props":6577,"children":6578},{"style":1260},[6579],{"type":48,"value":1403},{"type":42,"tag":229,"props":6581,"children":6582},{"style":1266},[6583],{"type":48,"value":1970},{"type":42,"tag":229,"props":6585,"children":6586},{"style":1272},[6587],{"type":48,"value":6424},{"type":42,"tag":229,"props":6589,"children":6590},{"style":1260},[6591],{"type":48,"value":1280},{"type":42,"tag":229,"props":6593,"children":6594},{"style":1260},[6595],{"type":48,"value":1285},{"type":42,"tag":229,"props":6597,"children":6598},{"style":1288},[6599],{"type":48,"value":6600},"280",{"type":42,"tag":229,"props":6602,"children":6603},{"style":1260},[6604],{"type":48,"value":1285},{"type":42,"tag":229,"props":6606,"children":6607},{"style":1260},[6608],{"type":48,"value":1361},{"type":42,"tag":229,"props":6610,"children":6611},{"style":1364},[6612],{"type":48,"value":6613},"…editor…",{"type":42,"tag":229,"props":6615,"children":6616},{"style":1260},[6617],{"type":48,"value":1371},{"type":42,"tag":229,"props":6619,"children":6620},{"style":1266},[6621],{"type":48,"value":1970},{"type":42,"tag":229,"props":6623,"children":6624},{"style":1260},[6625],{"type":48,"value":1322},{"type":42,"tag":229,"props":6627,"children":6628},{"class":231,"line":278},[6629,6633,6637],{"type":42,"tag":229,"props":6630,"children":6631},{"style":1260},[6632],{"type":48,"value":1700},{"type":42,"tag":229,"props":6634,"children":6635},{"style":1266},[6636],{"type":48,"value":2464},{"type":42,"tag":229,"props":6638,"children":6639},{"style":1260},[6640],{"type":48,"value":1322},{"type":42,"tag":229,"props":6642,"children":6643},{"class":231,"line":287},[6644,6648,6652],{"type":42,"tag":229,"props":6645,"children":6646},{"style":1260},[6647],{"type":48,"value":1371},{"type":42,"tag":229,"props":6649,"children":6650},{"style":1266},[6651],{"type":48,"value":2464},{"type":42,"tag":229,"props":6653,"children":6654},{"style":1260},[6655],{"type":48,"value":1322},{"type":42,"tag":57,"props":6657,"children":6658},{},[6659,6695,6710,6734,6766,6840],{"type":42,"tag":61,"props":6660,"children":6661},{},[6662,6672,6673,6679,6680,6685,6687,6693],{"type":42,"tag":513,"props":6663,"children":6664},{},[6665,6670],{"type":42,"tag":65,"props":6666,"children":6668},{"className":6667},[],[6669],{"type":48,"value":2487},{"type":48,"value":6671}," names an f32 Msg variant",{"type":48,"value":2620},{"type":42,"tag":65,"props":6674,"children":6676},{"className":6675},[],[6677],{"type":48,"value":6678},"sidebar_resized: f32",{"type":48,"value":2365},{"type":42,"tag":65,"props":6681,"children":6683},{"className":6682},[],[6684],{"type":48,"value":483},{"type":48,"value":6686}," stores it (",{"type":42,"tag":65,"props":6688,"children":6690},{"className":6689},[],[6691],{"type":48,"value":6692},"model.sidebar_split = fraction",{"type":48,"value":6694},"). The delivered fraction is the value the runtime already applied and clamped, so echoing it never fights the reconcile.",{"type":42,"tag":61,"props":6696,"children":6697},{},[6698,6708],{"type":42,"tag":513,"props":6699,"children":6700},{},[6701,6706],{"type":42,"tag":65,"props":6702,"children":6704},{"className":6703},[],[6705],{"type":48,"value":2502},{"type":48,"value":6707}," on the panes bounds the drag",{"type":48,"value":6709}," — the divider clamps so neither pane shrinks below its floor, on drag, keyboard, and layout alike.",{"type":42,"tag":61,"props":6711,"children":6712},{},[6713,6718,6720,6725,6727,6732],{"type":42,"tag":513,"props":6714,"children":6715},{},[6716],{"type":48,"value":6717},"Uncontrolled works too",{"type":48,"value":6719},": without ",{"type":42,"tag":65,"props":6721,"children":6723},{"className":6722},[],[6724],{"type":48,"value":2487},{"type":48,"value":6726},", the divider position survives rebuilds under the source-wins reconcile (a source-side ",{"type":42,"tag":65,"props":6728,"children":6730},{"className":6729},[],[6731],{"type":48,"value":2479},{"type":48,"value":6733}," change wins), but pane CONTENT lays out at the declared fraction until the model echoes — bind the handler for the exact controlled loop.",{"type":42,"tag":61,"props":6735,"children":6736},{},[6737,6741,6743,6749,6750,6756,6758,6764],{"type":42,"tag":513,"props":6738,"children":6739},{},[6740],{"type":48,"value":6180},{"type":48,"value":6742},": Tab reaches the divider; Left\u002FRight step the fraction (Shift for 2x), Home\u002FEnd jump to the clamp edges. Automation drives it with ",{"type":42,"tag":65,"props":6744,"children":6746},{"className":6745},[],[6747],{"type":48,"value":6748},"widget-drag",{"type":48,"value":456},{"type":42,"tag":65,"props":6751,"children":6753},{"className":6752},[],[6754],{"type":48,"value":6755},"widget-key",{"type":48,"value":6757},", and snapshots show the divider as ",{"type":42,"tag":65,"props":6759,"children":6761},{"className":6760},[],[6762],{"type":48,"value":6763},"role=separator",{"type":48,"value":6765}," with the fraction as its value.",{"type":42,"tag":61,"props":6767,"children":6768},{},[6769,6774,6775,6781,6783,6788,6790,6795,6797,6802,6804,6810,6811,6817,6818,6824,6825,6831,6832,6838],{"type":42,"tag":513,"props":6770,"children":6771},{},[6772],{"type":48,"value":6773},"Animated resize",{"type":48,"value":4178},{"type":42,"tag":65,"props":6776,"children":6778},{"className":6777},[],[6779],{"type":48,"value":6780},"resize-duration=\"180\"",{"type":48,"value":6782}," (milliseconds, ",{"type":42,"tag":65,"props":6784,"children":6786},{"className":6785},[],[6787],{"type":48,"value":2464},{"type":48,"value":6789}," only — a teaching error elsewhere) makes the bound ",{"type":42,"tag":65,"props":6791,"children":6793},{"className":6792},[],[6794],{"type":48,"value":2479},{"type":48,"value":6796}," a target — a model-driven move (a collapse toggle, not a drag echo) eases the rendered fraction there one presented frame at a time instead of snapping, dispatching the same ",{"type":42,"tag":65,"props":6798,"children":6800},{"className":6799},[],[6801],{"type":48,"value":2487},{"type":48,"value":6803}," echoes a drag would; ",{"type":42,"tag":65,"props":6805,"children":6807},{"className":6806},[],[6808],{"type":48,"value":6809},"resize-easing",{"type":48,"value":2620},{"type":42,"tag":65,"props":6812,"children":6814},{"className":6813},[],[6815],{"type":48,"value":6816},"linear",{"type":48,"value":456},{"type":42,"tag":65,"props":6819,"children":6821},{"className":6820},[],[6822],{"type":48,"value":6823},"standard",{"type":48,"value":456},{"type":42,"tag":65,"props":6826,"children":6828},{"className":6827},[],[6829],{"type":48,"value":6830},"emphasized",{"type":48,"value":456},{"type":42,"tag":65,"props":6833,"children":6835},{"className":6834},[],[6836],{"type":48,"value":6837},"spring",{"type":48,"value":6839},") shapes the ramp and needs the nonzero duration beside it (alone it is a teaching error), and reduced-motion appearances snap automatically — apps declare nothing extra.",{"type":42,"tag":61,"props":6841,"children":6842},{},[6843],{"type":48,"value":6844},"Three panes = nested splits, as above. More than two children is a validation error (put conditional content inside a pane).",{"type":42,"tag":612,"props":6846,"children":6848},{"id":6847},"trees-disclosure-rows-with-the-aria-tree-keymap",[6849],{"type":48,"value":6850},"Trees: disclosure rows with the ARIA tree keymap",{"type":42,"tag":51,"props":6852,"children":6853},{},[6854,6859,6861,6866],{"type":42,"tag":65,"props":6855,"children":6857},{"className":6856},[],[6858],{"type":48,"value":2523},{"type":48,"value":6860}," turns a rail of pressable rows into a keyboard-navigable disclosure tree. Rows are ROLE-driven: any pressable element carrying ",{"type":42,"tag":65,"props":6862,"children":6864},{"className":6863},[],[6865],{"type":48,"value":2538},{"type":48,"value":6867}," — at any nesting depth under the tree — joins one roving focus set:",{"type":42,"tag":219,"props":6869,"children":6871},{"className":1248,"code":6870,"language":1250,"meta":223,"style":223},"\u003Ctree gap=\"2\" label=\"Folders\">\n  \u003Cfor each=\"folderRows\" key=\"id\" as=\"f\">\n    \u003Cpanel role=\"treeitem\" expanded=\"{f.expanded}\" on-press=\"select_folder:{f.id}\" on-toggle=\"toggle_folder:{f.id}\" label=\"{f.label}\">\n      \u003Crow gap=\"8\" cross=\"center\">\u003Cicon name=\"folder\"\u002F>\u003Ctext grow=\"1\">{f.name}\u003C\u002Ftext>\u003C\u002Frow>\n    \u003C\u002Fpanel>\n  \u003C\u002Ffor>\n\u003C\u002Ftree>\n",[6872],{"type":42,"tag":65,"props":6873,"children":6874},{"__ignoreMap":223},[6875,6931,7009,7130,7274,7289,7304],{"type":42,"tag":229,"props":6876,"children":6877},{"class":231,"line":232},[6878,6882,6886,6890,6894,6898,6902,6906,6910,6914,6918,6923,6927],{"type":42,"tag":229,"props":6879,"children":6880},{"style":1260},[6881],{"type":48,"value":1263},{"type":42,"tag":229,"props":6883,"children":6884},{"style":1266},[6885],{"type":48,"value":2523},{"type":42,"tag":229,"props":6887,"children":6888},{"style":1272},[6889],{"type":48,"value":5219},{"type":42,"tag":229,"props":6891,"children":6892},{"style":1260},[6893],{"type":48,"value":1280},{"type":42,"tag":229,"props":6895,"children":6896},{"style":1260},[6897],{"type":48,"value":1285},{"type":42,"tag":229,"props":6899,"children":6900},{"style":1288},[6901],{"type":48,"value":5232},{"type":42,"tag":229,"props":6903,"children":6904},{"style":1260},[6905],{"type":48,"value":1285},{"type":42,"tag":229,"props":6907,"children":6908},{"style":1272},[6909],{"type":48,"value":1300},{"type":42,"tag":229,"props":6911,"children":6912},{"style":1260},[6913],{"type":48,"value":1280},{"type":42,"tag":229,"props":6915,"children":6916},{"style":1260},[6917],{"type":48,"value":1285},{"type":42,"tag":229,"props":6919,"children":6920},{"style":1288},[6921],{"type":48,"value":6922},"Folders",{"type":42,"tag":229,"props":6924,"children":6925},{"style":1260},[6926],{"type":48,"value":1285},{"type":42,"tag":229,"props":6928,"children":6929},{"style":1260},[6930],{"type":48,"value":1322},{"type":42,"tag":229,"props":6932,"children":6933},{"class":231,"line":241},[6934,6938,6942,6946,6950,6954,6959,6963,6967,6971,6975,6980,6984,6988,6992,6996,7001,7005],{"type":42,"tag":229,"props":6935,"children":6936},{"style":1260},[6937],{"type":48,"value":1330},{"type":42,"tag":229,"props":6939,"children":6940},{"style":1266},[6941],{"type":48,"value":1236},{"type":42,"tag":229,"props":6943,"children":6944},{"style":1272},[6945],{"type":48,"value":5277},{"type":42,"tag":229,"props":6947,"children":6948},{"style":1260},[6949],{"type":48,"value":1280},{"type":42,"tag":229,"props":6951,"children":6952},{"style":1260},[6953],{"type":48,"value":1285},{"type":42,"tag":229,"props":6955,"children":6956},{"style":1288},[6957],{"type":48,"value":6958},"folderRows",{"type":42,"tag":229,"props":6960,"children":6961},{"style":1260},[6962],{"type":48,"value":1285},{"type":42,"tag":229,"props":6964,"children":6965},{"style":1272},[6966],{"type":48,"value":5880},{"type":42,"tag":229,"props":6968,"children":6969},{"style":1260},[6970],{"type":48,"value":1280},{"type":42,"tag":229,"props":6972,"children":6973},{"style":1260},[6974],{"type":48,"value":1285},{"type":42,"tag":229,"props":6976,"children":6977},{"style":1288},[6978],{"type":48,"value":6979},"id",{"type":42,"tag":229,"props":6981,"children":6982},{"style":1260},[6983],{"type":48,"value":1285},{"type":42,"tag":229,"props":6985,"children":6986},{"style":1272},[6987],{"type":48,"value":5299},{"type":42,"tag":229,"props":6989,"children":6990},{"style":1260},[6991],{"type":48,"value":1280},{"type":42,"tag":229,"props":6993,"children":6994},{"style":1260},[6995],{"type":48,"value":1285},{"type":42,"tag":229,"props":6997,"children":6998},{"style":1288},[6999],{"type":48,"value":7000},"f",{"type":42,"tag":229,"props":7002,"children":7003},{"style":1260},[7004],{"type":48,"value":1285},{"type":42,"tag":229,"props":7006,"children":7007},{"style":1260},[7008],{"type":48,"value":1322},{"type":42,"tag":229,"props":7010,"children":7011},{"class":231,"line":251},[7012,7016,7020,7025,7029,7033,7037,7041,7046,7050,7054,7059,7063,7067,7071,7075,7080,7084,7088,7092,7096,7101,7105,7109,7113,7117,7122,7126],{"type":42,"tag":229,"props":7013,"children":7014},{"style":1260},[7015],{"type":48,"value":1403},{"type":42,"tag":229,"props":7017,"children":7018},{"style":1266},[7019],{"type":48,"value":1999},{"type":42,"tag":229,"props":7021,"children":7022},{"style":1272},[7023],{"type":48,"value":7024}," role",{"type":42,"tag":229,"props":7026,"children":7027},{"style":1260},[7028],{"type":48,"value":1280},{"type":42,"tag":229,"props":7030,"children":7031},{"style":1260},[7032],{"type":48,"value":1285},{"type":42,"tag":229,"props":7034,"children":7035},{"style":1288},[7036],{"type":48,"value":4945},{"type":42,"tag":229,"props":7038,"children":7039},{"style":1260},[7040],{"type":48,"value":1285},{"type":42,"tag":229,"props":7042,"children":7043},{"style":1272},[7044],{"type":48,"value":7045}," expanded",{"type":42,"tag":229,"props":7047,"children":7048},{"style":1260},[7049],{"type":48,"value":1280},{"type":42,"tag":229,"props":7051,"children":7052},{"style":1260},[7053],{"type":48,"value":1285},{"type":42,"tag":229,"props":7055,"children":7056},{"style":1288},[7057],{"type":48,"value":7058},"{f.expanded}",{"type":42,"tag":229,"props":7060,"children":7061},{"style":1260},[7062],{"type":48,"value":1285},{"type":42,"tag":229,"props":7064,"children":7065},{"style":1272},[7066],{"type":48,"value":1275},{"type":42,"tag":229,"props":7068,"children":7069},{"style":1260},[7070],{"type":48,"value":1280},{"type":42,"tag":229,"props":7072,"children":7073},{"style":1260},[7074],{"type":48,"value":1285},{"type":42,"tag":229,"props":7076,"children":7077},{"style":1288},[7078],{"type":48,"value":7079},"select_folder:{f.id}",{"type":42,"tag":229,"props":7081,"children":7082},{"style":1260},[7083],{"type":48,"value":1285},{"type":42,"tag":229,"props":7085,"children":7086},{"style":1272},[7087],{"type":48,"value":5379},{"type":42,"tag":229,"props":7089,"children":7090},{"style":1260},[7091],{"type":48,"value":1280},{"type":42,"tag":229,"props":7093,"children":7094},{"style":1260},[7095],{"type":48,"value":1285},{"type":42,"tag":229,"props":7097,"children":7098},{"style":1288},[7099],{"type":48,"value":7100},"toggle_folder:{f.id}",{"type":42,"tag":229,"props":7102,"children":7103},{"style":1260},[7104],{"type":48,"value":1285},{"type":42,"tag":229,"props":7106,"children":7107},{"style":1272},[7108],{"type":48,"value":1300},{"type":42,"tag":229,"props":7110,"children":7111},{"style":1260},[7112],{"type":48,"value":1280},{"type":42,"tag":229,"props":7114,"children":7115},{"style":1260},[7116],{"type":48,"value":1285},{"type":42,"tag":229,"props":7118,"children":7119},{"style":1288},[7120],{"type":48,"value":7121},"{f.label}",{"type":42,"tag":229,"props":7123,"children":7124},{"style":1260},[7125],{"type":48,"value":1285},{"type":42,"tag":229,"props":7127,"children":7128},{"style":1260},[7129],{"type":48,"value":1322},{"type":42,"tag":229,"props":7131,"children":7132},{"class":231,"line":260},[7133,7137,7141,7145,7149,7153,7158,7162,7167,7171,7175,7180,7184,7189,7193,7198,7202,7206,7211,7215,7220,7224,7228,7232,7236,7240,7244,7248,7253,7257,7261,7266,7270],{"type":42,"tag":229,"props":7134,"children":7135},{"style":1260},[7136],{"type":48,"value":1441},{"type":42,"tag":229,"props":7138,"children":7139},{"style":1266},[7140],{"type":48,"value":1963},{"type":42,"tag":229,"props":7142,"children":7143},{"style":1272},[7144],{"type":48,"value":5219},{"type":42,"tag":229,"props":7146,"children":7147},{"style":1260},[7148],{"type":48,"value":1280},{"type":42,"tag":229,"props":7150,"children":7151},{"style":1260},[7152],{"type":48,"value":1285},{"type":42,"tag":229,"props":7154,"children":7155},{"style":1288},[7156],{"type":48,"value":7157},"8",{"type":42,"tag":229,"props":7159,"children":7160},{"style":1260},[7161],{"type":48,"value":1285},{"type":42,"tag":229,"props":7163,"children":7164},{"style":1272},[7165],{"type":48,"value":7166}," cross",{"type":42,"tag":229,"props":7168,"children":7169},{"style":1260},[7170],{"type":48,"value":1280},{"type":42,"tag":229,"props":7172,"children":7173},{"style":1260},[7174],{"type":48,"value":1285},{"type":42,"tag":229,"props":7176,"children":7177},{"style":1288},[7178],{"type":48,"value":7179},"center",{"type":42,"tag":229,"props":7181,"children":7182},{"style":1260},[7183],{"type":48,"value":1285},{"type":42,"tag":229,"props":7185,"children":7186},{"style":1260},[7187],{"type":48,"value":7188},">\u003C",{"type":42,"tag":229,"props":7190,"children":7191},{"style":1266},[7192],{"type":48,"value":3301},{"type":42,"tag":229,"props":7194,"children":7195},{"style":1272},[7196],{"type":48,"value":7197}," name",{"type":42,"tag":229,"props":7199,"children":7200},{"style":1260},[7201],{"type":48,"value":1280},{"type":42,"tag":229,"props":7203,"children":7204},{"style":1260},[7205],{"type":48,"value":1285},{"type":42,"tag":229,"props":7207,"children":7208},{"style":1288},[7209],{"type":48,"value":7210},"folder",{"type":42,"tag":229,"props":7212,"children":7213},{"style":1260},[7214],{"type":48,"value":1285},{"type":42,"tag":229,"props":7216,"children":7217},{"style":1260},[7218],{"type":48,"value":7219},"\u002F>\u003C",{"type":42,"tag":229,"props":7221,"children":7222},{"style":1266},[7223],{"type":48,"value":48},{"type":42,"tag":229,"props":7225,"children":7226},{"style":1272},[7227],{"type":48,"value":1339},{"type":42,"tag":229,"props":7229,"children":7230},{"style":1260},[7231],{"type":48,"value":1280},{"type":42,"tag":229,"props":7233,"children":7234},{"style":1260},[7235],{"type":48,"value":1285},{"type":42,"tag":229,"props":7237,"children":7238},{"style":1288},[7239],{"type":48,"value":1352},{"type":42,"tag":229,"props":7241,"children":7242},{"style":1260},[7243],{"type":48,"value":1285},{"type":42,"tag":229,"props":7245,"children":7246},{"style":1260},[7247],{"type":48,"value":1361},{"type":42,"tag":229,"props":7249,"children":7250},{"style":1364},[7251],{"type":48,"value":7252},"{f.name}",{"type":42,"tag":229,"props":7254,"children":7255},{"style":1260},[7256],{"type":48,"value":1371},{"type":42,"tag":229,"props":7258,"children":7259},{"style":1266},[7260],{"type":48,"value":48},{"type":42,"tag":229,"props":7262,"children":7263},{"style":1260},[7264],{"type":48,"value":7265},">\u003C\u002F",{"type":42,"tag":229,"props":7267,"children":7268},{"style":1266},[7269],{"type":48,"value":1963},{"type":42,"tag":229,"props":7271,"children":7272},{"style":1260},[7273],{"type":48,"value":1322},{"type":42,"tag":229,"props":7275,"children":7276},{"class":231,"line":269},[7277,7281,7285],{"type":42,"tag":229,"props":7278,"children":7279},{"style":1260},[7280],{"type":48,"value":1548},{"type":42,"tag":229,"props":7282,"children":7283},{"style":1266},[7284],{"type":48,"value":1999},{"type":42,"tag":229,"props":7286,"children":7287},{"style":1260},[7288],{"type":48,"value":1322},{"type":42,"tag":229,"props":7290,"children":7291},{"class":231,"line":278},[7292,7296,7300],{"type":42,"tag":229,"props":7293,"children":7294},{"style":1260},[7295],{"type":48,"value":1700},{"type":42,"tag":229,"props":7297,"children":7298},{"style":1266},[7299],{"type":48,"value":1236},{"type":42,"tag":229,"props":7301,"children":7302},{"style":1260},[7303],{"type":48,"value":1322},{"type":42,"tag":229,"props":7305,"children":7306},{"class":231,"line":287},[7307,7311,7315],{"type":42,"tag":229,"props":7308,"children":7309},{"style":1260},[7310],{"type":48,"value":1371},{"type":42,"tag":229,"props":7312,"children":7313},{"style":1266},[7314],{"type":48,"value":2523},{"type":42,"tag":229,"props":7316,"children":7317},{"style":1260},[7318],{"type":48,"value":1322},{"type":42,"tag":57,"props":7320,"children":7321},{},[7322,7339,7363,7387,7404],{"type":42,"tag":61,"props":7323,"children":7324},{},[7325,7330,7332,7337],{"type":42,"tag":513,"props":7326,"children":7327},{},[7328],{"type":48,"value":7329},"Up\u002FDown walk the visible rows",{"type":48,"value":7331}," in tree order, across nesting levels. Selection follows focus: each move dispatches the landed row's ",{"type":42,"tag":65,"props":7333,"children":7335},{"className":7334},[],[7336],{"type":48,"value":1176},{"type":48,"value":7338},", so the model owns the selection exactly like a click.",{"type":42,"tag":61,"props":7340,"children":7341},{},[7342,7347,7349,7354,7356,7361],{"type":42,"tag":513,"props":7343,"children":7344},{},[7345],{"type":48,"value":7346},"Left\u002FRight are disclosure keys",{"type":48,"value":7348},": Left on an expanded row dispatches its ",{"type":42,"tag":65,"props":7350,"children":7352},{"className":7351},[],[7353],{"type":48,"value":2277},{"type":48,"value":7355}," (collapse); on a collapsed row or leaf it moves focus to the PARENT row. Right on a collapsed row dispatches ",{"type":42,"tag":65,"props":7357,"children":7359},{"className":7358},[],[7360],{"type":48,"value":2277},{"type":48,"value":7362}," (expand); on an expanded row it moves to the first child row.",{"type":42,"tag":61,"props":7364,"children":7365},{},[7366,7371,7373,7378,7380,7385],{"type":42,"tag":513,"props":7367,"children":7368},{},[7369],{"type":48,"value":7370},"Home\u002FEnd",{"type":48,"value":7372}," jump to the scope's first\u002Flast row; ",{"type":42,"tag":513,"props":7374,"children":7375},{},[7376],{"type":48,"value":7377},"Enter\u002FSpace",{"type":48,"value":7379}," activate (",{"type":42,"tag":65,"props":7381,"children":7383},{"className":7382},[],[7384],{"type":48,"value":1176},{"type":48,"value":7386},").",{"type":42,"tag":61,"props":7388,"children":7389},{},[7390,7395,7397,7402],{"type":42,"tag":513,"props":7391,"children":7392},{},[7393],{"type":48,"value":7394},"Expansion is model-owned",{"type":48,"value":7396},": expandable rows bind ",{"type":42,"tag":65,"props":7398,"children":7400},{"className":7399},[],[7401],{"type":48,"value":2553},{"type":48,"value":7403}," (omit it on leaves) and the model renders child rows only while expanded — collapsed subtrees are simply not in the tree, so \"visible rows\" needs no engine bookkeeping. Flat rails (the notes folder list) are honest trees of leaves: Up\u002FDown\u002FHome\u002FEnd\u002FEnter work, Left\u002FRight are inert.",{"type":42,"tag":61,"props":7405,"children":7406},{},[7407],{"type":48,"value":7408},"Single-select: selecting a row clears the previous selection across the WHOLE tree scope (rows nest, so this is not per-parent).",{"type":42,"tag":612,"props":7410,"children":7412},{"id":7411},"press-and-hold-on-hold",[7413],{"type":48,"value":7414},"Press-and-hold: on-hold",{"type":42,"tag":51,"props":7416,"children":7417},{},[7418,7423,7425,7430,7432,7437,7439,7444,7446,7451,7453,7458,7460,7465,7467,7473,7475,7480],{"type":42,"tag":65,"props":7419,"children":7421},{"className":7420},[],[7422],{"type":48,"value":1183},{"type":48,"value":7424}," is the click-acts, hold-reveals menu-button shape — a control that acts on click and offers more on hold: a pointer held ~350 ms dispatches the hold Msg (the release then presses nothing), a quick click dispatches ",{"type":42,"tag":65,"props":7426,"children":7428},{"className":7427},[],[7429],{"type":48,"value":1176},{"type":48,"value":7431}," as usual, and a right\u002Fctrl-click whose route offers no context menu dispatches the hold Msg immediately (a declared ",{"type":42,"tag":65,"props":7433,"children":7435},{"className":7434},[],[7436],{"type":48,"value":1168},{"type":48,"value":7438}," always wins the right-click — hold is the primary-button gesture, not the context-menu channel). Like ",{"type":42,"tag":65,"props":7440,"children":7442},{"className":7441},[],[7443],{"type":48,"value":1176},{"type":48,"value":7445},", binding it makes any element pressable. The breadcrumb-switcher pattern: ",{"type":42,"tag":65,"props":7447,"children":7449},{"className":7448},[],[7450],{"type":48,"value":1176},{"type":48,"value":7452}," selects the crumb, ",{"type":42,"tag":65,"props":7454,"children":7456},{"className":7455},[],[7457],{"type":48,"value":1183},{"type":48,"value":7459}," opens an anchored ",{"type":42,"tag":65,"props":7461,"children":7463},{"className":7462},[],[7464],{"type":48,"value":2204},{"type":48,"value":7466}," of its siblings — an app-designed hold-reveal surface, distinct from the row's right-click menu. Both legs are live-drivable: ",{"type":42,"tag":65,"props":7468,"children":7470},{"className":7469},[],[7471],{"type":48,"value":7472},"native automate widget-hold \u003Cview> \u003Cid>",{"type":48,"value":7474}," runs the pointer+timer gesture, ",{"type":42,"tag":65,"props":7476,"children":7478},{"className":7477},[],[7479],{"type":48,"value":1863},{"type":48,"value":7481}," the secondary click.",{"type":42,"tag":219,"props":7483,"children":7485},{"className":1248,"code":7484,"language":1250,"meta":223,"style":223},"\u003Cbutton on-press=\"select_crumb:{c.id}\" on-hold=\"open_crumb_menu:{c.id}\">{c.name}\u003C\u002Fbutton>\n",[7486],{"type":42,"tag":65,"props":7487,"children":7488},{"__ignoreMap":223},[7489],{"type":42,"tag":229,"props":7490,"children":7491},{"class":231,"line":232},[7492,7496,7500,7504,7508,7512,7517,7521,7526,7530,7534,7539,7543,7547,7552,7556,7560],{"type":42,"tag":229,"props":7493,"children":7494},{"style":1260},[7495],{"type":48,"value":1263},{"type":42,"tag":229,"props":7497,"children":7498},{"style":1266},[7499],{"type":48,"value":2795},{"type":42,"tag":229,"props":7501,"children":7502},{"style":1272},[7503],{"type":48,"value":1275},{"type":42,"tag":229,"props":7505,"children":7506},{"style":1260},[7507],{"type":48,"value":1280},{"type":42,"tag":229,"props":7509,"children":7510},{"style":1260},[7511],{"type":48,"value":1285},{"type":42,"tag":229,"props":7513,"children":7514},{"style":1288},[7515],{"type":48,"value":7516},"select_crumb:{c.id}",{"type":42,"tag":229,"props":7518,"children":7519},{"style":1260},[7520],{"type":48,"value":1285},{"type":42,"tag":229,"props":7522,"children":7523},{"style":1272},[7524],{"type":48,"value":7525}," on-hold",{"type":42,"tag":229,"props":7527,"children":7528},{"style":1260},[7529],{"type":48,"value":1280},{"type":42,"tag":229,"props":7531,"children":7532},{"style":1260},[7533],{"type":48,"value":1285},{"type":42,"tag":229,"props":7535,"children":7536},{"style":1288},[7537],{"type":48,"value":7538},"open_crumb_menu:{c.id}",{"type":42,"tag":229,"props":7540,"children":7541},{"style":1260},[7542],{"type":48,"value":1285},{"type":42,"tag":229,"props":7544,"children":7545},{"style":1260},[7546],{"type":48,"value":1361},{"type":42,"tag":229,"props":7548,"children":7549},{"style":1364},[7550],{"type":48,"value":7551},"{c.name}",{"type":42,"tag":229,"props":7553,"children":7554},{"style":1260},[7555],{"type":48,"value":1371},{"type":42,"tag":229,"props":7557,"children":7558},{"style":1266},[7559],{"type":48,"value":2795},{"type":42,"tag":229,"props":7561,"children":7562},{"style":1260},[7563],{"type":48,"value":1322},{"type":42,"tag":612,"props":7565,"children":7567},{"id":7566},"select-then-act-double-press-and-row-level-enter",[7568],{"type":48,"value":7569},"Select, then act: double press and row-level Enter",{"type":42,"tag":51,"props":7571,"children":7572},{},[7573,7575,7581,7583,7589,7591,7597,7599,7604,7606,7611,7612,7617,7619,7624,7626,7632,7634,7640,7642,7648,7650,7655,7657,7662,7664,7670],{"type":48,"value":7574},"The desktop list convention — click selects, the primary action (open the record, play the track) rides the double click and Enter — is two bindings on the same row. ",{"type":42,"tag":65,"props":7576,"children":7578},{"className":7577},[],[7579],{"type":48,"value":7580},"on_double_press",{"type":48,"value":7582}," (Zig views only — ",{"type":42,"tag":65,"props":7584,"children":7586},{"className":7585},[],[7587],{"type":48,"value":7588},"ElementOptions.on_double_press",{"type":48,"value":7590},"; the closed markup grammar has no double-click event) dispatches on the release whose runtime-derived click count reached 2, in place of a second press: the first click still dispatches ",{"type":42,"tag":65,"props":7592,"children":7594},{"className":7593},[],[7595],{"type":48,"value":7596},"on_press",{"type":48,"value":7598}," on its own release, so the pairing is additive, never a delay — no press timer, no swallowed first click. Like ",{"type":42,"tag":65,"props":7600,"children":7602},{"className":7601},[],[7603],{"type":48,"value":7596},{"type":48,"value":7605},", binding it makes the element a hit target; a widget with no double handler treats a double click as two single clicks. The keyboard mirror is row-level Enter: on a ",{"type":42,"tag":65,"props":7607,"children":7609},{"className":7608},[],[7610],{"type":48,"value":1269},{"type":48,"value":1820},{"type":42,"tag":65,"props":7613,"children":7615},{"className":7614},[],[7616],{"type":48,"value":3129},{"type":48,"value":7618}," grows a second home beyond text entry — with a submit handler bound, plain Enter on a keyboard-focused row dispatches it as the row's PRIMARY action while Space keeps the select activation (",{"type":42,"tag":65,"props":7620,"children":7622},{"className":7621},[],[7623],{"type":48,"value":1176},{"type":48,"value":7625},"); rows without one resolve Enter as select, unchanged. It works from markup (",{"type":42,"tag":65,"props":7627,"children":7629},{"className":7628},[],[7630],{"type":48,"value":7631},"\u003Clist-item on-press=\"select_track:{t.id}\" on-submit=\"play_track:{t.id}\">{t.title}\u003C\u002Flist-item>",{"type":48,"value":7633},") and Zig views alike, and tests drive it through ",{"type":42,"tag":65,"props":7635,"children":7637},{"className":7636},[],[7638],{"type":48,"value":7639},"msgForKeyboard",{"type":48,"value":7641}," (an Enter event resolves the submit handler, Space the press). ",{"type":42,"tag":65,"props":7643,"children":7645},{"className":7644},[],[7646],{"type":48,"value":7647},"examples\u002Fsoundboard",{"type":48,"value":7649},"'s track rows are the live reference: ",{"type":42,"tag":65,"props":7651,"children":7653},{"className":7652},[],[7654],{"type":48,"value":7596},{"type":48,"value":7656}," select, ",{"type":42,"tag":65,"props":7658,"children":7660},{"className":7659},[],[7661],{"type":48,"value":7580},{"type":48,"value":7663}," play, ",{"type":42,"tag":65,"props":7665,"children":7667},{"className":7666},[],[7668],{"type":48,"value":7669},"on_submit",{"type":48,"value":7671}," play.",{"type":42,"tag":212,"props":7673,"children":7675},{"id":7674},"widget-budgets-and-virtualization",[7676],{"type":48,"value":7677},"Widget budgets and virtualization",{"type":42,"tag":51,"props":7679,"children":7680},{},[7681,7683,7689,7690,7695,7696,7702,7704,7709,7711,7717,7719,7724,7726,7732,7733,7738,7740,7746,7748,7754,7755,7761,7762,7767,7769,7774,7776,7781,7783,7789],{"type":48,"value":7682},"Every view has fixed per-view capacities (",{"type":42,"tag":65,"props":7684,"children":7686},{"className":7685},[],[7687],{"type":48,"value":7688},"src\u002Fruntime\u002Fcanvas_limits.zig",{"type":48,"value":4119},{"type":42,"tag":513,"props":7691,"children":7692},{},[7693],{"type":48,"value":7694},"1024 retained widget nodes",{"type":48,"value":2620},{"type":42,"tag":65,"props":7697,"children":7699},{"className":7698},[],[7700],{"type":48,"value":7701},"max_canvas_widget_nodes_per_view",{"type":48,"value":7703}," — the budget that matters for tree design; semantics and spans match it), 64 KiB retained widget text, ",{"type":42,"tag":513,"props":7705,"children":7706},{},[7707],{"type":48,"value":7708},"512 declared context-menu items",{"type":48,"value":7710}," summed across all widgets of the view (",{"type":42,"tag":65,"props":7712,"children":7714},{"className":7713},[],[7715],{"type":48,"value":7716},"max_canvas_widget_context_menu_items_per_view",{"type":48,"value":7718}," — separators count as items), ",{"type":42,"tag":513,"props":7720,"children":7721},{},[7722],{"type":48,"value":7723},"64 chart series \u002F 16384 chart points",{"type":48,"value":7725}," summed across all charts of the view (",{"type":42,"tag":65,"props":7727,"children":7729},{"className":7728},[],[7730],{"type":48,"value":7731},"max_canvas_widget_chart_*",{"type":48,"value":83},{"type":42,"tag":65,"props":7734,"children":7736},{"className":7735},[],[7737],{"type":48,"value":4223},{"type":48,"value":7739}," downsamples every series to 256 points, so this is 64 maximal series or hundreds of sparklines), and per-frame content budgets (2048 commands, 8192 glyphs, 32 KiB frame text, 2048 path elements shared by icons and charts). Overflow is loud: ",{"type":42,"tag":65,"props":7741,"children":7743},{"className":7742},[],[7744],{"type":48,"value":7745},"error.WidgetLayoutListFull",{"type":48,"value":7747}," \u002F ",{"type":42,"tag":65,"props":7749,"children":7751},{"className":7750},[],[7752],{"type":48,"value":7753},"error.WidgetNodeLimitReached",{"type":48,"value":7747},{"type":42,"tag":65,"props":7756,"children":7758},{"className":7757},[],[7759],{"type":48,"value":7760},"error.WidgetContextMenuLimitReached",{"type":48,"value":7747},{"type":42,"tag":65,"props":7763,"children":7765},{"className":7764},[],[7766],{"type":48,"value":6295},{"type":48,"value":7768}," (at most ",{"type":42,"tag":513,"props":7770,"children":7771},{},[7772],{"type":48,"value":7773},"16 anchored floating surfaces",{"type":48,"value":7775}," mounted per view — ",{"type":42,"tag":65,"props":7777,"children":7779},{"className":7778},[],[7780],{"type":48,"value":6287},{"type":48,"value":7782},") fail tests under the harness's propagate policy and log a teaching diagnostic naming the budget in production (the app degrades to the previous frame). Watch headroom without overflowing: automation snapshots report ",{"type":42,"tag":65,"props":7784,"children":7786},{"className":7785},[],[7787],{"type":48,"value":7788},"widget_nodes=N\u002F1024 widget_semantics=N\u002F1024 context_menu_items=N\u002F512",{"type":48,"value":7790}," on every gpu_surface view line.",{"type":42,"tag":51,"props":7792,"children":7793},{},[7794],{"type":48,"value":7795},"Budget rules of thumb: 1024 nodes is roomy for a three-pane desktop app (~500 nodes measured for a dense sidebar + markdown detail + run surface), but node count scales with what is MOUNTED, not what is visible — so bound every unbounded collection:",{"type":42,"tag":57,"props":7797,"children":7798},{},[7799,7822,7863,7876],{"type":42,"tag":61,"props":7800,"children":7801},{},[7802,7807,7808,7814,7815,7820],{"type":42,"tag":513,"props":7803,"children":7804},{},[7805],{"type":48,"value":7806},"Dataset-scale uniform rows (feeds, logs, tables of thousands+): use the WINDOWED virtual list",{"type":48,"value":2620},{"type":42,"tag":65,"props":7809,"children":7811},{"className":7810},[],[7812],{"type":48,"value":7813},"ui.virtualWindow",{"type":48,"value":3952},{"type":42,"tag":65,"props":7816,"children":7818},{"className":7817},[],[7819],{"type":48,"value":1016},{"type":48,"value":7821},", Zig views — see the next section). The view builds only the visible window; the runtime owns the scroll; budgets stay viewport-sized at 100k items.",{"type":42,"tag":61,"props":7823,"children":7824},{},[7825,7830,7832,7837,7838,7843,7844,7849,7850,7855,7856,7861],{"type":42,"tag":65,"props":7826,"children":7828},{"className":7827},[],[7829],{"type":48,"value":4588},{"type":48,"value":7831}," on ",{"type":42,"tag":65,"props":7833,"children":7835},{"className":7834},[],[7836],{"type":48,"value":1008},{"type":48,"value":456},{"type":42,"tag":65,"props":7839,"children":7841},{"className":7840},[],[7842],{"type":48,"value":2077},{"type":48,"value":456},{"type":42,"tag":65,"props":7845,"children":7847},{"className":7846},[],[7848],{"type":48,"value":2084},{"type":48,"value":456},{"type":42,"tag":65,"props":7851,"children":7853},{"className":7852},[],[7854],{"type":48,"value":1920},{"type":48,"value":4638},{"type":42,"tag":65,"props":7857,"children":7859},{"className":7858},[],[7860],{"type":48,"value":4595},{"type":48,"value":7862}," for fixed-extent items) lays out only the visible window + overscan; a 10,000-item list materializes ~viewport\u002Fextent nodes. It bounds NODES, not your source data: the builder still walks every item, so it suits row sets the model already holds (hundreds). Legacy virtualized containers without a declared item count are app-driven for scrolling (wheel offsets do not mutate them).",{"type":42,"tag":61,"props":7864,"children":7865},{},[7866,7868,7874],{"type":48,"value":7867},"For non-uniform content (chat transcripts, ledgers, diffs), keep a bounded window in the model and slide it with ",{"type":42,"tag":65,"props":7869,"children":7871},{"className":7870},[],[7872],{"type":48,"value":7873},"on-scroll",{"type":48,"value":7875}," (see Messages) or explicit paging — the window follows the scrollbar instead of mounting everything.",{"type":42,"tag":61,"props":7877,"children":7878},{},[7879],{"type":48,"value":7880},"Remember multi-node rows multiply: a 4-node row × 50 mounted rows is 200 nodes before chrome.",{"type":42,"tag":212,"props":7882,"children":7884},{"id":7883},"windowed-virtual-lists-zig-views-100k-rows-viewport-sized-budgets",[7885],{"type":48,"value":7886},"Windowed virtual lists (Zig views): 100k rows, viewport-sized budgets",{"type":42,"tag":51,"props":7888,"children":7889},{},[7890,7892,7897,7899,7904,7906,7912],{"type":48,"value":7891},"The honest infinite-scroll primitive. The RUNTIME owns the viewport math (retained scroll offset + viewport → visible index range, from a fixed per-item extent), the MODEL owns the data, and the view is the seam between them: ask ",{"type":42,"tag":65,"props":7893,"children":7895},{"className":7894},[],[7896],{"type":48,"value":7813},{"type":48,"value":7898}," for the visible range, build ONE keyed node per item in it, hand both to ",{"type":42,"tag":65,"props":7900,"children":7902},{"className":7901},[],[7903],{"type":48,"value":1016},{"type":48,"value":7905},". The list is a runtime-scrolled scroll region — engine wheel\u002Fkinetic\u002Fkeyboard everywhere, the native scroll driver on macOS — whose scrollbar spans the FULL virtual extent (",{"type":42,"tag":65,"props":7907,"children":7909},{"className":7908},[],[7910],{"type":48,"value":7911},"item_count × stride",{"type":48,"value":7913},"), and every scroll observation re-derives the view so the window follows the offset with no app wiring.",{"type":42,"tag":219,"props":7915,"children":7917},{"className":221,"code":7916,"language":18,"meta":223,"style":223},"const options = Ui.VirtualListOptions{\n    .id = \"timeline\",            \u002F\u002F stable identity: global key + scroll-state lookup\n    .item_count = model.loaded,  \u002F\u002F TOTAL items the model holds right now\n    .item_extent = 84,           \u002F\u002F fixed row height (v1 contract: uniform rows)\n    .overscan = 4,\n    .grow = 1,\n    .on_reach_end = .load_more,  \u002F\u002F infinite fetch: update appends the next batch\n};\nconst window = ui.virtualWindow(options);\nconst rows = ui.arena.alloc(Ui.Node, window.itemCount()) catch { ui.failed = true; return ui.column(.{}, .{}); };\nfor (rows, 0..) |*row, offset| {\n    const index = window.start_index + offset;\n    var node = rowView(ui, model, index);\n    node.key = .{ .int = @intCast(index) };  \u002F\u002F identity = the ITEM, not the slot\n    row.* = node;\n}\nreturn ui.virtualList(options, window, .{rows});\n",[7918],{"type":42,"tag":65,"props":7919,"children":7920},{"__ignoreMap":223},[7921,7929,7937,7945,7953,7961,7969,7977,7984,7992,8000,8008,8016,8024,8032,8040,8047],{"type":42,"tag":229,"props":7922,"children":7923},{"class":231,"line":232},[7924],{"type":42,"tag":229,"props":7925,"children":7926},{},[7927],{"type":48,"value":7928},"const options = Ui.VirtualListOptions{\n",{"type":42,"tag":229,"props":7930,"children":7931},{"class":231,"line":241},[7932],{"type":42,"tag":229,"props":7933,"children":7934},{},[7935],{"type":48,"value":7936},"    .id = \"timeline\",            \u002F\u002F stable identity: global key + scroll-state lookup\n",{"type":42,"tag":229,"props":7938,"children":7939},{"class":231,"line":251},[7940],{"type":42,"tag":229,"props":7941,"children":7942},{},[7943],{"type":48,"value":7944},"    .item_count = model.loaded,  \u002F\u002F TOTAL items the model holds right now\n",{"type":42,"tag":229,"props":7946,"children":7947},{"class":231,"line":260},[7948],{"type":42,"tag":229,"props":7949,"children":7950},{},[7951],{"type":48,"value":7952},"    .item_extent = 84,           \u002F\u002F fixed row height (v1 contract: uniform rows)\n",{"type":42,"tag":229,"props":7954,"children":7955},{"class":231,"line":269},[7956],{"type":42,"tag":229,"props":7957,"children":7958},{},[7959],{"type":48,"value":7960},"    .overscan = 4,\n",{"type":42,"tag":229,"props":7962,"children":7963},{"class":231,"line":278},[7964],{"type":42,"tag":229,"props":7965,"children":7966},{},[7967],{"type":48,"value":7968},"    .grow = 1,\n",{"type":42,"tag":229,"props":7970,"children":7971},{"class":231,"line":287},[7972],{"type":42,"tag":229,"props":7973,"children":7974},{},[7975],{"type":48,"value":7976},"    .on_reach_end = .load_more,  \u002F\u002F infinite fetch: update appends the next batch\n",{"type":42,"tag":229,"props":7978,"children":7979},{"class":231,"line":296},[7980],{"type":42,"tag":229,"props":7981,"children":7982},{},[7983],{"type":48,"value":669},{"type":42,"tag":229,"props":7985,"children":7986},{"class":231,"line":305},[7987],{"type":42,"tag":229,"props":7988,"children":7989},{},[7990],{"type":48,"value":7991},"const window = ui.virtualWindow(options);\n",{"type":42,"tag":229,"props":7993,"children":7994},{"class":231,"line":314},[7995],{"type":42,"tag":229,"props":7996,"children":7997},{},[7998],{"type":48,"value":7999},"const rows = ui.arena.alloc(Ui.Node, window.itemCount()) catch { ui.failed = true; return ui.column(.{}, .{}); };\n",{"type":42,"tag":229,"props":8001,"children":8002},{"class":231,"line":323},[8003],{"type":42,"tag":229,"props":8004,"children":8005},{},[8006],{"type":48,"value":8007},"for (rows, 0..) |*row, offset| {\n",{"type":42,"tag":229,"props":8009,"children":8010},{"class":231,"line":332},[8011],{"type":42,"tag":229,"props":8012,"children":8013},{},[8014],{"type":48,"value":8015},"    const index = window.start_index + offset;\n",{"type":42,"tag":229,"props":8017,"children":8018},{"class":231,"line":341},[8019],{"type":42,"tag":229,"props":8020,"children":8021},{},[8022],{"type":48,"value":8023},"    var node = rowView(ui, model, index);\n",{"type":42,"tag":229,"props":8025,"children":8026},{"class":231,"line":350},[8027],{"type":42,"tag":229,"props":8028,"children":8029},{},[8030],{"type":48,"value":8031},"    node.key = .{ .int = @intCast(index) };  \u002F\u002F identity = the ITEM, not the slot\n",{"type":42,"tag":229,"props":8033,"children":8034},{"class":231,"line":359},[8035],{"type":42,"tag":229,"props":8036,"children":8037},{},[8038],{"type":48,"value":8039},"    row.* = node;\n",{"type":42,"tag":229,"props":8041,"children":8042},{"class":231,"line":368},[8043],{"type":42,"tag":229,"props":8044,"children":8045},{},[8046],{"type":48,"value":428},{"type":42,"tag":229,"props":8048,"children":8049},{"class":231,"line":377},[8050],{"type":42,"tag":229,"props":8051,"children":8052},{},[8053],{"type":48,"value":8054},"return ui.virtualList(options, window, .{rows});\n",{"type":42,"tag":51,"props":8056,"children":8057},{},[8058],{"type":48,"value":8059},"Rules:",{"type":42,"tag":57,"props":8061,"children":8062},{},[8063,8073,8112,8150,8176,8209,8233,8287,8304],{"type":42,"tag":61,"props":8064,"children":8065},{},[8066,8071],{"type":42,"tag":513,"props":8067,"children":8068},{},[8069],{"type":48,"value":8070},"Key every row by item identity",{"type":48,"value":8072}," (index or id). A row that scrolls away and back returns under the same structural id, so engine-owned row state and model-owned per-item state (selection washes, like counts keyed by index in the model) survive window shifts.",{"type":42,"tag":61,"props":8074,"children":8075},{},[8076,8089,8091,8096,8098,8103,8104,8110],{"type":42,"tag":513,"props":8077,"children":8078},{},[8079,8081,8087],{"type":48,"value":8080},"No ",{"type":42,"tag":65,"props":8082,"children":8084},{"className":8083},[],[8085],{"type":48,"value":8086},"on_scroll",{"type":48,"value":8088}," needed.",{"type":48,"value":8090}," The runtime re-derives the view on scroll for mounted virtual lists; bind ",{"type":42,"tag":65,"props":8092,"children":8094},{"className":8093},[],[8095],{"type":48,"value":8086},{"type":48,"value":8097}," only when the model wants to observe the position. Do not echo an offset into ",{"type":42,"tag":65,"props":8099,"children":8101},{"className":8100},[],[8102],{"type":48,"value":2479},{"type":48,"value":83},{"type":42,"tag":65,"props":8105,"children":8107},{"className":8106},[],[8108],{"type":48,"value":8109},"virtualList",{"type":48,"value":8111}," mirrors the runtime offset itself.",{"type":42,"tag":61,"props":8113,"children":8114},{},[8115,8126,8128,8134,8135,8140,8142,8148],{"type":42,"tag":513,"props":8116,"children":8117},{},[8118,8124],{"type":42,"tag":65,"props":8119,"children":8121},{"className":8120},[],[8122],{"type":48,"value":8123},"on_reach_end",{"type":48,"value":8125}," has hysteresis built in",{"type":48,"value":8127},": fires once when a scroll comes within one viewport of the end, re-arms past 1.5 viewports — which appending a batch causes on its own by growing the extent. One Msg per approach, never a fetch storm. It works on ANY scroll container (",{"type":42,"tag":65,"props":8129,"children":8131},{"className":8130},[],[8132],{"type":48,"value":8133},"on-reach-end",{"type":48,"value":7831},{"type":42,"tag":65,"props":8136,"children":8138},{"className":8137},[],[8139],{"type":48,"value":1008},{"type":48,"value":8141}," in markup). ",{"type":42,"tag":65,"props":8143,"children":8145},{"className":8144},[],[8146],{"type":48,"value":8147},"on_reach_start",{"type":48,"value":8149}," is the exact mirror for the content START (load older history; Zig views only).",{"type":42,"tag":61,"props":8151,"children":8152},{},[8153,8158,8160,8166,8168,8174],{"type":42,"tag":513,"props":8154,"children":8155},{},[8156],{"type":48,"value":8157},"Uniform rows are the fast path",{"type":48,"value":8159},": a non-zero ",{"type":42,"tag":65,"props":8161,"children":8163},{"className":8162},[],[8164],{"type":48,"value":8165},"item_extent",{"type":48,"value":8167}," makes 100k rows pure arithmetic. Give such rows single-line text (",{"type":42,"tag":65,"props":8169,"children":8171},{"className":8170},[],[8172],{"type":48,"value":8173},"wrap = false",{"type":48,"value":8175},") or fixed sub-layouts that fit the extent.",{"type":42,"tag":61,"props":8177,"children":8178},{},[8179,8184,8186,8192,8194,8200,8202,8207],{"type":42,"tag":513,"props":8180,"children":8181},{},[8182],{"type":48,"value":8183},"Variable-extent rows (chat transcripts, mixed-height feeds, markdown-bearing lists)",{"type":48,"value":8185},": set ",{"type":42,"tag":65,"props":8187,"children":8189},{"className":8188},[],[8190],{"type":48,"value":8191},"extent_estimate",{"type":48,"value":8193}," (a cheap pure fn: ",{"type":42,"tag":65,"props":8195,"children":8197},{"className":8196},[],[8198],{"type":48,"value":8199},"fn (context, logical_index) f32",{"type":48,"value":8201},", derived from model facts like line\u002Fbyte counts — NEVER from layout) and leave ",{"type":42,"tag":65,"props":8203,"children":8205},{"className":8204},[],[8206],{"type":48,"value":8165},{"type":48,"value":8208}," 0. Rows lay out at their intrinsic wrapped heights; the engine measures mounted rows and corrects an internal offset table, so the scrollbar geometry CONVERGES to truth as the user scrolls. Corrections are anchored on the first visible row — the scrollbar may drift as estimates correct (the honest behavior), but visible content NEVER jumps. Rough estimates are fine; wildly wrong ones just mean more scrollbar drift.",{"type":42,"tag":61,"props":8210,"children":8211},{},[8212,8217,8218,8224,8226,8231],{"type":42,"tag":513,"props":8213,"children":8214},{},[8215],{"type":48,"value":8216},"Tail anchoring (the chat contract)",{"type":48,"value":4178},{"type":42,"tag":65,"props":8219,"children":8221},{"className":8220},[],[8222],{"type":48,"value":8223},"anchor = .trailing",{"type":48,"value":8225}," opens the list at the bottom and keeps it pinned there while the user sits at the bottom (appends never yank a scrolled-away viewport). Works for uniform rows too (",{"type":42,"tag":65,"props":8227,"children":8229},{"className":8228},[],[8230],{"type":48,"value":8165},{"type":48,"value":8232}," doubles as the estimate).",{"type":42,"tag":61,"props":8234,"children":8235},{},[8236,8241,8243,8249,8251,8257,8259,8264,8266,8271,8273,8278,8280,8285],{"type":42,"tag":513,"props":8237,"children":8238},{},[8239],{"type":48,"value":8240},"Prepending history",{"type":48,"value":8242},": give items LOGICAL indices via ",{"type":42,"tag":65,"props":8244,"children":8246},{"className":8245},[],[8247],{"type":48,"value":8248},"index_base",{"type":48,"value":8250}," (e.g. the first loaded message's sequence number) and key rows by ",{"type":42,"tag":65,"props":8252,"children":8254},{"className":8253},[],[8255],{"type":48,"value":8256},"index_base + physical",{"type":48,"value":8258},". To load older history, decrease ",{"type":42,"tag":65,"props":8260,"children":8262},{"className":8261},[],[8263],{"type":48,"value":8248},{"type":48,"value":8265}," by the prepended count in ",{"type":42,"tag":65,"props":8267,"children":8269},{"className":8268},[],[8270],{"type":48,"value":483},{"type":48,"value":8272}," — row identity, measured extents, and the viewport anchor all survive, and the offset grows by the prepended extent so the user keeps reading the same rows. ",{"type":42,"tag":65,"props":8274,"children":8276},{"className":8275},[],[8277],{"type":48,"value":8147},{"type":48,"value":8279}," re-arms from that growth exactly like ",{"type":42,"tag":65,"props":8281,"children":8283},{"className":8282},[],[8284],{"type":48,"value":8123},{"type":48,"value":8286}," re-arms from an append.",{"type":42,"tag":61,"props":8288,"children":8289},{},[8290,8295,8296,8302],{"type":42,"tag":513,"props":8291,"children":8292},{},[8293],{"type":48,"value":8294},"First build \u002F resize converge automatically",{"type":48,"value":4178},{"type":42,"tag":65,"props":8297,"children":8299},{"className":8298},[],[8300],{"type":48,"value":8301},"UiApp",{"type":48,"value":8303}," resolves the window against retained scroll state, and re-derives once against the fresh geometry when a build's window under-covers it (first build, window grew) or a measured correction is pending.",{"type":42,"tag":61,"props":8305,"children":8306},{},[8307,8312,8314,8319,8321,8327,8329,8334,8335,8340],{"type":42,"tag":513,"props":8308,"children":8309},{},[8310],{"type":48,"value":8311},"Markup exclusion (documented call)",{"type":48,"value":8313},": the closed grammar has no channel for a ",{"type":42,"tag":65,"props":8315,"children":8317},{"className":8316},[],[8318],{"type":48,"value":1236},{"type":48,"value":8320}," binding to receive the runtime's range request, nor a binding form for the extent-estimate fn, so the windowed list (uniform and variable) is builder-only; markup keeps bounded ",{"type":42,"tag":65,"props":8322,"children":8324},{"className":8323},[],[8325],{"type":48,"value":8326},"\u003Clist virtualized>",{"type":48,"value":8328}," (layout-culled) plus ",{"type":42,"tag":65,"props":8330,"children":8332},{"className":8331},[],[8333],{"type":48,"value":8133},{"type":48,"value":7831},{"type":42,"tag":65,"props":8336,"children":8338},{"className":8337},[],[8339],{"type":48,"value":1008},{"type":48,"value":8341}," for honest infinite fetch.",{"type":42,"tag":51,"props":8343,"children":8344},{},[8345,8347,8353,8355,8361],{"type":48,"value":8346},"The ",{"type":42,"tag":65,"props":8348,"children":8350},{"className":8349},[],[8351],{"type":48,"value":8352},"examples\u002Ffeed",{"type":48,"value":8354}," app is the reference: a 100,000-post deterministic MIXED-HEIGHT corpus (one-liners to long-form walls, estimate from body length), reach-end batching, per-post state by index, a zero-jump scroll-storm test, and snapshot telemetry (",{"type":42,"tag":65,"props":8356,"children":8358},{"className":8357},[],[8359],{"type":48,"value":8360},"widget_nodes=",{"type":48,"value":8362},") proving the window stays viewport-sized.",{"type":42,"tag":212,"props":8364,"children":8366},{"id":8365},"style-token-attributes",[8367],{"type":48,"value":8368},"Style token attributes",{"type":42,"tag":51,"props":8370,"children":8371},{},[8372,8374,8380],{"type":48,"value":8373},"Color and radius come from the design tokens, referenced by token NAME — literals only, no bindings, no raw colors (dynamic styling stays in Zig via ",{"type":42,"tag":65,"props":8375,"children":8377},{"className":8376},[],[8378],{"type":48,"value":8379},"ElementOptions.style",{"type":48,"value":794},{"type":42,"tag":57,"props":8382,"children":8383},{},[8384,8597],{"type":42,"tag":61,"props":8385,"children":8386},{},[8387,8389,8395,8396,8401,8402,8408,8409,8415,8416,8422,8423,8429,8431,8437,8439,8444,8445,8451,8452,8458,8459,8465,8466,8471,8472,8478,8479,8485,8486,8491,8492,8498,8499,8505,8506,8512,8513,8519,8520,8526,8527,8533,8534,8540,8541,8547,8548,8554,8555,8561,8562,8568,8569,8574,8576,8581,8583,8588,8590,8595],{"type":48,"value":8388},"Color attributes: ",{"type":42,"tag":65,"props":8390,"children":8392},{"className":8391},[],[8393],{"type":48,"value":8394},"background",{"type":48,"value":1820},{"type":42,"tag":65,"props":8397,"children":8399},{"className":8398},[],[8400],{"type":48,"value":2773},{"type":48,"value":1820},{"type":42,"tag":65,"props":8403,"children":8405},{"className":8404},[],[8406],{"type":48,"value":8407},"accent",{"type":48,"value":1820},{"type":42,"tag":65,"props":8410,"children":8412},{"className":8411},[],[8413],{"type":48,"value":8414},"accent-foreground",{"type":48,"value":1820},{"type":42,"tag":65,"props":8417,"children":8419},{"className":8418},[],[8420],{"type":48,"value":8421},"border-color",{"type":48,"value":1820},{"type":42,"tag":65,"props":8424,"children":8426},{"className":8425},[],[8427],{"type":48,"value":8428},"focus-ring",{"type":48,"value":8430},". Values are ",{"type":42,"tag":65,"props":8432,"children":8434},{"className":8433},[],[8435],{"type":48,"value":8436},"canvas.ColorTokens",{"type":48,"value":8438}," field names — the complete list: ",{"type":42,"tag":65,"props":8440,"children":8442},{"className":8441},[],[8443],{"type":48,"value":8394},{"type":48,"value":1820},{"type":42,"tag":65,"props":8446,"children":8448},{"className":8447},[],[8449],{"type":48,"value":8450},"surface",{"type":48,"value":1820},{"type":42,"tag":65,"props":8453,"children":8455},{"className":8454},[],[8456],{"type":48,"value":8457},"surface_subtle",{"type":48,"value":1820},{"type":42,"tag":65,"props":8460,"children":8462},{"className":8461},[],[8463],{"type":48,"value":8464},"surface_pressed",{"type":48,"value":1820},{"type":42,"tag":65,"props":8467,"children":8469},{"className":8468},[],[8470],{"type":48,"value":48},{"type":48,"value":1820},{"type":42,"tag":65,"props":8473,"children":8475},{"className":8474},[],[8476],{"type":48,"value":8477},"text_muted",{"type":48,"value":1820},{"type":42,"tag":65,"props":8480,"children":8482},{"className":8481},[],[8483],{"type":48,"value":8484},"border",{"type":48,"value":1820},{"type":42,"tag":65,"props":8487,"children":8489},{"className":8488},[],[8490],{"type":48,"value":8407},{"type":48,"value":1820},{"type":42,"tag":65,"props":8493,"children":8495},{"className":8494},[],[8496],{"type":48,"value":8497},"accent_text",{"type":48,"value":1820},{"type":42,"tag":65,"props":8500,"children":8502},{"className":8501},[],[8503],{"type":48,"value":8504},"destructive",{"type":48,"value":1820},{"type":42,"tag":65,"props":8507,"children":8509},{"className":8508},[],[8510],{"type":48,"value":8511},"destructive_text",{"type":48,"value":1820},{"type":42,"tag":65,"props":8514,"children":8516},{"className":8515},[],[8517],{"type":48,"value":8518},"success",{"type":48,"value":1820},{"type":42,"tag":65,"props":8521,"children":8523},{"className":8522},[],[8524],{"type":48,"value":8525},"success_text",{"type":48,"value":1820},{"type":42,"tag":65,"props":8528,"children":8530},{"className":8529},[],[8531],{"type":48,"value":8532},"warning",{"type":48,"value":1820},{"type":42,"tag":65,"props":8535,"children":8537},{"className":8536},[],[8538],{"type":48,"value":8539},"warning_text",{"type":48,"value":1820},{"type":42,"tag":65,"props":8542,"children":8544},{"className":8543},[],[8545],{"type":48,"value":8546},"info",{"type":48,"value":1820},{"type":42,"tag":65,"props":8549,"children":8551},{"className":8550},[],[8552],{"type":48,"value":8553},"info_text",{"type":48,"value":1820},{"type":42,"tag":65,"props":8556,"children":8558},{"className":8557},[],[8559],{"type":48,"value":8560},"focus_ring",{"type":48,"value":1820},{"type":42,"tag":65,"props":8563,"children":8565},{"className":8564},[],[8566],{"type":48,"value":8567},"shadow",{"type":48,"value":1820},{"type":42,"tag":65,"props":8570,"children":8572},{"className":8571},[],[8573],{"type":48,"value":1206},{"type":48,"value":8575},". ",{"type":42,"tag":65,"props":8577,"children":8579},{"className":8578},[],[8580],{"type":48,"value":8546},{"type":48,"value":8582}," is the violet identity hue beside the status trio (merged PR badges, \"new\" chips). (",{"type":42,"tag":65,"props":8584,"children":8586},{"className":8585},[],[8587],{"type":48,"value":8421},{"type":48,"value":8589},", not bare ",{"type":42,"tag":65,"props":8591,"children":8593},{"className":8592},[],[8594],{"type":48,"value":8484},{"type":48,"value":8596}," — that name is reserved for a future width shorthand.)",{"type":42,"tag":61,"props":8598,"children":8599},{},[8600,8606,8607,8613,8615,8620,8621,8627,8628,8634,8635,8641],{"type":42,"tag":65,"props":8601,"children":8603},{"className":8602},[],[8604],{"type":48,"value":8605},"radius",{"type":48,"value":83},{"type":42,"tag":65,"props":8608,"children":8610},{"className":8609},[],[8611],{"type":48,"value":8612},"canvas.RadiusTokens",{"type":48,"value":8614}," field names: ",{"type":42,"tag":65,"props":8616,"children":8618},{"className":8617},[],[8619],{"type":48,"value":5348},{"type":48,"value":1820},{"type":42,"tag":65,"props":8622,"children":8624},{"className":8623},[],[8625],{"type":48,"value":8626},"md",{"type":48,"value":1820},{"type":42,"tag":65,"props":8629,"children":8631},{"className":8630},[],[8632],{"type":48,"value":8633},"lg",{"type":48,"value":1820},{"type":42,"tag":65,"props":8636,"children":8638},{"className":8637},[],[8639],{"type":48,"value":8640},"xl",{"type":48,"value":123},{"type":42,"tag":219,"props":8643,"children":8645},{"className":1248,"code":8644,"language":1250,"meta":223,"style":223},"\u003Crow background=\"surface\" radius=\"md\" padding=\"8\">\n  \u003Ctext foreground=\"text_muted\">Muted caption\u003C\u002Ftext>\n\u003C\u002Frow>\n",[8646],{"type":42,"tag":65,"props":8647,"children":8648},{"__ignoreMap":223},[8649,8727,8780],{"type":42,"tag":229,"props":8650,"children":8651},{"class":231,"line":232},[8652,8656,8660,8665,8669,8673,8677,8681,8686,8690,8694,8698,8702,8707,8711,8715,8719,8723],{"type":42,"tag":229,"props":8653,"children":8654},{"style":1260},[8655],{"type":48,"value":1263},{"type":42,"tag":229,"props":8657,"children":8658},{"style":1266},[8659],{"type":48,"value":1963},{"type":42,"tag":229,"props":8661,"children":8662},{"style":1272},[8663],{"type":48,"value":8664}," background",{"type":42,"tag":229,"props":8666,"children":8667},{"style":1260},[8668],{"type":48,"value":1280},{"type":42,"tag":229,"props":8670,"children":8671},{"style":1260},[8672],{"type":48,"value":1285},{"type":42,"tag":229,"props":8674,"children":8675},{"style":1288},[8676],{"type":48,"value":8450},{"type":42,"tag":229,"props":8678,"children":8679},{"style":1260},[8680],{"type":48,"value":1285},{"type":42,"tag":229,"props":8682,"children":8683},{"style":1272},[8684],{"type":48,"value":8685}," radius",{"type":42,"tag":229,"props":8687,"children":8688},{"style":1260},[8689],{"type":48,"value":1280},{"type":42,"tag":229,"props":8691,"children":8692},{"style":1260},[8693],{"type":48,"value":1285},{"type":42,"tag":229,"props":8695,"children":8696},{"style":1288},[8697],{"type":48,"value":8626},{"type":42,"tag":229,"props":8699,"children":8700},{"style":1260},[8701],{"type":48,"value":1285},{"type":42,"tag":229,"props":8703,"children":8704},{"style":1272},[8705],{"type":48,"value":8706}," padding",{"type":42,"tag":229,"props":8708,"children":8709},{"style":1260},[8710],{"type":48,"value":1280},{"type":42,"tag":229,"props":8712,"children":8713},{"style":1260},[8714],{"type":48,"value":1285},{"type":42,"tag":229,"props":8716,"children":8717},{"style":1288},[8718],{"type":48,"value":7157},{"type":42,"tag":229,"props":8720,"children":8721},{"style":1260},[8722],{"type":48,"value":1285},{"type":42,"tag":229,"props":8724,"children":8725},{"style":1260},[8726],{"type":48,"value":1322},{"type":42,"tag":229,"props":8728,"children":8729},{"class":231,"line":241},[8730,8734,8738,8743,8747,8751,8755,8759,8763,8768,8772,8776],{"type":42,"tag":229,"props":8731,"children":8732},{"style":1260},[8733],{"type":48,"value":1330},{"type":42,"tag":229,"props":8735,"children":8736},{"style":1266},[8737],{"type":48,"value":48},{"type":42,"tag":229,"props":8739,"children":8740},{"style":1272},[8741],{"type":48,"value":8742}," foreground",{"type":42,"tag":229,"props":8744,"children":8745},{"style":1260},[8746],{"type":48,"value":1280},{"type":42,"tag":229,"props":8748,"children":8749},{"style":1260},[8750],{"type":48,"value":1285},{"type":42,"tag":229,"props":8752,"children":8753},{"style":1288},[8754],{"type":48,"value":8477},{"type":42,"tag":229,"props":8756,"children":8757},{"style":1260},[8758],{"type":48,"value":1285},{"type":42,"tag":229,"props":8760,"children":8761},{"style":1260},[8762],{"type":48,"value":1361},{"type":42,"tag":229,"props":8764,"children":8765},{"style":1364},[8766],{"type":48,"value":8767},"Muted caption",{"type":42,"tag":229,"props":8769,"children":8770},{"style":1260},[8771],{"type":48,"value":1371},{"type":42,"tag":229,"props":8773,"children":8774},{"style":1266},[8775],{"type":48,"value":48},{"type":42,"tag":229,"props":8777,"children":8778},{"style":1260},[8779],{"type":48,"value":1322},{"type":42,"tag":229,"props":8781,"children":8782},{"class":231,"line":251},[8783,8787,8791],{"type":42,"tag":229,"props":8784,"children":8785},{"style":1260},[8786],{"type":48,"value":1371},{"type":42,"tag":229,"props":8788,"children":8789},{"style":1266},[8790],{"type":48,"value":1963},{"type":42,"tag":229,"props":8792,"children":8793},{"style":1260},[8794],{"type":48,"value":1322},{"type":42,"tag":51,"props":8796,"children":8797},{},[8798,8800,8806,8808,8814,8815,8821,8823,8828,8830,8835,8837,8842,8844,8849,8851,8856,8858,8864,8866,8872],{"type":48,"value":8799},"References resolve against the app's LIVE tokens on every rebuild (",{"type":42,"tag":65,"props":8801,"children":8803},{"className":8802},[],[8804],{"type":48,"value":8805},"finalizeWithTokens",{"type":48,"value":8807},"), so a themed app (",{"type":42,"tag":65,"props":8809,"children":8811},{"className":8810},[],[8812],{"type":48,"value":8813},"tokens",{"type":48,"value":456},{"type":42,"tag":65,"props":8816,"children":8818},{"className":8817},[],[8819],{"type":48,"value":8820},"tokens_fn",{"type":48,"value":8822},") re-resolves them when the theme changes — dark mode flips ",{"type":42,"tag":65,"props":8824,"children":8826},{"className":8825},[],[8827],{"type":48,"value":8450},{"type":48,"value":8829}," automatically. The DEFAULT theme follows the system appearance: an app that sets neither ",{"type":42,"tag":65,"props":8831,"children":8833},{"className":8832},[],[8834],{"type":48,"value":8813},{"type":48,"value":8836}," nor ",{"type":42,"tag":65,"props":8838,"children":8840},{"className":8839},[],[8841],{"type":48,"value":8820},{"type":48,"value":8843}," derives the stock tokens from the OS light\u002Fdark setting (plus high-contrast and reduce-motion) and re-themes live when the user flips it. Pass explicit ",{"type":42,"tag":65,"props":8845,"children":8847},{"className":8846},[],[8848],{"type":48,"value":8813},{"type":48,"value":8850}," for a fixed look, or ",{"type":42,"tag":65,"props":8852,"children":8854},{"className":8853},[],[8855],{"type":48,"value":8820},{"type":48,"value":8857}," for model-owned theming (custom palettes usually still follow the system scheme through ",{"type":42,"tag":65,"props":8859,"children":8861},{"className":8860},[],[8862],{"type":48,"value":8863},"on_appearance",{"type":48,"value":8865},"). An explicit ",{"type":42,"tag":65,"props":8867,"children":8869},{"className":8868},[],[8870],{"type":48,"value":8871},"style",{"type":48,"value":8873}," value set in Zig always wins over a token ref on the same field. Unknown token names are validation\u002Fcompile errors.",{"type":42,"tag":51,"props":8875,"children":8876},{},[8877,8879,8885],{"type":48,"value":8878},"One Zig-only style knob rides beside the tokens: ",{"type":42,"tag":65,"props":8880,"children":8882},{"className":8881},[],[8883],{"type":48,"value":8884},"ElementOptions.style = .{ .quiet_hover = true }",{"type":48,"value":8886}," silences a pressable surface's pointer HOVER wash only — press and selection fills, the focus ring, cursor intent, and hit testing stay — for image-forward content tiles (cover art, photo cards) where the pointer rests on content rather than a control register. Acting controls (list rows, menu items, buttons, tab triggers) keep their washes: there the hover fill IS the affordance.",{"type":42,"tag":212,"props":8888,"children":8890},{"id":8889},"expressions-the-complete-grammar",[8891],{"type":48,"value":8892},"Expressions — the complete grammar",{"type":42,"tag":51,"props":8894,"children":8895},{},[8896,8898,8904,8906,8912],{"type":48,"value":8897},"Attribute values take a literal or exactly ONE ",{"type":42,"tag":65,"props":8899,"children":8901},{"className":8900},[],[8902],{"type":48,"value":8903},"{expression}",{"type":48,"value":8905},"; text content interpolates any number (",{"type":42,"tag":65,"props":8907,"children":8909},{"className":8908},[],[8910],{"type":48,"value":8911},"{open_count} open · {done_count} done",{"type":48,"value":8913},"). An expression is PURE and TOTAL — spreadsheet power, never a programming language: no user-defined functions, no effects, no computed message names, guaranteed termination.",{"type":42,"tag":57,"props":8915,"children":8916},{},[8917,8965,9007,9043,9085,9103],{"type":42,"tag":61,"props":8918,"children":8919},{},[8920,8922,8928,8930,8936,8937,8943,8945,8951,8953,8958,8959,8964],{"type":48,"value":8921},"Operands: binding paths (",{"type":42,"tag":65,"props":8923,"children":8925},{"className":8924},[],[8926],{"type":48,"value":8927},"{c.title}",{"type":48,"value":8929},"), numbers (",{"type":42,"tag":65,"props":8931,"children":8933},{"className":8932},[],[8934],{"type":48,"value":8935},"3",{"type":48,"value":1820},{"type":42,"tag":65,"props":8938,"children":8940},{"className":8939},[],[8941],{"type":48,"value":8942},"0.5",{"type":48,"value":8944}," — no exponents), ",{"type":42,"tag":65,"props":8946,"children":8948},{"className":8947},[],[8949],{"type":48,"value":8950},"'strings'",{"type":48,"value":8952}," (single quotes, no escapes), ",{"type":42,"tag":65,"props":8954,"children":8956},{"className":8955},[],[8957],{"type":48,"value":5105},{"type":48,"value":456},{"type":42,"tag":65,"props":8960,"children":8962},{"className":8961},[],[8963],{"type":48,"value":5112},{"type":48,"value":123},{"type":42,"tag":61,"props":8966,"children":8967},{},[8968,8970,8976,8978,8983,8985,8991,8992,8998,8999,9005],{"type":48,"value":8969},"Arithmetic ",{"type":42,"tag":65,"props":8971,"children":8973},{"className":8972},[],[8974],{"type":48,"value":8975},"+ - * \u002F",{"type":48,"value":8977},": numbers only (int op int stays int; any float promotes; ",{"type":42,"tag":65,"props":8979,"children":8981},{"className":8980},[],[8982],{"type":48,"value":456},{"type":48,"value":8984}," ALWAYS produces a float — wrap in ",{"type":42,"tag":65,"props":8986,"children":8988},{"className":8987},[],[8989],{"type":48,"value":8990},"round()",{"type":48,"value":456},{"type":42,"tag":65,"props":8993,"children":8995},{"className":8994},[],[8996],{"type":48,"value":8997},"floor()",{"type":48,"value":456},{"type":42,"tag":65,"props":9000,"children":9002},{"className":9001},[],[9003],{"type":48,"value":9004},"ceil()",{"type":48,"value":9006}," for whole-number attributes). Division by zero, integer overflow, and non-finite float results are loud errors, never silent zeros\u002FNaN\u002Finf.",{"type":42,"tag":61,"props":9008,"children":9009},{},[9010,9012,9018,9020,9026,9028,9034,9036,9042],{"type":48,"value":9011},"Comparison ",{"type":42,"tag":65,"props":9013,"children":9015},{"className":9014},[],[9016],{"type":48,"value":9017},"== != \u003C \u003C= > >=",{"type":48,"value":9019},": ordering takes numbers only; equality compares any two values (different types are simply NOT equal; int\u002Ffloat compare numerically). Comparisons do not chain (",{"type":42,"tag":65,"props":9021,"children":9023},{"className":9022},[],[9024],{"type":48,"value":9025},"a \u003C b \u003C c",{"type":48,"value":9027}," is an error — use ",{"type":42,"tag":65,"props":9029,"children":9031},{"className":9030},[],[9032],{"type":48,"value":9033},"and",{"type":48,"value":9035},"). Comparison operands reject arena-computed bindings (compare source fields, or bind a ",{"type":42,"tag":65,"props":9037,"children":9039},{"className":9038},[],[9040],{"type":48,"value":9041},"pub fn ... bool",{"type":48,"value":7386},{"type":42,"tag":61,"props":9044,"children":9045},{},[9046,9048,9053,9054,9060,9061,9067,9069,9075,9077,9083],{"type":48,"value":9047},"Boolean ",{"type":42,"tag":65,"props":9049,"children":9051},{"className":9050},[],[9052],{"type":48,"value":9033},{"type":48,"value":7747},{"type":42,"tag":65,"props":9055,"children":9057},{"className":9056},[],[9058],{"type":48,"value":9059},"or",{"type":48,"value":7747},{"type":42,"tag":65,"props":9062,"children":9064},{"className":9063},[],[9065],{"type":48,"value":9066},"not",{"type":48,"value":9068},": booleans only — write ",{"type":42,"tag":65,"props":9070,"children":9072},{"className":9071},[],[9073],{"type":48,"value":9074},"{count > 0}",{"type":48,"value":9076},", not ",{"type":42,"tag":65,"props":9078,"children":9080},{"className":9079},[],[9081],{"type":48,"value":9082},"{count}",{"type":48,"value":9084},". Both sides always evaluate (pure, so only errors observable).",{"type":42,"tag":61,"props":9086,"children":9087},{},[9088,9094,9096,9102],{"type":42,"tag":65,"props":9089,"children":9091},{"className":9090},[],[9092],{"type":48,"value":9093},"++",{"type":48,"value":9095}," concatenation: joins ANY values as display text, formatted exactly like interpolation (",{"type":42,"tag":65,"props":9097,"children":9099},{"className":9098},[],[9100],{"type":48,"value":9101},"{'$' ++ fixed(price, 2)}",{"type":48,"value":7386},{"type":42,"tag":61,"props":9104,"children":9105},{},[9106,9112,9113,9119,9120,9126],{"type":42,"tag":65,"props":9107,"children":9109},{"className":9108},[],[9110],{"type":48,"value":9111},"msg",{"type":48,"value":3426},{"type":42,"tag":65,"props":9114,"children":9116},{"className":9115},[],[9117],{"type":48,"value":9118},"msg:{path}",{"type":48,"value":7831},{"type":42,"tag":65,"props":9121,"children":9123},{"className":9122},[],[9124],{"type":48,"value":9125},"on-*",{"type":48,"value":9127}," attributes stays its own form: tags and payloads are paths, never expressions.",{"type":42,"tag":51,"props":9129,"children":9130},{},[9131],{"type":48,"value":9132},"The function library is CLOSED (17 functions; adding one is a toolkit change — anything else is a model fn):",{"type":42,"tag":1920,"props":9134,"children":9135},{},[9136,9152],{"type":42,"tag":1924,"props":9137,"children":9138},{},[9139],{"type":42,"tag":1928,"props":9140,"children":9141},{},[9142,9147],{"type":42,"tag":1932,"props":9143,"children":9144},{},[9145],{"type":48,"value":9146},"fn",{"type":42,"tag":1932,"props":9148,"children":9149},{},[9150],{"type":48,"value":9151},"notes",{"type":42,"tag":1948,"props":9153,"children":9154},{},[9155,9188,9220,9250,9304,9335,9366,9397,9421],{"type":42,"tag":1928,"props":9156,"children":9157},{},[9158,9167],{"type":42,"tag":1955,"props":9159,"children":9160},{},[9161],{"type":42,"tag":65,"props":9162,"children":9164},{"className":9163},[],[9165],{"type":48,"value":9166},"fixed(x, digits)",{"type":42,"tag":1955,"props":9168,"children":9169},{},[9170,9172,9178,9180,9186],{"type":48,"value":9171},"exact decimals, digits 0-6, half-away rounding (",{"type":42,"tag":65,"props":9173,"children":9175},{"className":9174},[],[9176],{"type":48,"value":9177},"fixed(3.14159, 2)",{"type":48,"value":9179}," → ",{"type":42,"tag":65,"props":9181,"children":9183},{"className":9182},[],[9184],{"type":48,"value":9185},"3.14",{"type":48,"value":9187},")",{"type":42,"tag":1928,"props":9189,"children":9190},{},[9191,9200],{"type":42,"tag":1955,"props":9192,"children":9193},{},[9194],{"type":42,"tag":65,"props":9195,"children":9197},{"className":9196},[],[9198],{"type":48,"value":9199},"thousands(n)",{"type":42,"tag":1955,"props":9201,"children":9202},{},[9203,9205,9211,9213,9219],{"type":48,"value":9204},"whole number with ",{"type":42,"tag":65,"props":9206,"children":9208},{"className":9207},[],[9209],{"type":48,"value":9210},",",{"type":48,"value":9212}," separators (",{"type":42,"tag":65,"props":9214,"children":9216},{"className":9215},[],[9217],{"type":48,"value":9218},"1,234,567",{"type":48,"value":9187},{"type":42,"tag":1928,"props":9221,"children":9222},{},[9223,9232],{"type":42,"tag":1955,"props":9224,"children":9225},{},[9226],{"type":42,"tag":65,"props":9227,"children":9229},{"className":9228},[],[9230],{"type":48,"value":9231},"percent(fraction, digits?)",{"type":42,"tag":1955,"props":9233,"children":9234},{},[9235,9241,9242,9248],{"type":42,"tag":65,"props":9236,"children":9238},{"className":9237},[],[9239],{"type":48,"value":9240},"percent(0.42)",{"type":48,"value":9179},{"type":42,"tag":65,"props":9243,"children":9245},{"className":9244},[],[9246],{"type":48,"value":9247},"42%",{"type":48,"value":9249},"; digits default 0",{"type":42,"tag":1928,"props":9251,"children":9252},{},[9253,9276],{"type":42,"tag":1955,"props":9254,"children":9255},{},[9256,9262,9263,9269,9270],{"type":42,"tag":65,"props":9257,"children":9259},{"className":9258},[],[9260],{"type":48,"value":9261},"date(ts)",{"type":48,"value":7747},{"type":42,"tag":65,"props":9264,"children":9266},{"className":9265},[],[9267],{"type":48,"value":9268},"time(ts)",{"type":48,"value":7747},{"type":42,"tag":65,"props":9271,"children":9273},{"className":9272},[],[9274],{"type":48,"value":9275},"datetime(ts)",{"type":42,"tag":1955,"props":9277,"children":9278},{},[9279,9281,9287,9288,9294,9296,9302],{"type":48,"value":9280},"unix SECONDS from the model, formatted UTC (",{"type":42,"tag":65,"props":9282,"children":9284},{"className":9283},[],[9285],{"type":48,"value":9286},"2026-07-05",{"type":48,"value":1820},{"type":42,"tag":65,"props":9289,"children":9291},{"className":9290},[],[9292],{"type":48,"value":9293},"14:03",{"type":48,"value":9295},"); formatting model time is pure — ",{"type":42,"tag":65,"props":9297,"children":9299},{"className":9298},[],[9300],{"type":48,"value":9301},"now()",{"type":48,"value":9303}," is a teaching error: reading the clock is an effect, keep a timestamp field updated by update\u002Ffx",{"type":42,"tag":1928,"props":9305,"children":9306},{},[9307,9330],{"type":42,"tag":1955,"props":9308,"children":9309},{},[9310,9316,9317,9323,9324],{"type":42,"tag":65,"props":9311,"children":9313},{"className":9312},[],[9314],{"type":48,"value":9315},"upper(s)",{"type":48,"value":7747},{"type":42,"tag":65,"props":9318,"children":9320},{"className":9319},[],[9321],{"type":48,"value":9322},"lower(s)",{"type":48,"value":7747},{"type":42,"tag":65,"props":9325,"children":9327},{"className":9326},[],[9328],{"type":48,"value":9329},"trim(s)",{"type":42,"tag":1955,"props":9331,"children":9332},{},[9333],{"type":48,"value":9334},"ASCII case map \u002F whitespace trim; non-ASCII passes through unchanged",{"type":42,"tag":1928,"props":9336,"children":9337},{},[9338,9361],{"type":42,"tag":1955,"props":9339,"children":9340},{},[9341,9347,9348,9354,9355],{"type":42,"tag":65,"props":9342,"children":9344},{"className":9343},[],[9345],{"type":48,"value":9346},"min(a, b)",{"type":48,"value":7747},{"type":42,"tag":65,"props":9349,"children":9351},{"className":9350},[],[9352],{"type":48,"value":9353},"max(a, b)",{"type":48,"value":7747},{"type":42,"tag":65,"props":9356,"children":9358},{"className":9357},[],[9359],{"type":48,"value":9360},"abs(x)",{"type":42,"tag":1955,"props":9362,"children":9363},{},[9364],{"type":48,"value":9365},"numbers; int stays int",{"type":42,"tag":1928,"props":9367,"children":9368},{},[9369,9392],{"type":42,"tag":1955,"props":9370,"children":9371},{},[9372,9378,9379,9385,9386],{"type":42,"tag":65,"props":9373,"children":9375},{"className":9374},[],[9376],{"type":48,"value":9377},"round(x)",{"type":48,"value":7747},{"type":42,"tag":65,"props":9380,"children":9382},{"className":9381},[],[9383],{"type":48,"value":9384},"floor(x)",{"type":48,"value":7747},{"type":42,"tag":65,"props":9387,"children":9389},{"className":9388},[],[9390],{"type":48,"value":9391},"ceil(x)",{"type":42,"tag":1955,"props":9393,"children":9394},{},[9395],{"type":48,"value":9396},"number → whole number",{"type":42,"tag":1928,"props":9398,"children":9399},{},[9400,9409],{"type":42,"tag":1955,"props":9401,"children":9402},{},[9403],{"type":42,"tag":65,"props":9404,"children":9406},{"className":9405},[],[9407],{"type":48,"value":9408},"plural(count, singular, plural)",{"type":42,"tag":1955,"props":9410,"children":9411},{},[9412,9414,9420],{"type":48,"value":9413},"count exactly 1 picks singular (",{"type":42,"tag":65,"props":9415,"children":9417},{"className":9416},[],[9418],{"type":48,"value":9419},"{plural(n, 'item', 'items')}",{"type":48,"value":9187},{"type":42,"tag":1928,"props":9422,"children":9423},{},[9424,9433],{"type":42,"tag":1955,"props":9425,"children":9426},{},[9427],{"type":42,"tag":65,"props":9428,"children":9430},{"className":9429},[],[9431],{"type":48,"value":9432},"pad(x, width)",{"type":42,"tag":1955,"props":9434,"children":9435},{},[9436,9438,9443,9445,9451,9452,9458,9460,9466],{"type":48,"value":9437},"zero-pads the integer value of x to ",{"type":42,"tag":65,"props":9439,"children":9441},{"className":9440},[],[9442],{"type":48,"value":2339},{"type":48,"value":9444}," digits (",{"type":42,"tag":65,"props":9446,"children":9448},{"className":9447},[],[9449],{"type":48,"value":9450},"pad(7, 2)",{"type":48,"value":9179},{"type":42,"tag":65,"props":9453,"children":9455},{"className":9454},[],[9456],{"type":48,"value":9457},"07",{"type":48,"value":9459},"); a negative sign precedes the zeros and does not count toward width; numbers wider than width print in full — the mm:ss counter fn (",{"type":42,"tag":65,"props":9461,"children":9463},{"className":9462},[],[9464],{"type":48,"value":9465},"{pad(minutes, 2)}:{pad(seconds, 2)}",{"type":48,"value":9187},{"type":42,"tag":51,"props":9468,"children":9469},{},[9470,9472,9477,9479,9485,9487,9493],{"type":48,"value":9471},"Bounds (taught one past): 256 bytes, 64 terms, 16 nesting levels per expression. Where expressions are allowed: text interpolation, attribute values, ",{"type":42,"tag":65,"props":9473,"children":9475},{"className":9474},[],[9476],{"type":48,"value":1222},{"type":48,"value":9478}," tests, template args at use sites. Path-only by design: message tags\u002Fpayloads, ",{"type":42,"tag":65,"props":9480,"children":9482},{"className":9481},[],[9483],{"type":48,"value":9484},"for each",{"type":48,"value":9486}," iterables, import paths. Both engines evaluate through ONE shared evaluator — results are bit-for-bit identical, floats included — and ",{"type":42,"tag":65,"props":9488,"children":9490},{"className":9489},[],[9491],{"type":48,"value":9492},"native markup check",{"type":48,"value":9494}," validates syntax, bounds, function names (with did-you-mean), arity, and literal types without needing the model.",{"type":42,"tag":51,"props":9496,"children":9497},{},[9498,9500,9506,9507,9513],{"type":48,"value":9499},"Anything stateful or beyond the grammar is a Zig model function you bind to (",{"type":42,"tag":65,"props":9501,"children":9503},{"className":9502},[],[9504],{"type":48,"value":9505},"each=\"visible\"",{"type":48,"value":1820},{"type":42,"tag":65,"props":9508,"children":9510},{"className":9509},[],[9511],{"type":48,"value":9512},"{summaryLine}",{"type":48,"value":7386},{"type":42,"tag":51,"props":9515,"children":9516},{},[9517,9519,9525,9527,9533],{"type":48,"value":9518},"Where the line sits between inline arithmetic and a model fn: inline expression arithmetic is sanctioned for ONE-OFF presentation-level derivation — ",{"type":42,"tag":65,"props":9520,"children":9522},{"className":9521},[],[9523],{"type":48,"value":9524},"{percent(done \u002F total)}",{"type":48,"value":9526}," on the single readout that shows it is exactly what expressions are for. The moment a derivation is REUSED in a second binding, deserves a NAME, or carries meaning the model owns (a threshold, a rule, a policy), it belongs in a named model function: ",{"type":42,"tag":65,"props":9528,"children":9530},{"className":9529},[],[9531],{"type":48,"value":9532},"{completionRate}",{"type":48,"value":9534}," reads at the binding site, tests in Zig, and changes in one place.",{"type":42,"tag":212,"props":9536,"children":9538},{"id":9537},"binding-resolution-rules",[9539],{"type":48,"value":9540},"Binding resolution rules",{"type":42,"tag":51,"props":9542,"children":9543},{},[9544,9546,9552,9554,9559],{"type":48,"value":9545},"A path like ",{"type":42,"tag":65,"props":9547,"children":9549},{"className":9548},[],[9550],{"type":48,"value":9551},"{h.streak}",{"type":48,"value":9553}," resolves left to right, starting from the model or a ",{"type":42,"tag":65,"props":9555,"children":9557},{"className":9556},[],[9558],{"type":48,"value":1236},{"type":48,"value":9560}," variable:",{"type":42,"tag":57,"props":9562,"children":9563},{},[9564,9627,9645,9664,9711,9740,9775],{"type":42,"tag":61,"props":9565,"children":9566},{},[9567,9569,9575,9577,9583,9585,9590,9592,9597,9599,9605,9606,9611,9613,9619,9621],{"type":48,"value":9568},"everything bindable is declared INSIDE the Model struct: fields, and ",{"type":42,"tag":65,"props":9570,"children":9572},{"className":9571},[],[9573],{"type":48,"value":9574},"pub fn",{"type":48,"value":9576}," METHODS in the struct body. A file-scope ",{"type":42,"tag":65,"props":9578,"children":9580},{"className":9579},[],[9581],{"type":48,"value":9582},"pub fn visibleRows(model: *const Model, ...)",{"type":48,"value":9584}," written NEXT TO the struct is invisible to bindings and ",{"type":42,"tag":65,"props":9586,"children":9588},{"className":9587},[],[9589],{"type":48,"value":9484},{"type":48,"value":9591}," — a model-free ",{"type":42,"tag":65,"props":9593,"children":9595},{"className":9594},[],[9596],{"type":48,"value":9492},{"type":48,"value":9598}," still passes (grammar-only), and the view then fails at test\u002Frun time with \"each does not name an iterable\"; with a fresh model contract (refreshed by ",{"type":42,"tag":65,"props":9600,"children":9602},{"className":9601},[],[9603],{"type":48,"value":9604},"native test",{"type":48,"value":4517},{"type":42,"tag":65,"props":9607,"children":9609},{"className":9608},[],[9610],{"type":48,"value":3347},{"type":48,"value":9612}," catches it instantly with a did-you-mean. If a binding or ",{"type":42,"tag":65,"props":9614,"children":9616},{"className":9615},[],[9617],{"type":48,"value":9618},"each",{"type":48,"value":9620}," cannot find your fn, first check it lives inside ",{"type":42,"tag":65,"props":9622,"children":9624},{"className":9623},[],[9625],{"type":48,"value":9626},"pub const Model = struct { ... }",{"type":42,"tag":61,"props":9628,"children":9629},{},[9630,9632,9638,9639],{"type":48,"value":9631},"struct fields bind directly: ",{"type":42,"tag":65,"props":9633,"children":9635},{"className":9634},[],[9636],{"type":48,"value":9637},"{habit_count}",{"type":48,"value":1820},{"type":42,"tag":65,"props":9640,"children":9642},{"className":9641},[],[9643],{"type":48,"value":9644},"{h.done}",{"type":42,"tag":61,"props":9646,"children":9647},{},[9648,9650,9656,9658],{"type":48,"value":9649},"zero-arg pub methods bind like fields: ",{"type":42,"tag":65,"props":9651,"children":9653},{"className":9652},[],[9654],{"type":48,"value":9655},"{totalDays}",{"type":48,"value":9657}," calls ",{"type":42,"tag":65,"props":9659,"children":9661},{"className":9660},[],[9662],{"type":48,"value":9663},"pub fn totalDays(m: *const Model) usize",{"type":42,"tag":61,"props":9665,"children":9666},{},[9667,9669,9675,9676,9682,9684,9690,9692,9698,9699,9704,9706],{"type":48,"value":9668},"arena-taking scalar methods bind the same way: ",{"type":42,"tag":65,"props":9670,"children":9672},{"className":9671},[],[9673],{"type":48,"value":9674},"{summary}",{"type":48,"value":9657},{"type":42,"tag":65,"props":9677,"children":9679},{"className":9678},[],[9680],{"type":48,"value":9681},"pub fn summary(m: *const Model, arena: std.mem.Allocator) []const u8",{"type":48,"value":9683}," — format derived display strings straight into the build arena (it lives exactly one view build). Works anywhere a scalar binding does — text interpolation, attribute values, message payloads, expression function arguments (",{"type":42,"tag":65,"props":9685,"children":9687},{"className":9686},[],[9688],{"type":48,"value":9689},"{upper(summary)}",{"type":48,"value":9691},") — EXCEPT as a comparison operand (",{"type":42,"tag":65,"props":9693,"children":9695},{"className":9694},[],[9696],{"type":48,"value":9697},"==",{"type":48,"value":1820},{"type":42,"tag":65,"props":9700,"children":9702},{"className":9701},[],[9703],{"type":48,"value":1263},{"type":48,"value":9705},", ...), which rejects arena-computed values with a teaching error: compare the source fields, or bind a ",{"type":42,"tag":65,"props":9707,"children":9709},{"className":9708},[],[9710],{"type":48,"value":9041},{"type":42,"tag":61,"props":9712,"children":9713},{},[9714,9716,9722,9724,9730,9732,9738],{"type":48,"value":9715},"enums resolve to their tag name — so ",{"type":42,"tag":65,"props":9717,"children":9719},{"className":9718},[],[9720],{"type":48,"value":9721},"{f}",{"type":48,"value":9723}," renders \"active\", ",{"type":42,"tag":65,"props":9725,"children":9727},{"className":9726},[],[9728],{"type":48,"value":9729},"{f == filter}",{"type":48,"value":9731}," compares tags, and ",{"type":42,"tag":65,"props":9733,"children":9735},{"className":9734},[],[9736],{"type":48,"value":9737},"set_filter:{f}",{"type":48,"value":9739}," coerces the tag back into an enum payload",{"type":42,"tag":61,"props":9741,"children":9742},{},[9743,9749,9751,9757,9759,9765,9767,9773],{"type":42,"tag":65,"props":9744,"children":9746},{"className":9745},[],[9747],{"type":48,"value":9748},"for each=\"name\"",{"type":48,"value":9750}," resolves, in order: a Model field that is a slice\u002Farray, a pub array\u002Fslice decl (",{"type":42,"tag":65,"props":9752,"children":9754},{"className":9753},[],[9755],{"type":48,"value":9756},"pub const filters = [_]Filter{...}",{"type":48,"value":9758},"), a pub fn ",{"type":42,"tag":65,"props":9760,"children":9762},{"className":9761},[],[9763],{"type":48,"value":9764},"(*const Model) []const T",{"type":48,"value":9766},", or a pub fn ",{"type":42,"tag":65,"props":9768,"children":9770},{"className":9769},[],[9771],{"type":48,"value":9772},"(*const Model, std.mem.Allocator) []const T",{"type":48,"value":9774}," — the allocator variant is how filtered\u002Fderived lists work (allocate from the passed arena)",{"type":42,"tag":61,"props":9776,"children":9777},{},[9778,9780,9786,9788],{"type":48,"value":9779},"item methods work too: ",{"type":42,"tag":65,"props":9781,"children":9783},{"className":9782},[],[9784],{"type":48,"value":9785},"{h.name}",{"type":48,"value":9787}," may be a field or ",{"type":42,"tag":65,"props":9789,"children":9791},{"className":9790},[],[9792],{"type":48,"value":9793},"pub fn name(h: *const Habit) []const u8",{"type":42,"tag":51,"props":9795,"children":9796},{},[9797],{"type":48,"value":9798},"Bindings are zero-argument. A parameterized query (cards of column X) becomes one model function per case.",{"type":42,"tag":212,"props":9800,"children":9802},{"id":9801},"derive-dont-store",[9803],{"type":48,"value":9804},"Derive, don't store",{"type":42,"tag":51,"props":9806,"children":9807},{},[9808,9810,9815],{"type":48,"value":9809},"The model stores source-of-truth state ONLY: the raw items, the current filter, the draft text. Anything the view shows that is computable from those — counts, sums, filtered views, formatted strings — is a pub method the markup binds to, never a model field. A cached derivable must be re-maintained in every ",{"type":42,"tag":65,"props":9811,"children":9813},{"className":9812},[],[9814],{"type":48,"value":483},{"type":48,"value":9816}," arm and goes stale the moment one is missed; a derived method cannot.",{"type":42,"tag":219,"props":9818,"children":9820},{"className":221,"code":9819,"language":18,"meta":223,"style":223},"\u002F\u002F WRONG: derived state cached in the model, maintained by hand in update()\nvisible_count: usize,\nsummary_storage: [64]u8,   \u002F\u002F preformatted display string\n\n\u002F\u002F RIGHT: the model keeps integers + the filter; methods derive per rebuild\npub fn visibleCount(model: *const Model) usize { ... }\npub fn visibleCents(model: *const Model) u64 { ... }\n",[9821],{"type":42,"tag":65,"props":9822,"children":9823},{"__ignoreMap":223},[9824,9832,9840,9848,9855,9863,9871],{"type":42,"tag":229,"props":9825,"children":9826},{"class":231,"line":232},[9827],{"type":42,"tag":229,"props":9828,"children":9829},{},[9830],{"type":48,"value":9831},"\u002F\u002F WRONG: derived state cached in the model, maintained by hand in update()\n",{"type":42,"tag":229,"props":9833,"children":9834},{"class":231,"line":241},[9835],{"type":42,"tag":229,"props":9836,"children":9837},{},[9838],{"type":48,"value":9839},"visible_count: usize,\n",{"type":42,"tag":229,"props":9841,"children":9842},{"class":231,"line":251},[9843],{"type":42,"tag":229,"props":9844,"children":9845},{},[9846],{"type":48,"value":9847},"summary_storage: [64]u8,   \u002F\u002F preformatted display string\n",{"type":42,"tag":229,"props":9849,"children":9850},{"class":231,"line":260},[9851],{"type":42,"tag":229,"props":9852,"children":9853},{"emptyLinePlaceholder":245},[9854],{"type":48,"value":248},{"type":42,"tag":229,"props":9856,"children":9857},{"class":231,"line":269},[9858],{"type":42,"tag":229,"props":9859,"children":9860},{},[9861],{"type":48,"value":9862},"\u002F\u002F RIGHT: the model keeps integers + the filter; methods derive per rebuild\n",{"type":42,"tag":229,"props":9864,"children":9865},{"class":231,"line":278},[9866],{"type":42,"tag":229,"props":9867,"children":9868},{},[9869],{"type":48,"value":9870},"pub fn visibleCount(model: *const Model) usize { ... }\n",{"type":42,"tag":229,"props":9872,"children":9873},{"class":231,"line":287},[9874],{"type":42,"tag":229,"props":9875,"children":9876},{},[9877],{"type":48,"value":9878},"pub fn visibleCents(model: *const Model) u64 { ... }\n",{"type":42,"tag":51,"props":9880,"children":9881},{},[9882,9884,9889],{"type":48,"value":9883},"Derived numbers need no allocation: bind the methods and let text interpolation compose the line — this is exactly how the examples' status bars work (",{"type":42,"tag":65,"props":9885,"children":9887},{"className":9886},[],[9888],{"type":48,"value":608},{"type":48,"value":794},{"type":42,"tag":219,"props":9891,"children":9893},{"className":1248,"code":9892,"language":1250,"meta":223,"style":223},"\u003Cstatus-bar>{habit_count} habits · {totalDays} total days\u003C\u002Fstatus-bar>\n",[9894],{"type":42,"tag":65,"props":9895,"children":9896},{"__ignoreMap":223},[9897],{"type":42,"tag":229,"props":9898,"children":9899},{"class":231,"line":232},[9900,9904,9908,9912,9917,9921,9925],{"type":42,"tag":229,"props":9901,"children":9902},{"style":1260},[9903],{"type":48,"value":1263},{"type":42,"tag":229,"props":9905,"children":9906},{"style":1266},[9907],{"type":48,"value":3171},{"type":42,"tag":229,"props":9909,"children":9910},{"style":1260},[9911],{"type":48,"value":1361},{"type":42,"tag":229,"props":9913,"children":9914},{"style":1364},[9915],{"type":48,"value":9916},"{habit_count} habits · {totalDays} total days",{"type":42,"tag":229,"props":9918,"children":9919},{"style":1260},[9920],{"type":48,"value":1371},{"type":42,"tag":229,"props":9922,"children":9923},{"style":1266},[9924],{"type":48,"value":3171},{"type":42,"tag":229,"props":9926,"children":9927},{"style":1260},[9928],{"type":48,"value":1322},{"type":42,"tag":51,"props":9930,"children":9931},{},[9932,9934,9939,9941,9947],{"type":48,"value":9933},"Computed strings (money, dates, percentages) are formatted into the BUILD ARENA inside the ",{"type":42,"tag":65,"props":9935,"children":9937},{"className":9936},[],[9938],{"type":48,"value":9484},{"type":48,"value":9940}," allocator fn — derive display rows whose string fields are ",{"type":42,"tag":65,"props":9942,"children":9944},{"className":9943},[],[9945],{"type":48,"value":9946},"allocPrint",{"type":48,"value":9948},"ed there. The arena lives for exactly one view build, so nothing is stored and nothing goes stale. Store amounts as integer cents; format at view time:",{"type":42,"tag":219,"props":9950,"children":9952},{"className":221,"code":9951,"language":18,"meta":223,"style":223},"pub const VisibleExpense = struct { id: u32, date: []const u8, amount: []const u8 };\n\n\u002F\u002F A METHOD — declared inside `pub const Model = struct { ... }`. Bindings and\n\u002F\u002F `for each` resolve Model decls only; the same fn at file scope is invisible.\npub fn visible(model: *const Model, arena: std.mem.Allocator) []const VisibleExpense {\n    const out = arena.alloc(VisibleExpense, model.expense_count) catch return &.{};\n    var count: usize = 0;\n    for (model.expenses[0..model.expense_count]) |*e| {\n        if (!model.matches(e.*)) continue;\n        out[count] = .{\n            .id = e.id,\n            .date = e.date(),\n            .amount = std.fmt.allocPrint(arena, \"${d}.{d:0>2}\", .{ e.amount_cents \u002F 100, e.amount_cents % 100 }) catch \"\",\n        };\n        count += 1;\n    }\n    return out[0..count];\n}\n",[9953],{"type":42,"tag":65,"props":9954,"children":9955},{"__ignoreMap":223},[9956,9964,9971,9979,9987,9995,10003,10011,10019,10027,10035,10043,10051,10059,10067,10074,10081,10089],{"type":42,"tag":229,"props":9957,"children":9958},{"class":231,"line":232},[9959],{"type":42,"tag":229,"props":9960,"children":9961},{},[9962],{"type":48,"value":9963},"pub const VisibleExpense = struct { id: u32, date: []const u8, amount: []const u8 };\n",{"type":42,"tag":229,"props":9965,"children":9966},{"class":231,"line":241},[9967],{"type":42,"tag":229,"props":9968,"children":9969},{"emptyLinePlaceholder":245},[9970],{"type":48,"value":248},{"type":42,"tag":229,"props":9972,"children":9973},{"class":231,"line":251},[9974],{"type":42,"tag":229,"props":9975,"children":9976},{},[9977],{"type":48,"value":9978},"\u002F\u002F A METHOD — declared inside `pub const Model = struct { ... }`. Bindings and\n",{"type":42,"tag":229,"props":9980,"children":9981},{"class":231,"line":260},[9982],{"type":42,"tag":229,"props":9983,"children":9984},{},[9985],{"type":48,"value":9986},"\u002F\u002F `for each` resolve Model decls only; the same fn at file scope is invisible.\n",{"type":42,"tag":229,"props":9988,"children":9989},{"class":231,"line":269},[9990],{"type":42,"tag":229,"props":9991,"children":9992},{},[9993],{"type":48,"value":9994},"pub fn visible(model: *const Model, arena: std.mem.Allocator) []const VisibleExpense {\n",{"type":42,"tag":229,"props":9996,"children":9997},{"class":231,"line":278},[9998],{"type":42,"tag":229,"props":9999,"children":10000},{},[10001],{"type":48,"value":10002},"    const out = arena.alloc(VisibleExpense, model.expense_count) catch return &.{};\n",{"type":42,"tag":229,"props":10004,"children":10005},{"class":231,"line":287},[10006],{"type":42,"tag":229,"props":10007,"children":10008},{},[10009],{"type":48,"value":10010},"    var count: usize = 0;\n",{"type":42,"tag":229,"props":10012,"children":10013},{"class":231,"line":296},[10014],{"type":42,"tag":229,"props":10015,"children":10016},{},[10017],{"type":48,"value":10018},"    for (model.expenses[0..model.expense_count]) |*e| {\n",{"type":42,"tag":229,"props":10020,"children":10021},{"class":231,"line":305},[10022],{"type":42,"tag":229,"props":10023,"children":10024},{},[10025],{"type":48,"value":10026},"        if (!model.matches(e.*)) continue;\n",{"type":42,"tag":229,"props":10028,"children":10029},{"class":231,"line":314},[10030],{"type":42,"tag":229,"props":10031,"children":10032},{},[10033],{"type":48,"value":10034},"        out[count] = .{\n",{"type":42,"tag":229,"props":10036,"children":10037},{"class":231,"line":323},[10038],{"type":42,"tag":229,"props":10039,"children":10040},{},[10041],{"type":48,"value":10042},"            .id = e.id,\n",{"type":42,"tag":229,"props":10044,"children":10045},{"class":231,"line":332},[10046],{"type":42,"tag":229,"props":10047,"children":10048},{},[10049],{"type":48,"value":10050},"            .date = e.date(),\n",{"type":42,"tag":229,"props":10052,"children":10053},{"class":231,"line":341},[10054],{"type":42,"tag":229,"props":10055,"children":10056},{},[10057],{"type":48,"value":10058},"            .amount = std.fmt.allocPrint(arena, \"${d}.{d:0>2}\", .{ e.amount_cents \u002F 100, e.amount_cents % 100 }) catch \"\",\n",{"type":42,"tag":229,"props":10060,"children":10061},{"class":231,"line":350},[10062],{"type":42,"tag":229,"props":10063,"children":10064},{},[10065],{"type":48,"value":10066},"        };\n",{"type":42,"tag":229,"props":10068,"children":10069},{"class":231,"line":359},[10070],{"type":42,"tag":229,"props":10071,"children":10072},{},[10073],{"type":48,"value":948},{"type":42,"tag":229,"props":10075,"children":10076},{"class":231,"line":368},[10077],{"type":42,"tag":229,"props":10078,"children":10079},{},[10080],{"type":48,"value":956},{"type":42,"tag":229,"props":10082,"children":10083},{"class":231,"line":377},[10084],{"type":42,"tag":229,"props":10085,"children":10086},{},[10087],{"type":48,"value":10088},"    return out[0..count];\n",{"type":42,"tag":229,"props":10090,"children":10091},{"class":231,"line":386},[10092],{"type":42,"tag":229,"props":10093,"children":10094},{},[10095],{"type":48,"value":428},{"type":42,"tag":51,"props":10097,"children":10098},{},[10099],{"type":48,"value":10100},"A one-off formatted line that plain interpolation can't express (e.g. a currency total in a summary) is an arena-taking scalar fn bound directly:",{"type":42,"tag":219,"props":10102,"children":10104},{"className":221,"code":10103,"language":18,"meta":223,"style":223},"pub fn summary(model: *const Model, arena: std.mem.Allocator) []const u8 {\n    return std.fmt.allocPrint(arena, \"{d} expenses · {s} total\", .{\n        model.visibleCount(), formatCents(arena, model.visibleCents()),\n    }) catch \"\";\n}\n",[10105],{"type":42,"tag":65,"props":10106,"children":10107},{"__ignoreMap":223},[10108,10116,10124,10132,10140],{"type":42,"tag":229,"props":10109,"children":10110},{"class":231,"line":232},[10111],{"type":42,"tag":229,"props":10112,"children":10113},{},[10114],{"type":48,"value":10115},"pub fn summary(model: *const Model, arena: std.mem.Allocator) []const u8 {\n",{"type":42,"tag":229,"props":10117,"children":10118},{"class":231,"line":241},[10119],{"type":42,"tag":229,"props":10120,"children":10121},{},[10122],{"type":48,"value":10123},"    return std.fmt.allocPrint(arena, \"{d} expenses · {s} total\", .{\n",{"type":42,"tag":229,"props":10125,"children":10126},{"class":231,"line":251},[10127],{"type":42,"tag":229,"props":10128,"children":10129},{},[10130],{"type":48,"value":10131},"        model.visibleCount(), formatCents(arena, model.visibleCents()),\n",{"type":42,"tag":229,"props":10133,"children":10134},{"class":231,"line":260},[10135],{"type":42,"tag":229,"props":10136,"children":10137},{},[10138],{"type":48,"value":10139},"    }) catch \"\";\n",{"type":42,"tag":229,"props":10141,"children":10142},{"class":231,"line":269},[10143],{"type":42,"tag":229,"props":10144,"children":10145},{},[10146],{"type":48,"value":428},{"type":42,"tag":219,"props":10148,"children":10150},{"className":1248,"code":10149,"language":1250,"meta":223,"style":223},"\u003Cstatus-bar>{summary}\u003C\u002Fstatus-bar>\n",[10151],{"type":42,"tag":65,"props":10152,"children":10153},{"__ignoreMap":223},[10154],{"type":42,"tag":229,"props":10155,"children":10156},{"class":231,"line":232},[10157,10161,10165,10169,10173,10177,10181],{"type":42,"tag":229,"props":10158,"children":10159},{"style":1260},[10160],{"type":48,"value":1263},{"type":42,"tag":229,"props":10162,"children":10163},{"style":1266},[10164],{"type":48,"value":3171},{"type":42,"tag":229,"props":10166,"children":10167},{"style":1260},[10168],{"type":48,"value":1361},{"type":42,"tag":229,"props":10170,"children":10171},{"style":1364},[10172],{"type":48,"value":9674},{"type":42,"tag":229,"props":10174,"children":10175},{"style":1260},[10176],{"type":48,"value":1371},{"type":42,"tag":229,"props":10178,"children":10179},{"style":1266},[10180],{"type":48,"value":3171},{"type":42,"tag":229,"props":10182,"children":10183},{"style":1260},[10184],{"type":48,"value":1322},{"type":42,"tag":51,"props":10186,"children":10187},{},[10188,10190,10196,10198,10204,10206,10212],{"type":48,"value":10189},"(The old workaround — wrapping the string in a one-element slice and iterating it with ",{"type":42,"tag":65,"props":10191,"children":10193},{"className":10192},[],[10194],{"type":48,"value":10195},"\u003Cfor each=\"summary\" as=\"s\">",{"type":48,"value":10197}," — is no longer needed; bind the fn directly. Item methods take the arena too: ",{"type":42,"tag":65,"props":10199,"children":10201},{"className":10200},[],[10202],{"type":48,"value":10203},"{e.amount}",{"type":48,"value":10205}," may call ",{"type":42,"tag":65,"props":10207,"children":10209},{"className":10208},[],[10210],{"type":48,"value":10211},"pub fn amount(e: *const Expense, arena: std.mem.Allocator) []const u8",{"type":48,"value":10213},".)",{"type":42,"tag":51,"props":10215,"children":10216},{},[10217,10219,10225,10227,10233,10234,10240,10242,10248],{"type":48,"value":10218},"For ",{"type":42,"tag":65,"props":10220,"children":10222},{"className":10221},[],[10223],{"type":48,"value":10224},"\u003Cif test>",{"type":48,"value":10226},", prefer an explicit boolean predicate method over numeric truthiness: ",{"type":42,"tag":65,"props":10228,"children":10230},{"className":10229},[],[10231],{"type":48,"value":10232},"test=\"{hasHabits}\"",{"type":48,"value":157},{"type":42,"tag":65,"props":10235,"children":10237},{"className":10236},[],[10238],{"type":48,"value":10239},"pub fn hasHabits(m: *const Model) bool",{"type":48,"value":10241}," states the condition; ",{"type":42,"tag":65,"props":10243,"children":10245},{"className":10244},[],[10246],{"type":48,"value":10247},"test=\"{habit_count}\"",{"type":48,"value":10249}," works (non-zero is truthy, non-empty strings too) but hides it.",{"type":42,"tag":212,"props":10251,"children":10253},{"id":10252},"messages",[10254],{"type":48,"value":10255},"Messages",{"type":42,"tag":51,"props":10257,"children":10258},{},[10259,10264,10265,10270,10271,10276,10277,10282,10284,10289,10291,10296,10298,10304,10305,10311,10313,10318,10319,10325,10327,10332,10334,10340,10342,10347,10349,10354,10356,10362,10364,10369,10371,10376,10378,10383,10384,10390,10392,10398,10399,10405,10406,10412,10413,10419,10421,10427,10428,10434,10435,10441,10442,10447,10448,10453,10454,10459,10461,10466],{"type":42,"tag":65,"props":10260,"children":10262},{"className":10261},[],[10263],{"type":48,"value":1176},{"type":48,"value":1820},{"type":42,"tag":65,"props":10266,"children":10268},{"className":10267},[],[10269],{"type":48,"value":2277},{"type":48,"value":1820},{"type":42,"tag":65,"props":10272,"children":10274},{"className":10273},[],[10275],{"type":48,"value":3043},{"type":48,"value":1820},{"type":42,"tag":65,"props":10278,"children":10280},{"className":10279},[],[10281],{"type":48,"value":3129},{"type":48,"value":10283}," (enter in a text field; primary+enter in a textarea, where enter inserts a newline), ",{"type":42,"tag":65,"props":10285,"children":10287},{"className":10286},[],[10288],{"type":48,"value":2235},{"type":48,"value":10290}," (dismissible surfaces: dialog, drawer, sheet, dropdown-menu — dispatched when Escape or a click outside dismisses the surface, so the model owns the close), ",{"type":42,"tag":65,"props":10292,"children":10294},{"className":10293},[],[10295],{"type":48,"value":1183},{"type":48,"value":10297}," (press-and-hold, see the Pickers section), and ",{"type":42,"tag":65,"props":10299,"children":10301},{"className":10300},[],[10302],{"type":48,"value":10303},"on-hover-enter",{"type":48,"value":456},{"type":42,"tag":65,"props":10306,"children":10308},{"className":10307},[],[10309],{"type":48,"value":10310},"on-hover-leave",{"type":48,"value":10312}," (the pointer-hover containment pair, below) take ",{"type":42,"tag":65,"props":10314,"children":10316},{"className":10315},[],[10317],{"type":48,"value":15},{"type":48,"value":3426},{"type":42,"tag":65,"props":10320,"children":10322},{"className":10321},[],[10323],{"type":48,"value":10324},"tag:{payload}",{"type":48,"value":10326},". The tag must be a variant of your ",{"type":42,"tag":65,"props":10328,"children":10330},{"className":10329},[],[10331],{"type":48,"value":97},{"type":48,"value":10333}," union; payload bindings coerce to the variant's payload type: integers, floats, enums (from tag names), ",{"type":42,"tag":65,"props":10335,"children":10337},{"className":10336},[],[10338],{"type":48,"value":10339},"[]const u8",{"type":48,"value":10341},", bool. ",{"type":42,"tag":65,"props":10343,"children":10345},{"className":10344},[],[10346],{"type":48,"value":3121},{"type":48,"value":10348}," is special: name a ",{"type":42,"tag":65,"props":10350,"children":10352},{"className":10351},[],[10353],{"type":48,"value":97},{"type":48,"value":10355}," variant whose payload is ",{"type":42,"tag":65,"props":10357,"children":10359},{"className":10358},[],[10360],{"type":48,"value":10361},"canvas.TextInputEvent",{"type":48,"value":10363}," and the runtime delivers each text edit in it. ",{"type":42,"tag":65,"props":10365,"children":10367},{"className":10366},[],[10368],{"type":48,"value":7873},{"type":48,"value":10370}," (the ",{"type":42,"tag":65,"props":10372,"children":10374},{"className":10373},[],[10375],{"type":48,"value":1008},{"type":48,"value":10377}," element only) is the same shape: name a ",{"type":42,"tag":65,"props":10379,"children":10381},{"className":10380},[],[10382],{"type":48,"value":97},{"type":48,"value":10355},{"type":42,"tag":65,"props":10385,"children":10387},{"className":10386},[],[10388],{"type":48,"value":10389},"canvas.ScrollState",{"type":48,"value":10391}," and the runtime delivers the post-scroll state — ",{"type":42,"tag":65,"props":10393,"children":10395},{"className":10394},[],[10396],{"type":48,"value":10397},"offset",{"type":48,"value":1820},{"type":42,"tag":65,"props":10400,"children":10402},{"className":10401},[],[10403],{"type":48,"value":10404},"viewport_extent",{"type":48,"value":1820},{"type":42,"tag":65,"props":10407,"children":10409},{"className":10408},[],[10410],{"type":48,"value":10411},"content_extent",{"type":48,"value":1820},{"type":42,"tag":65,"props":10414,"children":10416},{"className":10415},[],[10417],{"type":48,"value":10418},"maxOffset()",{"type":48,"value":10420}," — after every user scroll (wheel, kinetic momentum steps, keyboard, accessibility). In Zig views the constructors are ",{"type":42,"tag":65,"props":10422,"children":10424},{"className":10423},[],[10425],{"type":48,"value":10426},"Ui.inputMsg(.tag)",{"type":48,"value":7747},{"type":42,"tag":65,"props":10429,"children":10431},{"className":10430},[],[10432],{"type":48,"value":10433},"Ui.scrollMsg(.tag)",{"type":48,"value":7831},{"type":42,"tag":65,"props":10436,"children":10438},{"className":10437},[],[10439],{"type":48,"value":10440},"on_input",{"type":48,"value":7747},{"type":42,"tag":65,"props":10443,"children":10445},{"className":10444},[],[10446],{"type":48,"value":8086},{"type":48,"value":8575},{"type":42,"tag":65,"props":10449,"children":10451},{"className":10450},[],[10452],{"type":48,"value":8133},{"type":48,"value":10370},{"type":42,"tag":65,"props":10455,"children":10457},{"className":10456},[],[10458],{"type":48,"value":1008},{"type":48,"value":10460}," element only; ",{"type":42,"tag":65,"props":10462,"children":10464},{"className":10463},[],[10465],{"type":48,"value":8123},{"type":48,"value":10467}," in Zig views, any scroll container including the windowed virtual list) is a plain Msg dispatched when a user scroll comes within one viewport of the content end — the infinite-fetch signal, fired once per approach with hysteresis (re-arms past 1.5 viewports, which appending a batch causes by growing the extent). A programmatic jump to the end fires once and NEVER re-arms while the offset stays near the end — re-arming needs a post-scroll observation at least 1.5 viewports from it.",{"type":42,"tag":51,"props":10469,"children":10470},{},[10471,10473,10478,10480,10485,10487,10492,10494,10500,10502,10507],{"type":48,"value":10472},"Scroll offsets follow the same mirror discipline as text: the Msg carries the offset the runtime ALREADY applied, so store it in the model and echo it back through the scroll's ",{"type":42,"tag":65,"props":10474,"children":10476},{"className":10475},[],[10477],{"type":48,"value":2479},{"type":48,"value":10479}," — the echoed source value equals the runtime offset, which the scroll reconcile rule treats as \"unchanged\", so rebuilds never stomp live scrolling. ",{"type":42,"tag":65,"props":10481,"children":10483},{"className":10482},[],[10484],{"type":48,"value":7873},{"type":48,"value":10486}," is how long content pages or lazy-loads: keep a bounded window in the model and slide it from ",{"type":42,"tag":65,"props":10488,"children":10490},{"className":10489},[],[10491],{"type":48,"value":10397},{"type":48,"value":10493}," (near-end when ",{"type":42,"tag":65,"props":10495,"children":10497},{"className":10496},[],[10498],{"type":48,"value":10499},"offset + viewport_extent",{"type":48,"value":10501}," approaches ",{"type":42,"tag":65,"props":10503,"children":10505},{"className":10504},[],[10506],{"type":48,"value":10411},{"type":48,"value":7386},{"type":42,"tag":51,"props":10509,"children":10510},{},[10511,10516,10517,10522,10524,10530,10531,10537,10539,10545,10547,10552],{"type":42,"tag":65,"props":10512,"children":10514},{"className":10513},[],[10515],{"type":48,"value":10303},{"type":48,"value":2555},{"type":42,"tag":65,"props":10518,"children":10520},{"className":10519},[],[10521],{"type":48,"value":10310},{"type":48,"value":10523}," (any element; ",{"type":42,"tag":65,"props":10525,"children":10527},{"className":10526},[],[10528],{"type":48,"value":10529},"ElementOptions.on_hover_enter",{"type":48,"value":7747},{"type":42,"tag":65,"props":10532,"children":10534},{"className":10533},[],[10535],{"type":48,"value":10536},"on_hover_leave",{"type":48,"value":10538}," in Zig views) are the pointer-hover containment pair — Elm's onMouseEnter\u002FonMouseLeave: enter dispatches once when the pointer enters the element's hit region, leave once when it exits — discrete edges, never per-move, so hover previews, prefetch, and hover cards are ordinary Msgs. Binding either makes the element hover-hittable the way a bound press makes it pressable — but never pressable: clicks fall through, no accessibility action, and NO hover wash (the wash is the visual channel of acting controls; a ",{"type":42,"tag":65,"props":10540,"children":10542},{"className":10541},[],[10543],{"type":48,"value":10544},"quiet_hover",{"type":48,"value":10546}," content tile that binds hover stays visually quiet while the model hears it). Nested bound elements track containment independently (entering a bound row inside a bound card never leaves the card); enters fire outermost-first, leaves innermost-first. Every enter is answered by exactly one eventual leave — the leave Msg is captured while the element stands (kept fresh across rebuilds, retained if unbound), so it still arrives when the exit is the element unmounting. Exits resolve exactly like the hover wash: moving off and the pointer leaving the window are direct edges; content scrolling or reflowing out from under a stationary pointer re-hit-tests the last pointer position; a dismissal removing the surface under the pointer delivers that surface's leaves immediately, and whatever it reveals is entered when the model's close rebuild re-hit-tests (pair dismissible surfaces with ",{"type":42,"tag":65,"props":10548,"children":10550},{"className":10549},[],[10551],{"type":48,"value":2235},{"type":48,"value":10553},", as always); overlays occlude hover the way they occlude clicks. Mouse\u002Ftrackpad only — containment advances on hover-phase motion, a pointer floating without contact that touch physically cannot produce, so touch never synthesizes hover and hover-revealed affordances need a second path.",{"type":42,"tag":51,"props":10555,"children":10556},{},[10557,10559,10565,10567,10573,10575,10581,10583,10589,10591,10597],{"type":48,"value":10558},"A handler or update error DEGRADES, it does not exit the app: dispatch catches it, records it in a bounded ring (",{"type":42,"tag":65,"props":10560,"children":10562},{"className":10561},[],[10563],{"type":48,"value":10564},"runtime.dispatchErrors()",{"type":48,"value":10566},", the ",{"type":42,"tag":65,"props":10568,"children":10570},{"className":10569},[],[10571],{"type":48,"value":10572},"error event=... name=...",{"type":48,"value":10574}," lines and ",{"type":42,"tag":65,"props":10576,"children":10578},{"className":10577},[],[10579],{"type":48,"value":10580},"dispatch_errors=",{"type":48,"value":10582}," count in automation snapshots, and a ",{"type":42,"tag":65,"props":10584,"children":10586},{"className":10585},[],[10587],{"type":48,"value":10588},"dispatch.error",{"type":48,"value":10590}," trace record at error level), and the app keeps running. Trace-sink capacity failures likewise never fail dispatch — dropped records are counted (",{"type":42,"tag":65,"props":10592,"children":10594},{"className":10593},[],[10595],{"type":48,"value":10596},"dropped_trace_records=",{"type":48,"value":10598},"), not fatal. Design for it: an arm that can fail should still surface its own status in the model; the error ring is the safety net, not the UX.",{"type":42,"tag":51,"props":10600,"children":10601},{},[10602,10604,10609,10610,10615,10617,10623,10624,10630,10631,10637,10639,10644,10645,10650,10651,10656,10658,10664],{"type":48,"value":10603},"Presses follow ONE rule: a click lands on the nearest pressable widget under the pointer — plain text, icons, images, badges, and layout containers let it fall through to their closest pressable ancestor, and dragging still selects text. \"Pressable\" means an interactive kind (button, checkbox, list-item, ...) or ANY element with a bound ",{"type":42,"tag":65,"props":10605,"children":10607},{"className":10606},[],[10608],{"type":48,"value":1176},{"type":48,"value":456},{"type":42,"tag":65,"props":10611,"children":10613},{"className":10612},[],[10614],{"type":48,"value":2277},{"type":48,"value":10616}," — the handler itself makes the element a hit target, so a pressable row is just ",{"type":42,"tag":65,"props":10618,"children":10620},{"className":10619},[],[10621],{"type":48,"value":10622},"\u003Cpanel on-press=\"open:{id}\">",{"type":48,"value":6260},{"type":42,"tag":65,"props":10625,"children":10627},{"className":10626},[],[10628],{"type":48,"value":10629},"\u003Crow on-press=...>",{"type":48,"value":1820},{"type":42,"tag":65,"props":10632,"children":10634},{"className":10633},[],[10635],{"type":48,"value":10636},"ui.row(.{ .on_press = ... })",{"type":48,"value":10638},") with plain text children: no empty-text overlays, no duplicating the handler onto every text leaf. Nested pressables resolve to the deepest one (a button inside a pressable row wins over the row); editable text fields, scroll containers, and modal surfaces (dialog, drawer, sheet, popover, menus) always claim their own presses, so a click in a field inside a pressable row places the caret instead of activating the row. The value\u002Ftext handlers (",{"type":42,"tag":65,"props":10640,"children":10642},{"className":10641},[],[10643],{"type":48,"value":3043},{"type":48,"value":1820},{"type":42,"tag":65,"props":10646,"children":10648},{"className":10647},[],[10649],{"type":48,"value":3129},{"type":48,"value":1820},{"type":42,"tag":65,"props":10652,"children":10654},{"className":10653},[],[10655],{"type":48,"value":3121},{"type":48,"value":10657},") still only belong on controls — both engines and ",{"type":42,"tag":65,"props":10659,"children":10661},{"className":10660},[],[10662],{"type":48,"value":10663},"markup check",{"type":48,"value":10665}," reject them on layout\u002Fdecoration elements with a teaching error.",{"type":42,"tag":612,"props":10667,"children":10669},{"id":10668},"keys-quiet-list-rows-and-the-app-level-fallback",[10670],{"type":48,"value":10671},"Keys: quiet list rows and the app-level fallback",{"type":42,"tag":51,"props":10673,"children":10674},{},[10675,10677,10683],{"type":48,"value":10676},"Keyboard focus has two registers. RING focus is the keyboard contract: Tab\u002FShift+Tab walk the focusables and draw the visible ring, and a ring-focused widget owns its keys in full — activation, group arrows, Home\u002FEnd. QUIET focus is bookkeeping: a pointer press records which widget the user last touched, with no ring drawn (editable text kinds are the exception — a caret is a visible promise, so they show it however focus arrives). A key event resolves top to bottom, the focused widget always outranking the app: (1) the focused widget's bound handler; (2) structural consume — any key on an editable text kind (typing stays typing, checked by widget KIND, so a focused search field blocks app shortcuts without knowing they exist) and any key the widget's kind maps as a control intent; (3) only an unclaimed key_down reaches ",{"type":42,"tag":65,"props":10678,"children":10680},{"className":10679},[],[10681],{"type":48,"value":10682},"Options.on_key",{"type":48,"value":10684},", the app-level fallback (a target-less event — nothing focused — skips straight there). The fallback is a plain function from the key event to an optional Msg:",{"type":42,"tag":219,"props":10686,"children":10688},{"className":221,"code":10687,"language":18,"meta":223,"style":223},"\u002F\u002F options: .on_key = onKey,\npub fn onKey(keyboard: canvas.WidgetKeyboardEvent) ?Msg {\n    if (keyboard.modifiers.hasNavigationModifier() or keyboard.modifiers.shift) return null;\n    if (std.ascii.eqlIgnoreCase(keyboard.key, \"space\")) return .toggle_play;\n    if (std.ascii.eqlIgnoreCase(keyboard.key, \"arrowdown\")) return .select_next;\n    if (std.ascii.eqlIgnoreCase(keyboard.key, \"arrowup\")) return .select_previous;\n    return null;\n}\n",[10689],{"type":42,"tag":65,"props":10690,"children":10691},{"__ignoreMap":223},[10692,10700,10708,10716,10724,10732,10740,10748],{"type":42,"tag":229,"props":10693,"children":10694},{"class":231,"line":232},[10695],{"type":42,"tag":229,"props":10696,"children":10697},{},[10698],{"type":48,"value":10699},"\u002F\u002F options: .on_key = onKey,\n",{"type":42,"tag":229,"props":10701,"children":10702},{"class":231,"line":241},[10703],{"type":42,"tag":229,"props":10704,"children":10705},{},[10706],{"type":48,"value":10707},"pub fn onKey(keyboard: canvas.WidgetKeyboardEvent) ?Msg {\n",{"type":42,"tag":229,"props":10709,"children":10710},{"class":231,"line":251},[10711],{"type":42,"tag":229,"props":10712,"children":10713},{},[10714],{"type":48,"value":10715},"    if (keyboard.modifiers.hasNavigationModifier() or keyboard.modifiers.shift) return null;\n",{"type":42,"tag":229,"props":10717,"children":10718},{"class":231,"line":260},[10719],{"type":42,"tag":229,"props":10720,"children":10721},{},[10722],{"type":48,"value":10723},"    if (std.ascii.eqlIgnoreCase(keyboard.key, \"space\")) return .toggle_play;\n",{"type":42,"tag":229,"props":10725,"children":10726},{"class":231,"line":269},[10727],{"type":42,"tag":229,"props":10728,"children":10729},{},[10730],{"type":48,"value":10731},"    if (std.ascii.eqlIgnoreCase(keyboard.key, \"arrowdown\")) return .select_next;\n",{"type":42,"tag":229,"props":10733,"children":10734},{"class":231,"line":278},[10735],{"type":42,"tag":229,"props":10736,"children":10737},{},[10738],{"type":48,"value":10739},"    if (std.ascii.eqlIgnoreCase(keyboard.key, \"arrowup\")) return .select_previous;\n",{"type":42,"tag":229,"props":10741,"children":10742},{"class":231,"line":287},[10743],{"type":42,"tag":229,"props":10744,"children":10745},{},[10746],{"type":48,"value":10747},"    return null;\n",{"type":42,"tag":229,"props":10749,"children":10750},{"class":231,"line":296},[10751],{"type":42,"tag":229,"props":10752,"children":10753},{},[10754],{"type":48,"value":428},{"type":42,"tag":51,"props":10756,"children":10757},{},[10758,10760,10765,10767,10773,10774,10780,10782,10787,10789,10794],{"type":48,"value":10759},"One rule bridges the registers: quiet focus on a plain ",{"type":42,"tag":65,"props":10761,"children":10763},{"className":10762},[],[10764],{"type":48,"value":1269},{"type":48,"value":10766}," routes NO keys — arrows, Enter, and Space on a row the user merely clicked all fall through to the fallback exactly as if nothing were focused. The why is selection-follows-intent apps: in a music library the selection is MODEL state — a click selects a row, and the arrows should keep moving that app-owned selection through the fallback (",{"type":42,"tag":65,"props":10768,"children":10770},{"className":10769},[],[10771],{"type":48,"value":10772},"select_next",{"type":48,"value":456},{"type":42,"tag":65,"props":10775,"children":10777},{"className":10776},[],[10778],{"type":48,"value":10779},"select_previous",{"type":48,"value":10781}," above), not silently start walking engine focus from whichever row was clicked last. Tab restores the full row contract (arrows walk the rows, Space selects, Enter submits — see \"Select, then act\"), rows carrying ",{"type":42,"tag":65,"props":10783,"children":10785},{"className":10784},[],[10786],{"type":48,"value":2538},{"type":48,"value":10788}," keep the tree keymap under either register, and every other widget kind keeps its keys under quiet focus (a quietly focused button still presses on Space). The fallback fn is directly unit-testable (feed it key events, assert the Msgs), and ",{"type":42,"tag":65,"props":10790,"children":10792},{"className":10791},[],[10793],{"type":48,"value":7647},{"type":48,"value":10795}," pins the whole pattern: Space toggles the transport from anywhere, a tabbed-to row takes Space for select and Enter for play, and the search field takes every key as typing.",{"type":42,"tag":212,"props":10797,"children":10799},{"id":10798},"text-fields-the-elm-style-mirror-pattern",[10800],{"type":48,"value":10801},"Text fields: the elm-style mirror pattern",{"type":42,"tag":51,"props":10803,"children":10804},{},[10805],{"type":48,"value":10806},"The model applies every edit event and is the source of truth; the runtime keeps caret\u002Fselection while your source text matches, and a source-side change (like clearing on submit) wins:",{"type":42,"tag":219,"props":10808,"children":10810},{"className":1248,"code":10809,"language":1250,"meta":223,"style":223},"\u003Ctext-field text=\"{draft}\" placeholder=\"New task…\" on-input=\"draft_edit\" on-submit=\"add\" grow=\"1\" \u002F>\n",[10811],{"type":42,"tag":65,"props":10812,"children":10813},{"__ignoreMap":223},[10814],{"type":42,"tag":229,"props":10815,"children":10816},{"class":231,"line":232},[10817,10821,10825,10829,10833,10837,10842,10846,10850,10854,10858,10863,10867,10872,10876,10880,10885,10889,10894,10898,10902,10907,10911,10915,10919,10923,10927,10931],{"type":42,"tag":229,"props":10818,"children":10819},{"style":1260},[10820],{"type":48,"value":1263},{"type":42,"tag":229,"props":10822,"children":10823},{"style":1266},[10824],{"type":48,"value":3072},{"type":42,"tag":229,"props":10826,"children":10827},{"style":1272},[10828],{"type":48,"value":5685},{"type":42,"tag":229,"props":10830,"children":10831},{"style":1260},[10832],{"type":48,"value":1280},{"type":42,"tag":229,"props":10834,"children":10835},{"style":1260},[10836],{"type":48,"value":1285},{"type":42,"tag":229,"props":10838,"children":10839},{"style":1288},[10840],{"type":48,"value":10841},"{draft}",{"type":42,"tag":229,"props":10843,"children":10844},{"style":1260},[10845],{"type":48,"value":1285},{"type":42,"tag":229,"props":10847,"children":10848},{"style":1272},[10849],{"type":48,"value":5663},{"type":42,"tag":229,"props":10851,"children":10852},{"style":1260},[10853],{"type":48,"value":1280},{"type":42,"tag":229,"props":10855,"children":10856},{"style":1260},[10857],{"type":48,"value":1285},{"type":42,"tag":229,"props":10859,"children":10860},{"style":1288},[10861],{"type":48,"value":10862},"New task…",{"type":42,"tag":229,"props":10864,"children":10865},{"style":1260},[10866],{"type":48,"value":1285},{"type":42,"tag":229,"props":10868,"children":10869},{"style":1272},[10870],{"type":48,"value":10871}," on-input",{"type":42,"tag":229,"props":10873,"children":10874},{"style":1260},[10875],{"type":48,"value":1280},{"type":42,"tag":229,"props":10877,"children":10878},{"style":1260},[10879],{"type":48,"value":1285},{"type":42,"tag":229,"props":10881,"children":10882},{"style":1288},[10883],{"type":48,"value":10884},"draft_edit",{"type":42,"tag":229,"props":10886,"children":10887},{"style":1260},[10888],{"type":48,"value":1285},{"type":42,"tag":229,"props":10890,"children":10891},{"style":1272},[10892],{"type":48,"value":10893}," on-submit",{"type":42,"tag":229,"props":10895,"children":10896},{"style":1260},[10897],{"type":48,"value":1280},{"type":42,"tag":229,"props":10899,"children":10900},{"style":1260},[10901],{"type":48,"value":1285},{"type":42,"tag":229,"props":10903,"children":10904},{"style":1288},[10905],{"type":48,"value":10906},"add",{"type":42,"tag":229,"props":10908,"children":10909},{"style":1260},[10910],{"type":48,"value":1285},{"type":42,"tag":229,"props":10912,"children":10913},{"style":1272},[10914],{"type":48,"value":1339},{"type":42,"tag":229,"props":10916,"children":10917},{"style":1260},[10918],{"type":48,"value":1280},{"type":42,"tag":229,"props":10920,"children":10921},{"style":1260},[10922],{"type":48,"value":1285},{"type":42,"tag":229,"props":10924,"children":10925},{"style":1288},[10926],{"type":48,"value":1352},{"type":42,"tag":229,"props":10928,"children":10929},{"style":1260},[10930],{"type":48,"value":1285},{"type":42,"tag":229,"props":10932,"children":10933},{"style":1260},[10934],{"type":48,"value":10935}," \u002F>\n",{"type":42,"tag":219,"props":10937,"children":10939},{"className":221,"code":10938,"language":18,"meta":223,"style":223},"draft_buffer: canvas.TextBuffer(64) = .{},            \u002F\u002F model field: text + selection + composition\npub fn draft(model: *const Model) []const u8 {        \u002F\u002F the fn the markup's text= binds\n    return model.draft_buffer.text();\n}\n.draft_edit => |edit| model.draft_buffer.apply(edit), \u002F\u002F mirror every edit\n.add => { model.addTask(model.draft_buffer.text()); model.draft_buffer.clear(); },  \u002F\u002F clearing the source clears the field\n",[10940],{"type":42,"tag":65,"props":10941,"children":10942},{"__ignoreMap":223},[10943,10951,10959,10967,10974,10982],{"type":42,"tag":229,"props":10944,"children":10945},{"class":231,"line":232},[10946],{"type":42,"tag":229,"props":10947,"children":10948},{},[10949],{"type":48,"value":10950},"draft_buffer: canvas.TextBuffer(64) = .{},            \u002F\u002F model field: text + selection + composition\n",{"type":42,"tag":229,"props":10952,"children":10953},{"class":231,"line":241},[10954],{"type":42,"tag":229,"props":10955,"children":10956},{},[10957],{"type":48,"value":10958},"pub fn draft(model: *const Model) []const u8 {        \u002F\u002F the fn the markup's text= binds\n",{"type":42,"tag":229,"props":10960,"children":10961},{"class":231,"line":251},[10962],{"type":42,"tag":229,"props":10963,"children":10964},{},[10965],{"type":48,"value":10966},"    return model.draft_buffer.text();\n",{"type":42,"tag":229,"props":10968,"children":10969},{"class":231,"line":260},[10970],{"type":42,"tag":229,"props":10971,"children":10972},{},[10973],{"type":48,"value":428},{"type":42,"tag":229,"props":10975,"children":10976},{"class":231,"line":269},[10977],{"type":42,"tag":229,"props":10978,"children":10979},{},[10980],{"type":48,"value":10981},".draft_edit => |edit| model.draft_buffer.apply(edit), \u002F\u002F mirror every edit\n",{"type":42,"tag":229,"props":10983,"children":10984},{"class":231,"line":278},[10985],{"type":42,"tag":229,"props":10986,"children":10987},{},[10988],{"type":48,"value":10989},".add => { model.addTask(model.draft_buffer.text()); model.draft_buffer.clear(); },  \u002F\u002F clearing the source clears the field\n",{"type":42,"tag":51,"props":10991,"children":10992},{},[10993,10995,11001,11003,11009,11011,11017],{"type":48,"value":10994},"The markup binds the FN (",{"type":42,"tag":65,"props":10996,"children":10998},{"className":10997},[],[10999],{"type":48,"value":11000},"text=\"{draft}\"",{"type":48,"value":11002},"), never the buffer: binding a ",{"type":42,"tag":65,"props":11004,"children":11006},{"className":11005},[],[11007],{"type":48,"value":11008},"TextBuffer",{"type":48,"value":11010}," model FIELD directly is a teaching error pointing at this pattern — the buffer holds editor state, and the binding wants the text it derives. See ",{"type":42,"tag":65,"props":11012,"children":11014},{"className":11013},[],[11015],{"type":48,"value":11016},"examples\u002Fui-inbox",{"type":48,"value":11018}," for the complete pattern.",{"type":42,"tag":612,"props":11020,"children":11022},{"id":11021},"clipboard-and-selection-free-do-not-reimplement",[11023],{"type":48,"value":11024},"Clipboard and selection (free — do not reimplement)",{"type":42,"tag":51,"props":11026,"children":11027},{},[11028,11030,11035,11037,11043,11045,11051,11053,11059,11061,11066,11068,11074,11076,11081],{"type":48,"value":11029},"The runtime owns cmd\u002Fctrl+C\u002FX\u002FV in editable text: copy writes the current selection to the system clipboard, cut copies then delivers the removal to your ",{"type":42,"tag":65,"props":11031,"children":11033},{"className":11032},[],[11034],{"type":48,"value":3121},{"type":48,"value":11036}," handler as an ",{"type":42,"tag":65,"props":11038,"children":11040},{"className":11039},[],[11041],{"type":48,"value":11042},"insert_text \"\"",{"type":48,"value":11044}," edit, and paste arrives as an ordinary ",{"type":42,"tag":65,"props":11046,"children":11048},{"className":11047},[],[11049],{"type":48,"value":11050},"insert_text",{"type":48,"value":11052}," edit — the TEA mirror above stays consistent with zero extra code. Paste is clamped to the view's text capacity: when bytes were dropped, the keyboard event carries ",{"type":42,"tag":65,"props":11054,"children":11056},{"className":11055},[],[11057],{"type":48,"value":11058},"edit_truncated = true",{"type":48,"value":11060}," and your ",{"type":42,"tag":65,"props":11062,"children":11064},{"className":11063},[],[11065],{"type":48,"value":11008},{"type":48,"value":11067}," mirror sets its own ",{"type":42,"tag":65,"props":11069,"children":11071},{"className":11070},[],[11072],{"type":48,"value":11073},"truncated",{"type":48,"value":11075}," flag (check it if lost paste bytes matter to your UX; ",{"type":42,"tag":65,"props":11077,"children":11079},{"className":11078},[],[11080],{"type":48,"value":11008},{"type":48,"value":11082}," clamps oversized insertions at a UTF-8 boundary rather than dropping the edit). Shift+arrows\u002Fhome\u002Fend extend the selection from the keyboard.",{"type":42,"tag":51,"props":11084,"children":11085},{},[11086,11088,11093,11095,11101,11103,11108,11110,11116,11118,11123,11124,11130,11131,11137,11139,11145,11147,11153,11154,11160],{"type":48,"value":11087},"Static text is selectable too: click-drag inside one ",{"type":42,"tag":65,"props":11089,"children":11091},{"className":11090},[],[11092],{"type":48,"value":48},{"type":48,"value":11094}," leaf or ",{"type":42,"tag":65,"props":11096,"children":11098},{"className":11097},[],[11099],{"type":48,"value":11100},"paragraph",{"type":48,"value":11102}," (markdown bodies included) selects with a highlight, cmd\u002Fctrl+C copies it, and pressing anywhere else clears it. Selection and pressing coexist inside pressable rows — dragging selects (and presses nothing), a plain click collapses the selection and lands on the row's ",{"type":42,"tag":65,"props":11104,"children":11106},{"className":11105},[],[11107],{"type":48,"value":1176},{"type":48,"value":11109},". Selection is per-widget by design — there is no document model ordering text across widgets, so a drag cannot span two paragraphs (copy per paragraph). The selection survives rebuilds while that widget's text bytes are unchanged, and shows up in semantics\u002Fautomation snapshots as ",{"type":42,"tag":65,"props":11111,"children":11113},{"className":11112},[],[11114],{"type":48,"value":11115},"selection=a..b",{"type":48,"value":11117}," on the widget line. Clipboard access from ",{"type":42,"tag":65,"props":11119,"children":11121},{"className":11120},[],[11122],{"type":48,"value":483},{"type":48,"value":3747},{"type":42,"tag":65,"props":11125,"children":11127},{"className":11126},[],[11128],{"type":48,"value":11129},"fx.writeClipboard",{"type":48,"value":7747},{"type":42,"tag":65,"props":11132,"children":11134},{"className":11133},[],[11135],{"type":48,"value":11136},"fx.readClipboard",{"type":48,"value":11138}," on the effects channel (see Effects) — never a ",{"type":42,"tag":65,"props":11140,"children":11142},{"className":11141},[],[11143],{"type":48,"value":11144},"pbcopy",{"type":48,"value":11146}," spawn; ",{"type":42,"tag":65,"props":11148,"children":11150},{"className":11149},[],[11151],{"type":48,"value":11152},"runtime.readClipboard(&buffer)",{"type":48,"value":7747},{"type":42,"tag":65,"props":11155,"children":11157},{"className":11156},[],[11158],{"type":48,"value":11159},"runtime.writeClipboard(text)",{"type":48,"value":11161}," remain for code that holds the runtime.",{"type":42,"tag":212,"props":11163,"children":11165},{"id":11164},"effects-subprocesses-and-http-from-update",[11166],{"type":48,"value":11167},"Effects: subprocesses and HTTP from update",{"type":42,"tag":51,"props":11169,"children":11170},{},[11171,11176,11178,11184,11186,11192],{"type":42,"tag":65,"props":11172,"children":11174},{"className":11173},[],[11175],{"type":48,"value":483},{"type":48,"value":11177}," can take a third parameter — the effects channel — by declaring ",{"type":42,"tag":65,"props":11179,"children":11181},{"className":11180},[],[11182],{"type":48,"value":11183},".update_fx",{"type":48,"value":11185}," instead of ",{"type":42,"tag":65,"props":11187,"children":11189},{"className":11188},[],[11190],{"type":48,"value":11191},".update",{"type":48,"value":11193}," (existing two-argument apps are untouched; set exactly one):",{"type":42,"tag":219,"props":11195,"children":11197},{"className":221,"code":11196,"language":18,"meta":223,"style":223},"const App = native_sdk.UiApp(Model, Msg);\nconst Effects = App.Effects;\n\npub fn update(model: *Model, msg: Msg, fx: *Effects) void { ... }\n\u002F\u002F options: .update_fx = update,\n",[11198],{"type":42,"tag":65,"props":11199,"children":11200},{"__ignoreMap":223},[11201,11209,11217,11224,11232],{"type":42,"tag":229,"props":11202,"children":11203},{"class":231,"line":232},[11204],{"type":42,"tag":229,"props":11205,"children":11206},{},[11207],{"type":48,"value":11208},"const App = native_sdk.UiApp(Model, Msg);\n",{"type":42,"tag":229,"props":11210,"children":11211},{"class":231,"line":241},[11212],{"type":42,"tag":229,"props":11213,"children":11214},{},[11215],{"type":48,"value":11216},"const Effects = App.Effects;\n",{"type":42,"tag":229,"props":11218,"children":11219},{"class":231,"line":251},[11220],{"type":42,"tag":229,"props":11221,"children":11222},{"emptyLinePlaceholder":245},[11223],{"type":48,"value":248},{"type":42,"tag":229,"props":11225,"children":11226},{"class":231,"line":260},[11227],{"type":42,"tag":229,"props":11228,"children":11229},{},[11230],{"type":48,"value":11231},"pub fn update(model: *Model, msg: Msg, fx: *Effects) void { ... }\n",{"type":42,"tag":229,"props":11233,"children":11234},{"class":231,"line":269},[11235],{"type":42,"tag":229,"props":11236,"children":11237},{},[11238],{"type":48,"value":11239},"\u002F\u002F options: .update_fx = update,\n",{"type":42,"tag":51,"props":11241,"children":11242},{},[11243,11245,11251,11253,11259,11260,11265],{"type":48,"value":11244},"Boot-time effects — fetching the data the app opens with — go in ",{"type":42,"tag":65,"props":11246,"children":11248},{"className":11247},[],[11249],{"type":48,"value":11250},".init_fx",{"type":48,"value":11252},", TEA's init command. It runs exactly once, on the installing frame, before the first view build, so a loading flag set there is in the very first paint; results arrive as ordinary Msgs. This is THE way to boot-fetch — never a guarded ",{"type":42,"tag":65,"props":11254,"children":11256},{"className":11255},[],[11257],{"type":48,"value":11258},"on_frame",{"type":48,"value":2620},{"type":42,"tag":65,"props":11261,"children":11263},{"className":11262},[],[11264],{"type":48,"value":11258},{"type":48,"value":11266}," is the per-frame hook for renderer diagnostics and presented-frame reactions, and unguarded spawning from it refires forever):",{"type":42,"tag":219,"props":11268,"children":11270},{"className":221,"code":11269,"language":18,"meta":223,"style":223},"fn boot(model: *Model, fx: *Effects) void {\n    model.loading = true;\n    fx.spawn(.{\n        .key = issues_key,\n        .argv = &.{ \"gh\", \"issue\", \"list\", \"--json\", \"number,title\" },\n        .output = .collect,                        \u002F\u002F whole JSON on the exit Msg\n        .on_exit = Effects.exitMsg(.issues_loaded),\n    });\n}\n\u002F\u002F options: .init_fx = boot,   (works with either update form)\n",[11271],{"type":42,"tag":65,"props":11272,"children":11273},{"__ignoreMap":223},[11274,11282,11290,11298,11306,11314,11322,11330,11337,11344],{"type":42,"tag":229,"props":11275,"children":11276},{"class":231,"line":232},[11277],{"type":42,"tag":229,"props":11278,"children":11279},{},[11280],{"type":48,"value":11281},"fn boot(model: *Model, fx: *Effects) void {\n",{"type":42,"tag":229,"props":11283,"children":11284},{"class":231,"line":241},[11285],{"type":42,"tag":229,"props":11286,"children":11287},{},[11288],{"type":48,"value":11289},"    model.loading = true;\n",{"type":42,"tag":229,"props":11291,"children":11292},{"class":231,"line":251},[11293],{"type":42,"tag":229,"props":11294,"children":11295},{},[11296],{"type":48,"value":11297},"    fx.spawn(.{\n",{"type":42,"tag":229,"props":11299,"children":11300},{"class":231,"line":260},[11301],{"type":42,"tag":229,"props":11302,"children":11303},{},[11304],{"type":48,"value":11305},"        .key = issues_key,\n",{"type":42,"tag":229,"props":11307,"children":11308},{"class":231,"line":269},[11309],{"type":42,"tag":229,"props":11310,"children":11311},{},[11312],{"type":48,"value":11313},"        .argv = &.{ \"gh\", \"issue\", \"list\", \"--json\", \"number,title\" },\n",{"type":42,"tag":229,"props":11315,"children":11316},{"class":231,"line":278},[11317],{"type":42,"tag":229,"props":11318,"children":11319},{},[11320],{"type":48,"value":11321},"        .output = .collect,                        \u002F\u002F whole JSON on the exit Msg\n",{"type":42,"tag":229,"props":11323,"children":11324},{"class":231,"line":287},[11325],{"type":42,"tag":229,"props":11326,"children":11327},{},[11328],{"type":48,"value":11329},"        .on_exit = Effects.exitMsg(.issues_loaded),\n",{"type":42,"tag":229,"props":11331,"children":11332},{"class":231,"line":296},[11333],{"type":42,"tag":229,"props":11334,"children":11335},{},[11336],{"type":48,"value":392},{"type":42,"tag":229,"props":11338,"children":11339},{"class":231,"line":305},[11340],{"type":42,"tag":229,"props":11341,"children":11342},{},[11343],{"type":48,"value":428},{"type":42,"tag":229,"props":11345,"children":11346},{"class":231,"line":314},[11347],{"type":42,"tag":229,"props":11348,"children":11349},{},[11350],{"type":48,"value":11351},"\u002F\u002F options: .init_fx = boot,   (works with either update form)\n",{"type":42,"tag":51,"props":11353,"children":11354},{},[11355,11361,11363,11368],{"type":42,"tag":65,"props":11356,"children":11358},{"className":11357},[],[11359],{"type":48,"value":11360},"fx.spawn",{"type":48,"value":11362}," runs a subprocess on a runtime-owned worker thread and streams each stdout line back as a typed Msg; the exit arrives as one more Msg. Keys are caller-chosen ",{"type":42,"tag":65,"props":11364,"children":11366},{"className":11365},[],[11367],{"type":48,"value":4308},{"type":48,"value":11369},"s you keep in the model — no handles:",{"type":42,"tag":219,"props":11371,"children":11373},{"className":221,"code":11372,"language":18,"meta":223,"style":223},"pub const Msg = union(enum) {\n    start,\n    cancel,\n    line: native_sdk.EffectLine,     \u002F\u002F payload types are fixed\n    exited: native_sdk.EffectExit,\n};\n\n.start => fx.spawn(.{\n    .key = stream_key,                          \u002F\u002F model-stored identity\n    .argv = &.{ \"gh\", \"issue\", \"list\" },\n    .stdin = null,                              \u002F\u002F optional, written once\n    .on_line = Effects.lineMsg(.line),          \u002F\u002F comptime constructors,\n    .on_exit = Effects.exitMsg(.exited),        \u002F\u002F like ui.inputMsg(.tag)\n}),\n.cancel => fx.cancel(stream_key),\n.line => |line| model.recordLine(line),         \u002F\u002F COPY line.line — it is\n                                                \u002F\u002F drain scratch, dead after\n                                                \u002F\u002F this update call\n.exited => |exit| model.finish(exit),           \u002F\u002F exit.reason, exit.code\n",[11374],{"type":42,"tag":65,"props":11375,"children":11376},{"__ignoreMap":223},[11377,11385,11393,11401,11409,11417,11424,11431,11439,11447,11455,11463,11471,11479,11487,11495,11503,11511,11519],{"type":42,"tag":229,"props":11378,"children":11379},{"class":231,"line":232},[11380],{"type":42,"tag":229,"props":11381,"children":11382},{},[11383],{"type":48,"value":11384},"pub const Msg = union(enum) {\n",{"type":42,"tag":229,"props":11386,"children":11387},{"class":231,"line":241},[11388],{"type":42,"tag":229,"props":11389,"children":11390},{},[11391],{"type":48,"value":11392},"    start,\n",{"type":42,"tag":229,"props":11394,"children":11395},{"class":231,"line":251},[11396],{"type":42,"tag":229,"props":11397,"children":11398},{},[11399],{"type":48,"value":11400},"    cancel,\n",{"type":42,"tag":229,"props":11402,"children":11403},{"class":231,"line":260},[11404],{"type":42,"tag":229,"props":11405,"children":11406},{},[11407],{"type":48,"value":11408},"    line: native_sdk.EffectLine,     \u002F\u002F payload types are fixed\n",{"type":42,"tag":229,"props":11410,"children":11411},{"class":231,"line":269},[11412],{"type":42,"tag":229,"props":11413,"children":11414},{},[11415],{"type":48,"value":11416},"    exited: native_sdk.EffectExit,\n",{"type":42,"tag":229,"props":11418,"children":11419},{"class":231,"line":278},[11420],{"type":42,"tag":229,"props":11421,"children":11422},{},[11423],{"type":48,"value":669},{"type":42,"tag":229,"props":11425,"children":11426},{"class":231,"line":287},[11427],{"type":42,"tag":229,"props":11428,"children":11429},{"emptyLinePlaceholder":245},[11430],{"type":48,"value":248},{"type":42,"tag":229,"props":11432,"children":11433},{"class":231,"line":296},[11434],{"type":42,"tag":229,"props":11435,"children":11436},{},[11437],{"type":48,"value":11438},".start => fx.spawn(.{\n",{"type":42,"tag":229,"props":11440,"children":11441},{"class":231,"line":305},[11442],{"type":42,"tag":229,"props":11443,"children":11444},{},[11445],{"type":48,"value":11446},"    .key = stream_key,                          \u002F\u002F model-stored identity\n",{"type":42,"tag":229,"props":11448,"children":11449},{"class":231,"line":314},[11450],{"type":42,"tag":229,"props":11451,"children":11452},{},[11453],{"type":48,"value":11454},"    .argv = &.{ \"gh\", \"issue\", \"list\" },\n",{"type":42,"tag":229,"props":11456,"children":11457},{"class":231,"line":323},[11458],{"type":42,"tag":229,"props":11459,"children":11460},{},[11461],{"type":48,"value":11462},"    .stdin = null,                              \u002F\u002F optional, written once\n",{"type":42,"tag":229,"props":11464,"children":11465},{"class":231,"line":332},[11466],{"type":42,"tag":229,"props":11467,"children":11468},{},[11469],{"type":48,"value":11470},"    .on_line = Effects.lineMsg(.line),          \u002F\u002F comptime constructors,\n",{"type":42,"tag":229,"props":11472,"children":11473},{"class":231,"line":341},[11474],{"type":42,"tag":229,"props":11475,"children":11476},{},[11477],{"type":48,"value":11478},"    .on_exit = Effects.exitMsg(.exited),        \u002F\u002F like ui.inputMsg(.tag)\n",{"type":42,"tag":229,"props":11480,"children":11481},{"class":231,"line":350},[11482],{"type":42,"tag":229,"props":11483,"children":11484},{},[11485],{"type":48,"value":11486},"}),\n",{"type":42,"tag":229,"props":11488,"children":11489},{"class":231,"line":359},[11490],{"type":42,"tag":229,"props":11491,"children":11492},{},[11493],{"type":48,"value":11494},".cancel => fx.cancel(stream_key),\n",{"type":42,"tag":229,"props":11496,"children":11497},{"class":231,"line":368},[11498],{"type":42,"tag":229,"props":11499,"children":11500},{},[11501],{"type":48,"value":11502},".line => |line| model.recordLine(line),         \u002F\u002F COPY line.line — it is\n",{"type":42,"tag":229,"props":11504,"children":11505},{"class":231,"line":377},[11506],{"type":42,"tag":229,"props":11507,"children":11508},{},[11509],{"type":48,"value":11510},"                                                \u002F\u002F drain scratch, dead after\n",{"type":42,"tag":229,"props":11512,"children":11513},{"class":231,"line":386},[11514],{"type":42,"tag":229,"props":11515,"children":11516},{},[11517],{"type":48,"value":11518},"                                                \u002F\u002F this update call\n",{"type":42,"tag":229,"props":11520,"children":11521},{"class":231,"line":395},[11522],{"type":42,"tag":229,"props":11523,"children":11524},{},[11525],{"type":48,"value":11526},".exited => |exit| model.finish(exit),           \u002F\u002F exit.reason, exit.code\n",{"type":42,"tag":51,"props":11528,"children":11529},{},[11530],{"type":48,"value":11531},"Rules that keep this honest:",{"type":42,"tag":57,"props":11533,"children":11534},{},[11535,11540,11605,11634,11663,11699],{"type":42,"tag":61,"props":11536,"children":11537},{},[11538],{"type":48,"value":11539},"Effects are update-side ONLY. The view never spawns anything — a button dispatches a Msg, and that Msg's update arm spawns. Markup stays declarative.",{"type":42,"tag":61,"props":11541,"children":11542},{},[11543,11545,11551,11553,11559,11561,11567,11569,11575,11577,11583,11584,11590,11591,11597,11598,11604],{"type":48,"value":11544},"One ",{"type":42,"tag":65,"props":11546,"children":11548},{"className":11547},[],[11549],{"type":48,"value":11550},"on_exit",{"type":48,"value":11552}," Msg per spawn, always. A spawn that cannot run (all ",{"type":42,"tag":65,"props":11554,"children":11556},{"className":11555},[],[11557],{"type":48,"value":11558},"max_effects = 16",{"type":48,"value":11560}," slots busy, duplicate active key, argv over capacity) still delivers it, with reason ",{"type":42,"tag":65,"props":11562,"children":11564},{"className":11563},[],[11565],{"type":48,"value":11566},".rejected",{"type":48,"value":11568},". Reasons: ",{"type":42,"tag":65,"props":11570,"children":11572},{"className":11571},[],[11573],{"type":48,"value":11574},"exited",{"type":48,"value":11576}," (code is real), ",{"type":42,"tag":65,"props":11578,"children":11580},{"className":11579},[],[11581],{"type":48,"value":11582},"signaled",{"type":48,"value":1820},{"type":42,"tag":65,"props":11585,"children":11587},{"className":11586},[],[11588],{"type":48,"value":11589},"cancelled",{"type":48,"value":1820},{"type":42,"tag":65,"props":11592,"children":11594},{"className":11593},[],[11595],{"type":48,"value":11596},"rejected",{"type":48,"value":1820},{"type":42,"tag":65,"props":11599,"children":11601},{"className":11600},[],[11602],{"type":48,"value":11603},"spawn_failed",{"type":48,"value":123},{"type":42,"tag":61,"props":11606,"children":11607},{},[11608,11610,11616,11618,11624,11626,11632],{"type":48,"value":11609},"After ",{"type":42,"tag":65,"props":11611,"children":11613},{"className":11612},[],[11614],{"type":48,"value":11615},"fx.cancel(key)",{"type":48,"value":11617}," returns, no further ",{"type":42,"tag":65,"props":11619,"children":11621},{"className":11620},[],[11622],{"type":48,"value":11623},"on_line",{"type":48,"value":11625}," Msgs for that spawn arrive; exactly one ",{"type":42,"tag":65,"props":11627,"children":11629},{"className":11628},[],[11630],{"type":48,"value":11631},".cancelled",{"type":48,"value":11633}," exit follows. The process is killed and reaped — no zombies. Streaming a chat agent's stdout for minutes and cancelling mid-stream is the designed-for case.",{"type":42,"tag":61,"props":11635,"children":11636},{},[11637,11639,11645,11647,11653,11655,11661],{"type":48,"value":11638},"Overflow is never silent: a full completion queue drops lines but the next delivered line's ",{"type":42,"tag":65,"props":11640,"children":11642},{"className":11641},[],[11643],{"type":48,"value":11644},"dropped_before",{"type":48,"value":11646}," and the exit's ",{"type":42,"tag":65,"props":11648,"children":11650},{"className":11649},[],[11651],{"type":48,"value":11652},"dropped_lines",{"type":48,"value":11654}," carry the count; over-long lines arrive truncated with ",{"type":42,"tag":65,"props":11656,"children":11658},{"className":11657},[],[11659],{"type":48,"value":11660},"truncated = true",{"type":48,"value":11662},". Capacities: 16 in-flight effects, 4 KiB per line by default, 64 queued completions.",{"type":42,"tag":61,"props":11664,"children":11665},{},[11666,11668,11674,11676,11682,11684,11690,11692,11697],{"type":48,"value":11667},"Agent CLIs emit whole events as single NDJSON lines far beyond 4 KiB (",{"type":42,"tag":65,"props":11669,"children":11671},{"className":11670},[],[11672],{"type":48,"value":11673},"claude -p --output-format stream-json",{"type":48,"value":11675}," repeats the entire answer on one line). Raise the bound per spawn with ",{"type":42,"tag":65,"props":11677,"children":11679},{"className":11678},[],[11680],{"type":48,"value":11681},".max_line_bytes = 64 * 1024",{"type":48,"value":11683}," — anything up to ",{"type":42,"tag":65,"props":11685,"children":11687},{"className":11686},[],[11688],{"type":48,"value":11689},"max_effect_line_bytes_ceiling",{"type":48,"value":11691}," (256 KiB); requests above the ceiling (or zero) are rejected through ",{"type":42,"tag":65,"props":11693,"children":11695},{"className":11694},[],[11696],{"type":48,"value":11550},{"type":48,"value":11698},", never silently clamped. Lines beyond the granted bound still arrive truncated and flagged. The ceiling has envelope headroom: a stream that WRAPS another stream's lines (sandbox exec NDJSON envelopes carrying JSON-escaped agent events) can carry a full 64 KiB inner line with escaping overhead to spare — size the outer bound at roughly 2-3x the inner one.",{"type":42,"tag":61,"props":11700,"children":11701},{},[11702,11704,11710,11711,11717,11718,11724,11726,11732,11734,11740,11742,11748,11750,11756,11758,11764,11766,11771,11773,11779,11780,11786,11788,11793,11794,11799,11801,11807,11809,11814,11816,11822,11824,11830],{"type":48,"value":11703},"JSON-over-stdout (",{"type":42,"tag":65,"props":11705,"children":11707},{"className":11706},[],[11708],{"type":48,"value":11709},"gh --json",{"type":48,"value":1820},{"type":42,"tag":65,"props":11712,"children":11714},{"className":11713},[],[11715],{"type":48,"value":11716},"jq -c",{"type":48,"value":1820},{"type":42,"tag":65,"props":11719,"children":11721},{"className":11720},[],[11722],{"type":48,"value":11723},"curl",{"type":48,"value":11725},") emits one giant line the 4 KiB line cap would destroy. Spawn with ",{"type":42,"tag":65,"props":11727,"children":11729},{"className":11728},[],[11730],{"type":48,"value":11731},".output = .collect",{"type":48,"value":11733}," instead of the default ",{"type":42,"tag":65,"props":11735,"children":11737},{"className":11736},[],[11738],{"type":48,"value":11739},".lines",{"type":48,"value":11741},": whole stdout (up to 512 KiB) arrives ONCE on the exit Msg as ",{"type":42,"tag":65,"props":11743,"children":11745},{"className":11744},[],[11746],{"type":48,"value":11747},"exit.output",{"type":48,"value":11749},", plus the child's stderr tail (last 4 KiB) in ",{"type":42,"tag":65,"props":11751,"children":11753},{"className":11752},[],[11754],{"type":48,"value":11755},"exit.stderr_tail",{"type":48,"value":11757}," — check it when ",{"type":42,"tag":65,"props":11759,"children":11761},{"className":11760},[],[11762],{"type":48,"value":11763},"exit.code != 0",{"type":48,"value":11765}," (auth errors, usage messages). No ",{"type":42,"tag":65,"props":11767,"children":11769},{"className":11768},[],[11770],{"type":48,"value":11623},{"type":48,"value":11772}," Msgs fire for a collect spawn; overflow arrives cut with ",{"type":42,"tag":65,"props":11774,"children":11776},{"className":11775},[],[11777],{"type":48,"value":11778},"output_truncated",{"type":48,"value":456},{"type":42,"tag":65,"props":11781,"children":11783},{"className":11782},[],[11784],{"type":48,"value":11785},"stderr_truncated",{"type":48,"value":11787}," set, never silently. COPY ",{"type":42,"tag":65,"props":11789,"children":11791},{"className":11790},[],[11792],{"type":48,"value":11747},{"type":48,"value":456},{"type":42,"tag":65,"props":11795,"children":11797},{"className":11796},[],[11798],{"type":48,"value":11755},{"type":48,"value":11800}," in update — drain scratch like ",{"type":42,"tag":65,"props":11802,"children":11804},{"className":11803},[],[11805],{"type":48,"value":11806},"line.line",{"type":48,"value":11808},"; the scalar exit fields stay plain data, safe to store. (",{"type":42,"tag":65,"props":11810,"children":11812},{"className":11811},[],[11813],{"type":48,"value":11739},{"type":48,"value":11815}," mode still ignores stderr entirely; use ",{"type":42,"tag":65,"props":11817,"children":11819},{"className":11818},[],[11820],{"type":48,"value":11821},".collect",{"type":48,"value":11823},", or an sh ",{"type":42,"tag":65,"props":11825,"children":11827},{"className":11826},[],[11828],{"type":48,"value":11829},"2>&1",{"type":48,"value":11831}," re-route if you truly need interleaved streaming.)",{"type":42,"tag":51,"props":11833,"children":11834},{},[11835,11841],{"type":42,"tag":65,"props":11836,"children":11838},{"className":11837},[],[11839],{"type":48,"value":11840},"fx.fetch",{"type":48,"value":11842}," runs one HTTP(S) request on a worker thread and delivers its terminal outcome — response, classified failure, timeout, or cancel — as exactly ONE Msg:",{"type":42,"tag":219,"props":11844,"children":11846},{"className":221,"code":11845,"language":18,"meta":223,"style":223},"pub const Msg = union(enum) {\n    load,\n    stop,\n    fetched: native_sdk.EffectResponse,   \u002F\u002F the fixed payload type\n};\n\n.load => fx.fetch(.{\n    .key = search_key,                     \u002F\u002F same key space + 16 slots as spawns\n    .method = .POST,                       \u002F\u002F std.http.Method; default .GET\n    .url = \"https:\u002F\u002Fapi.example.com\u002Frun\",  \u002F\u002F http:\u002F\u002F or https:\u002F\u002F only\n    .headers = &.{.{ .name = \"authorization\", .value = \"Bearer abc\" }},\n    .body = \"{\\\"q\\\":\\\"zig\\\"}\",             \u002F\u002F optional request payload\n    .timeout_ms = 10_000,                  \u002F\u002F whole exchange; default 30 s\n    .on_response = Effects.responseMsg(.fetched),\n}),\n.stop => fx.cancel(search_key),\n.fetched => |response| model.record(response),  \u002F\u002F COPY response.body — drain\n                                                \u002F\u002F scratch, dead after this call\n",[11847],{"type":42,"tag":65,"props":11848,"children":11849},{"__ignoreMap":223},[11850,11857,11865,11873,11881,11888,11895,11903,11911,11919,11927,11935,11943,11951,11959,11966,11974,11982],{"type":42,"tag":229,"props":11851,"children":11852},{"class":231,"line":232},[11853],{"type":42,"tag":229,"props":11854,"children":11855},{},[11856],{"type":48,"value":11384},{"type":42,"tag":229,"props":11858,"children":11859},{"class":231,"line":241},[11860],{"type":42,"tag":229,"props":11861,"children":11862},{},[11863],{"type":48,"value":11864},"    load,\n",{"type":42,"tag":229,"props":11866,"children":11867},{"class":231,"line":251},[11868],{"type":42,"tag":229,"props":11869,"children":11870},{},[11871],{"type":48,"value":11872},"    stop,\n",{"type":42,"tag":229,"props":11874,"children":11875},{"class":231,"line":260},[11876],{"type":42,"tag":229,"props":11877,"children":11878},{},[11879],{"type":48,"value":11880},"    fetched: native_sdk.EffectResponse,   \u002F\u002F the fixed payload type\n",{"type":42,"tag":229,"props":11882,"children":11883},{"class":231,"line":269},[11884],{"type":42,"tag":229,"props":11885,"children":11886},{},[11887],{"type":48,"value":669},{"type":42,"tag":229,"props":11889,"children":11890},{"class":231,"line":278},[11891],{"type":42,"tag":229,"props":11892,"children":11893},{"emptyLinePlaceholder":245},[11894],{"type":48,"value":248},{"type":42,"tag":229,"props":11896,"children":11897},{"class":231,"line":287},[11898],{"type":42,"tag":229,"props":11899,"children":11900},{},[11901],{"type":48,"value":11902},".load => fx.fetch(.{\n",{"type":42,"tag":229,"props":11904,"children":11905},{"class":231,"line":296},[11906],{"type":42,"tag":229,"props":11907,"children":11908},{},[11909],{"type":48,"value":11910},"    .key = search_key,                     \u002F\u002F same key space + 16 slots as spawns\n",{"type":42,"tag":229,"props":11912,"children":11913},{"class":231,"line":305},[11914],{"type":42,"tag":229,"props":11915,"children":11916},{},[11917],{"type":48,"value":11918},"    .method = .POST,                       \u002F\u002F std.http.Method; default .GET\n",{"type":42,"tag":229,"props":11920,"children":11921},{"class":231,"line":314},[11922],{"type":42,"tag":229,"props":11923,"children":11924},{},[11925],{"type":48,"value":11926},"    .url = \"https:\u002F\u002Fapi.example.com\u002Frun\",  \u002F\u002F http:\u002F\u002F or https:\u002F\u002F only\n",{"type":42,"tag":229,"props":11928,"children":11929},{"class":231,"line":323},[11930],{"type":42,"tag":229,"props":11931,"children":11932},{},[11933],{"type":48,"value":11934},"    .headers = &.{.{ .name = \"authorization\", .value = \"Bearer abc\" }},\n",{"type":42,"tag":229,"props":11936,"children":11937},{"class":231,"line":332},[11938],{"type":42,"tag":229,"props":11939,"children":11940},{},[11941],{"type":48,"value":11942},"    .body = \"{\\\"q\\\":\\\"zig\\\"}\",             \u002F\u002F optional request payload\n",{"type":42,"tag":229,"props":11944,"children":11945},{"class":231,"line":341},[11946],{"type":42,"tag":229,"props":11947,"children":11948},{},[11949],{"type":48,"value":11950},"    .timeout_ms = 10_000,                  \u002F\u002F whole exchange; default 30 s\n",{"type":42,"tag":229,"props":11952,"children":11953},{"class":231,"line":350},[11954],{"type":42,"tag":229,"props":11955,"children":11956},{},[11957],{"type":48,"value":11958},"    .on_response = Effects.responseMsg(.fetched),\n",{"type":42,"tag":229,"props":11960,"children":11961},{"class":231,"line":359},[11962],{"type":42,"tag":229,"props":11963,"children":11964},{},[11965],{"type":48,"value":11486},{"type":42,"tag":229,"props":11967,"children":11968},{"class":231,"line":368},[11969],{"type":42,"tag":229,"props":11970,"children":11971},{},[11972],{"type":48,"value":11973},".stop => fx.cancel(search_key),\n",{"type":42,"tag":229,"props":11975,"children":11976},{"class":231,"line":377},[11977],{"type":42,"tag":229,"props":11978,"children":11979},{},[11980],{"type":48,"value":11981},".fetched => |response| model.record(response),  \u002F\u002F COPY response.body — drain\n",{"type":42,"tag":229,"props":11983,"children":11984},{"class":231,"line":386},[11985],{"type":42,"tag":229,"props":11986,"children":11987},{},[11988],{"type":48,"value":11989},"                                                \u002F\u002F scratch, dead after this call\n",{"type":42,"tag":51,"props":11991,"children":11992},{},[11993],{"type":48,"value":11994},"Fetch rules:",{"type":42,"tag":57,"props":11996,"children":11997},{},[11998,12078,12096],{"type":42,"tag":61,"props":11999,"children":12000},{},[12001,12003,12009,12011,12017,12019,12025,12027,12033,12035,12040,12042,12048,12050,12056,12057,12063,12065,12071,12072,12077],{"type":48,"value":12002},"Exactly one ",{"type":42,"tag":65,"props":12004,"children":12006},{"className":12005},[],[12007],{"type":48,"value":12008},"on_response",{"type":48,"value":12010}," Msg per fetch, always terminal. ",{"type":42,"tag":65,"props":12012,"children":12014},{"className":12013},[],[12015],{"type":48,"value":12016},"response.outcome",{"type":48,"value":12018}," says what happened: ",{"type":42,"tag":65,"props":12020,"children":12022},{"className":12021},[],[12023],{"type":48,"value":12024},".ok",{"type":48,"value":12026}," (real HTTP status in ",{"type":42,"tag":65,"props":12028,"children":12030},{"className":12029},[],[12031],{"type":48,"value":12032},".status",{"type":48,"value":12034}," — non-2xx included; an HTTP-level error is still a delivered response), ",{"type":42,"tag":65,"props":12036,"children":12038},{"className":12037},[],[12039],{"type":48,"value":11566},{"type":48,"value":12041}," (never started: slots busy, duplicate active key, malformed URL or non-http(s) scheme, over-capacity URL\u002Fheaders\u002Fpayload), ",{"type":42,"tag":65,"props":12043,"children":12045},{"className":12044},[],[12046],{"type":48,"value":12047},".connect_failed",{"type":48,"value":12049}," (DNS or TCP), ",{"type":42,"tag":65,"props":12051,"children":12053},{"className":12052},[],[12054],{"type":48,"value":12055},".tls_failed",{"type":48,"value":1820},{"type":42,"tag":65,"props":12058,"children":12060},{"className":12059},[],[12061],{"type":48,"value":12062},".protocol_failed",{"type":48,"value":12064}," (mid-exchange), ",{"type":42,"tag":65,"props":12066,"children":12068},{"className":12067},[],[12069],{"type":48,"value":12070},".timed_out",{"type":48,"value":1820},{"type":42,"tag":65,"props":12073,"children":12075},{"className":12074},[],[12076],{"type":48,"value":11631},{"type":48,"value":123},{"type":42,"tag":61,"props":12079,"children":12080},{},[12081,12087,12089,12094],{"type":42,"tag":65,"props":12082,"children":12084},{"className":12083},[],[12085],{"type":48,"value":12086},"response.body",{"type":48,"value":12088}," is binary-safe bytes (zeros and high bits round-trip). Bodies over 256 KiB arrive cut at that bound with ",{"type":42,"tag":65,"props":12090,"children":12092},{"className":12091},[],[12093],{"type":48,"value":11660},{"type":48,"value":12095}," — never silently. Capacities: 2 KiB URLs, 8 extra headers (1 KiB of names+values total), 64 KiB request payloads.",{"type":42,"tag":61,"props":12097,"children":12098},{},[12099,12104,12106,12111],{"type":42,"tag":65,"props":12100,"children":12102},{"className":12101},[],[12103],{"type":48,"value":11615},{"type":48,"value":12105}," keeps the spawn promise: exactly one ",{"type":42,"tag":65,"props":12107,"children":12109},{"className":12108},[],[12110],{"type":48,"value":11631},{"type":48,"value":12112}," response Msg, nothing for that fetch after it.",{"type":42,"tag":51,"props":12114,"children":12115},{},[12116,12118,12124,12126,12131,12133,12138,12140,12146],{"type":48,"value":12117},"Streaming responses (",{"type":42,"tag":65,"props":12119,"children":12121},{"className":12120},[],[12122],{"type":48,"value":12123},".response = .stream",{"type":48,"value":12125},") frame the body into ",{"type":42,"tag":65,"props":12127,"children":12129},{"className":12128},[],[12130],{"type":48,"value":11623},{"type":48,"value":12132}," Msgs as lines arrive — the spawn ",{"type":42,"tag":65,"props":12134,"children":12136},{"className":12135},[],[12137],{"type":48,"value":11739},{"type":48,"value":12139}," contract over HTTP. This is THE mode for NDJSON\u002FSSE endpoints that hold the connection open for a command's whole lifetime (Vercel Sandbox ",{"type":42,"tag":65,"props":12141,"children":12143},{"className":12142},[],[12144],{"type":48,"value":12145},"POST ...\u002Fcmd",{"type":48,"value":12147}," with wait+logs, agent event streams):",{"type":42,"tag":219,"props":12149,"children":12151},{"className":221,"code":12150,"language":18,"meta":223,"style":223},".run => fx.fetch(.{\n    .key = exec_key,\n    .method = .POST,\n    .url = sandbox_cmd_url,\n    .headers = &.{.{ .name = \"authorization\", .value = token }},\n    .body = cmd_json,\n    .timeout_ms = 600_000,                 \u002F\u002F covers the STREAM's whole lifetime\n    .response = .stream,                   \u002F\u002F body arrives as on_line Msgs\n    .max_line_bytes = 256 * 1024,          \u002F\u002F envelope lines WRAP agent events\n                                           \u002F\u002F (JSON-escaped): size the outer\n                                           \u002F\u002F bound 2-3x the inner one\n    .on_line = Effects.lineMsg(.exec_event),\n    .on_response = Effects.responseMsg(.exec_done),\n}),\n.exec_event => |line| model.recordEvent(line),  \u002F\u002F COPY line.line — drain scratch\n.exec_done => |response| model.finish(response), \u002F\u002F status set, body always empty\n",[12152],{"type":42,"tag":65,"props":12153,"children":12154},{"__ignoreMap":223},[12155,12163,12171,12179,12187,12195,12203,12211,12219,12227,12235,12243,12251,12259,12266,12274],{"type":42,"tag":229,"props":12156,"children":12157},{"class":231,"line":232},[12158],{"type":42,"tag":229,"props":12159,"children":12160},{},[12161],{"type":48,"value":12162},".run => fx.fetch(.{\n",{"type":42,"tag":229,"props":12164,"children":12165},{"class":231,"line":241},[12166],{"type":42,"tag":229,"props":12167,"children":12168},{},[12169],{"type":48,"value":12170},"    .key = exec_key,\n",{"type":42,"tag":229,"props":12172,"children":12173},{"class":231,"line":251},[12174],{"type":42,"tag":229,"props":12175,"children":12176},{},[12177],{"type":48,"value":12178},"    .method = .POST,\n",{"type":42,"tag":229,"props":12180,"children":12181},{"class":231,"line":260},[12182],{"type":42,"tag":229,"props":12183,"children":12184},{},[12185],{"type":48,"value":12186},"    .url = sandbox_cmd_url,\n",{"type":42,"tag":229,"props":12188,"children":12189},{"class":231,"line":269},[12190],{"type":42,"tag":229,"props":12191,"children":12192},{},[12193],{"type":48,"value":12194},"    .headers = &.{.{ .name = \"authorization\", .value = token }},\n",{"type":42,"tag":229,"props":12196,"children":12197},{"class":231,"line":278},[12198],{"type":42,"tag":229,"props":12199,"children":12200},{},[12201],{"type":48,"value":12202},"    .body = cmd_json,\n",{"type":42,"tag":229,"props":12204,"children":12205},{"class":231,"line":287},[12206],{"type":42,"tag":229,"props":12207,"children":12208},{},[12209],{"type":48,"value":12210},"    .timeout_ms = 600_000,                 \u002F\u002F covers the STREAM's whole lifetime\n",{"type":42,"tag":229,"props":12212,"children":12213},{"class":231,"line":296},[12214],{"type":42,"tag":229,"props":12215,"children":12216},{},[12217],{"type":48,"value":12218},"    .response = .stream,                   \u002F\u002F body arrives as on_line Msgs\n",{"type":42,"tag":229,"props":12220,"children":12221},{"class":231,"line":305},[12222],{"type":42,"tag":229,"props":12223,"children":12224},{},[12225],{"type":48,"value":12226},"    .max_line_bytes = 256 * 1024,          \u002F\u002F envelope lines WRAP agent events\n",{"type":42,"tag":229,"props":12228,"children":12229},{"class":231,"line":314},[12230],{"type":42,"tag":229,"props":12231,"children":12232},{},[12233],{"type":48,"value":12234},"                                           \u002F\u002F (JSON-escaped): size the outer\n",{"type":42,"tag":229,"props":12236,"children":12237},{"class":231,"line":323},[12238],{"type":42,"tag":229,"props":12239,"children":12240},{},[12241],{"type":48,"value":12242},"                                           \u002F\u002F bound 2-3x the inner one\n",{"type":42,"tag":229,"props":12244,"children":12245},{"class":231,"line":332},[12246],{"type":42,"tag":229,"props":12247,"children":12248},{},[12249],{"type":48,"value":12250},"    .on_line = Effects.lineMsg(.exec_event),\n",{"type":42,"tag":229,"props":12252,"children":12253},{"class":231,"line":341},[12254],{"type":42,"tag":229,"props":12255,"children":12256},{},[12257],{"type":48,"value":12258},"    .on_response = Effects.responseMsg(.exec_done),\n",{"type":42,"tag":229,"props":12260,"children":12261},{"class":231,"line":350},[12262],{"type":42,"tag":229,"props":12263,"children":12264},{},[12265],{"type":48,"value":11486},{"type":42,"tag":229,"props":12267,"children":12268},{"class":231,"line":359},[12269],{"type":42,"tag":229,"props":12270,"children":12271},{},[12272],{"type":48,"value":12273},".exec_event => |line| model.recordEvent(line),  \u002F\u002F COPY line.line — drain scratch\n",{"type":42,"tag":229,"props":12275,"children":12276},{"class":231,"line":368},[12277],{"type":42,"tag":229,"props":12278,"children":12279},{},[12280],{"type":48,"value":12281},".exec_done => |response| model.finish(response), \u002F\u002F status set, body always empty\n",{"type":42,"tag":51,"props":12283,"children":12284},{},[12285,12287,12292,12294,12300,12302,12307,12309,12314,12316,12321,12323,12329,12331,12337,12339,12345,12347,12353],{"type":48,"value":12286},"Stream rules: each body line is one ",{"type":42,"tag":65,"props":12288,"children":12290},{"className":12289},[],[12291],{"type":48,"value":11623},{"type":48,"value":12293}," Msg (same payload type and copy rule as spawn lines; ",{"type":42,"tag":65,"props":12295,"children":12297},{"className":12296},[],[12298],{"type":48,"value":12299},"max_line_bytes",{"type":48,"value":12301}," mirrors the spawn override with the same 256 KiB ceiling); the terminal ",{"type":42,"tag":65,"props":12303,"children":12305},{"className":12304},[],[12306],{"type":48,"value":12008},{"type":48,"value":12308}," Msg carries the real HTTP status with an empty body; ",{"type":42,"tag":65,"props":12310,"children":12312},{"className":12311},[],[12313],{"type":48,"value":11615},{"type":48,"value":12315}," mid-stream stops the lines and delivers exactly one ",{"type":42,"tag":65,"props":12317,"children":12319},{"className":12318},[],[12320],{"type":48,"value":11631},{"type":48,"value":12322}," terminal; the whole-exchange ",{"type":42,"tag":65,"props":12324,"children":12326},{"className":12325},[],[12327],{"type":48,"value":12328},"timeout_ms",{"type":48,"value":12330}," covers the stream's full lifetime, so raise it for long-running commands; lines dropped on a full queue that no later line reported ride the terminal's ",{"type":42,"tag":65,"props":12332,"children":12334},{"className":12333},[],[12335],{"type":48,"value":12336},"response.dropped_before",{"type":48,"value":12338},". In the fake executor, ",{"type":42,"tag":65,"props":12340,"children":12342},{"className":12341},[],[12343],{"type":48,"value":12344},"feedLine",{"type":48,"value":12346}," feeds a stream fetch's lines and ",{"type":42,"tag":65,"props":12348,"children":12350},{"className":12349},[],[12351],{"type":48,"value":12352},"feedResponse(key, status, \"\")",{"type":48,"value":12354}," delivers its terminal.",{"type":42,"tag":51,"props":12356,"children":12357},{},[12358,12364,12365,12371,12373,12379,12381,12386,12388,12393],{"type":42,"tag":65,"props":12359,"children":12361},{"className":12360},[],[12362],{"type":48,"value":12363},"fx.writeFile",{"type":48,"value":7747},{"type":42,"tag":65,"props":12366,"children":12368},{"className":12367},[],[12369],{"type":48,"value":12370},"fx.readFile",{"type":48,"value":12372}," are TEA-friendly file persistence — session snapshots, app state — without smuggling an ",{"type":42,"tag":65,"props":12374,"children":12376},{"className":12375},[],[12377],{"type":48,"value":12378},"Io",{"type":48,"value":12380}," handle from ",{"type":42,"tag":65,"props":12382,"children":12384},{"className":12383},[],[12385],{"type":48,"value":113},{"type":48,"value":12387}," into ",{"type":42,"tag":65,"props":12389,"children":12391},{"className":12390},[],[12392],{"type":48,"value":483},{"type":48,"value":12394},". Same discipline as spawn and fetch: bounded, key-based (shared key space and 16 slots), exactly one terminal Msg with an explicit outcome:",{"type":42,"tag":219,"props":12396,"children":12398},{"className":221,"code":12397,"language":18,"meta":223,"style":223},"pub const Msg = union(enum) {\n    save,\n    boot,\n    saved: native_sdk.EffectFileResult,   \u002F\u002F the fixed payload type\n    loaded: native_sdk.EffectFileResult,\n};\n\n.save => fx.writeFile(.{\n    .key = save_key,\n    .path = model.sessionPath(),           \u002F\u002F ≤ 1 KiB; parent dirs are created\n    .bytes = model.snapshotJson(),         \u002F\u002F ≤ 1 MiB, copied at call time\n    .on_result = Effects.fileMsg(.saved),\n}),\n.boot => fx.readFile(.{\n    .key = load_key,\n    .path = model.sessionPath(),\n    .on_result = Effects.fileMsg(.loaded),\n}),\n.saved => |result| model.noteSaved(result.outcome),\n.loaded => |result| model.restore(result),  \u002F\u002F COPY result.bytes — drain scratch\n",[12399],{"type":42,"tag":65,"props":12400,"children":12401},{"__ignoreMap":223},[12402,12409,12417,12425,12433,12441,12448,12455,12463,12471,12479,12487,12495,12502,12510,12518,12526,12534,12541,12549],{"type":42,"tag":229,"props":12403,"children":12404},{"class":231,"line":232},[12405],{"type":42,"tag":229,"props":12406,"children":12407},{},[12408],{"type":48,"value":11384},{"type":42,"tag":229,"props":12410,"children":12411},{"class":231,"line":241},[12412],{"type":42,"tag":229,"props":12413,"children":12414},{},[12415],{"type":48,"value":12416},"    save,\n",{"type":42,"tag":229,"props":12418,"children":12419},{"class":231,"line":251},[12420],{"type":42,"tag":229,"props":12421,"children":12422},{},[12423],{"type":48,"value":12424},"    boot,\n",{"type":42,"tag":229,"props":12426,"children":12427},{"class":231,"line":260},[12428],{"type":42,"tag":229,"props":12429,"children":12430},{},[12431],{"type":48,"value":12432},"    saved: native_sdk.EffectFileResult,   \u002F\u002F the fixed payload type\n",{"type":42,"tag":229,"props":12434,"children":12435},{"class":231,"line":269},[12436],{"type":42,"tag":229,"props":12437,"children":12438},{},[12439],{"type":48,"value":12440},"    loaded: native_sdk.EffectFileResult,\n",{"type":42,"tag":229,"props":12442,"children":12443},{"class":231,"line":278},[12444],{"type":42,"tag":229,"props":12445,"children":12446},{},[12447],{"type":48,"value":669},{"type":42,"tag":229,"props":12449,"children":12450},{"class":231,"line":287},[12451],{"type":42,"tag":229,"props":12452,"children":12453},{"emptyLinePlaceholder":245},[12454],{"type":48,"value":248},{"type":42,"tag":229,"props":12456,"children":12457},{"class":231,"line":296},[12458],{"type":42,"tag":229,"props":12459,"children":12460},{},[12461],{"type":48,"value":12462},".save => fx.writeFile(.{\n",{"type":42,"tag":229,"props":12464,"children":12465},{"class":231,"line":305},[12466],{"type":42,"tag":229,"props":12467,"children":12468},{},[12469],{"type":48,"value":12470},"    .key = save_key,\n",{"type":42,"tag":229,"props":12472,"children":12473},{"class":231,"line":314},[12474],{"type":42,"tag":229,"props":12475,"children":12476},{},[12477],{"type":48,"value":12478},"    .path = model.sessionPath(),           \u002F\u002F ≤ 1 KiB; parent dirs are created\n",{"type":42,"tag":229,"props":12480,"children":12481},{"class":231,"line":323},[12482],{"type":42,"tag":229,"props":12483,"children":12484},{},[12485],{"type":48,"value":12486},"    .bytes = model.snapshotJson(),         \u002F\u002F ≤ 1 MiB, copied at call time\n",{"type":42,"tag":229,"props":12488,"children":12489},{"class":231,"line":332},[12490],{"type":42,"tag":229,"props":12491,"children":12492},{},[12493],{"type":48,"value":12494},"    .on_result = Effects.fileMsg(.saved),\n",{"type":42,"tag":229,"props":12496,"children":12497},{"class":231,"line":341},[12498],{"type":42,"tag":229,"props":12499,"children":12500},{},[12501],{"type":48,"value":11486},{"type":42,"tag":229,"props":12503,"children":12504},{"class":231,"line":350},[12505],{"type":42,"tag":229,"props":12506,"children":12507},{},[12508],{"type":48,"value":12509},".boot => fx.readFile(.{\n",{"type":42,"tag":229,"props":12511,"children":12512},{"class":231,"line":359},[12513],{"type":42,"tag":229,"props":12514,"children":12515},{},[12516],{"type":48,"value":12517},"    .key = load_key,\n",{"type":42,"tag":229,"props":12519,"children":12520},{"class":231,"line":368},[12521],{"type":42,"tag":229,"props":12522,"children":12523},{},[12524],{"type":48,"value":12525},"    .path = model.sessionPath(),\n",{"type":42,"tag":229,"props":12527,"children":12528},{"class":231,"line":377},[12529],{"type":42,"tag":229,"props":12530,"children":12531},{},[12532],{"type":48,"value":12533},"    .on_result = Effects.fileMsg(.loaded),\n",{"type":42,"tag":229,"props":12535,"children":12536},{"class":231,"line":386},[12537],{"type":42,"tag":229,"props":12538,"children":12539},{},[12540],{"type":48,"value":11486},{"type":42,"tag":229,"props":12542,"children":12543},{"class":231,"line":395},[12544],{"type":42,"tag":229,"props":12545,"children":12546},{},[12547],{"type":48,"value":12548},".saved => |result| model.noteSaved(result.outcome),\n",{"type":42,"tag":229,"props":12550,"children":12551},{"class":231,"line":404},[12552],{"type":42,"tag":229,"props":12553,"children":12554},{},[12555],{"type":48,"value":12556},".loaded => |result| model.restore(result),  \u002F\u002F COPY result.bytes — drain scratch\n",{"type":42,"tag":51,"props":12558,"children":12559},{},[12560],{"type":48,"value":12561},"File rules:",{"type":42,"tag":57,"props":12563,"children":12564},{},[12565,12642,12655],{"type":42,"tag":61,"props":12566,"children":12567},{},[12568,12574,12576,12581,12583,12589,12591,12597,12599,12605,12607,12613,12615,12621,12622,12627,12629,12634,12636,12641],{"type":42,"tag":65,"props":12569,"children":12571},{"className":12570},[],[12572],{"type":48,"value":12573},"result.outcome",{"type":48,"value":12575}," is explicit: ",{"type":42,"tag":65,"props":12577,"children":12579},{"className":12578},[],[12580],{"type":48,"value":12024},{"type":48,"value":12582}," (a read's whole content in ",{"type":42,"tag":65,"props":12584,"children":12586},{"className":12585},[],[12587],{"type":48,"value":12588},"result.bytes",{"type":48,"value":12590},"; a write fully on disk), ",{"type":42,"tag":65,"props":12592,"children":12594},{"className":12593},[],[12595],{"type":48,"value":12596},".not_found",{"type":48,"value":12598}," (reads only — writes create the path, parent directories included), ",{"type":42,"tag":65,"props":12600,"children":12602},{"className":12601},[],[12603],{"type":48,"value":12604},".io_failed",{"type":48,"value":12606}," (permissions, path is a directory, disk), ",{"type":42,"tag":65,"props":12608,"children":12610},{"className":12609},[],[12611],{"type":48,"value":12612},".truncated",{"type":48,"value":12614}," (the file exceeds the 1 MiB ",{"type":42,"tag":65,"props":12616,"children":12618},{"className":12617},[],[12619],{"type":48,"value":12620},"max_effect_file_bytes",{"type":48,"value":2930},{"type":42,"tag":65,"props":12623,"children":12625},{"className":12624},[],[12626],{"type":48,"value":12588},{"type":48,"value":12628}," is the first bound bytes — its own outcome, not a flag, because a cut JSON snapshot must not parse as whole), ",{"type":42,"tag":65,"props":12630,"children":12632},{"className":12631},[],[12633],{"type":48,"value":11566},{"type":48,"value":12635}," (never ran: slots busy, duplicate key, empty\u002Fover-long path, write bytes over the bound — an over-bound WRITE is rejected outright since a partial write would corrupt the file), ",{"type":42,"tag":65,"props":12637,"children":12639},{"className":12638},[],[12640],{"type":48,"value":11631},{"type":48,"value":123},{"type":42,"tag":61,"props":12643,"children":12644},{},[12645,12647,12653],{"type":48,"value":12646},"Writes replace the file whole; ",{"type":42,"tag":65,"props":12648,"children":12650},{"className":12649},[],[12651],{"type":48,"value":12652},"writeFile",{"type":48,"value":12654}," bytes are copied at call time so the caller's buffer is immediately reusable. Reads deliver drain-scratch bytes — copy what the model keeps.",{"type":42,"tag":61,"props":12656,"children":12657},{},[12658,12660,12666,12668,12673,12674,12680,12681,12687,12688,12694,12696,12702,12704,12709,12711,12717],{"type":48,"value":12659},"In the fake executor: ",{"type":42,"tag":65,"props":12661,"children":12663},{"className":12662},[],[12664],{"type":48,"value":12665},"pendingFileAt(0)",{"type":48,"value":12667}," records ",{"type":42,"tag":65,"props":12669,"children":12671},{"className":12670},[],[12672],{"type":48,"value":3595},{"type":48,"value":456},{"type":42,"tag":65,"props":12675,"children":12677},{"className":12676},[],[12678],{"type":48,"value":12679},"op",{"type":48,"value":456},{"type":42,"tag":65,"props":12682,"children":12684},{"className":12683},[],[12685],{"type":48,"value":12686},"path",{"type":48,"value":456},{"type":42,"tag":65,"props":12689,"children":12691},{"className":12690},[],[12692],{"type":48,"value":12693},"bytes",{"type":48,"value":12695}," for assertions; ",{"type":42,"tag":65,"props":12697,"children":12699},{"className":12698},[],[12700],{"type":48,"value":12701},"feedFileResult(key, .ok, \"{...}\")",{"type":48,"value":12703}," answers a read (over-bound content is cut and rewritten to ",{"type":42,"tag":65,"props":12705,"children":12707},{"className":12706},[],[12708],{"type":48,"value":12612},{"type":48,"value":12710},", mirroring the real reader), ",{"type":42,"tag":65,"props":12712,"children":12714},{"className":12713},[],[12715],{"type":48,"value":12716},"feedFileResult(key, .ok, \"\")",{"type":48,"value":12718}," acknowledges a write; failure outcomes pass through as fed.",{"type":42,"tag":51,"props":12720,"children":12721},{},[12722,12727,12728,12733,12735,12740,12741,12747,12748,12754],{"type":42,"tag":65,"props":12723,"children":12725},{"className":12724},[],[12726],{"type":48,"value":11129},{"type":48,"value":7747},{"type":42,"tag":65,"props":12729,"children":12731},{"className":12730},[],[12732],{"type":48,"value":11136},{"type":48,"value":12734}," put text on (and read it from) the system clipboard through the platform pasteboard — the same seam the runtime's cmd+C copy uses. Never spawn ",{"type":42,"tag":65,"props":12736,"children":12738},{"className":12737},[],[12739],{"type":48,"value":11144},{"type":48,"value":456},{"type":42,"tag":65,"props":12742,"children":12744},{"className":12743},[],[12745],{"type":48,"value":12746},"pbpaste",{"type":48,"value":456},{"type":42,"tag":65,"props":12749,"children":12751},{"className":12750},[],[12752],{"type":48,"value":12753},"xclip",{"type":48,"value":12755}," for this. Same discipline: key-based (shared key space and 16 slots), exactly one terminal Msg with an explicit outcome. The pasteboard call is synchronous on the loop thread (no worker), but the result still arrives as an ordinary Msg on the next drain:",{"type":42,"tag":219,"props":12757,"children":12759},{"className":221,"code":12758,"language":18,"meta":223,"style":223},"pub const Msg = union(enum) {\n    share,\n    copied: native_sdk.EffectClipboardResult,   \u002F\u002F the fixed payload type\n};\n\n.share => fx.writeClipboard(.{\n    .key = share_key,\n    .text = model.shareLine(),             \u002F\u002F ≤ 64 KiB, copied at call time\n    .on_result = Effects.clipboardMsg(.copied),\n}),\n.copied => |result| model.noteCopied(result.outcome),\n",[12760],{"type":42,"tag":65,"props":12761,"children":12762},{"__ignoreMap":223},[12763,12770,12778,12786,12793,12800,12808,12816,12824,12832,12839],{"type":42,"tag":229,"props":12764,"children":12765},{"class":231,"line":232},[12766],{"type":42,"tag":229,"props":12767,"children":12768},{},[12769],{"type":48,"value":11384},{"type":42,"tag":229,"props":12771,"children":12772},{"class":231,"line":241},[12773],{"type":42,"tag":229,"props":12774,"children":12775},{},[12776],{"type":48,"value":12777},"    share,\n",{"type":42,"tag":229,"props":12779,"children":12780},{"class":231,"line":251},[12781],{"type":42,"tag":229,"props":12782,"children":12783},{},[12784],{"type":48,"value":12785},"    copied: native_sdk.EffectClipboardResult,   \u002F\u002F the fixed payload type\n",{"type":42,"tag":229,"props":12787,"children":12788},{"class":231,"line":260},[12789],{"type":42,"tag":229,"props":12790,"children":12791},{},[12792],{"type":48,"value":669},{"type":42,"tag":229,"props":12794,"children":12795},{"class":231,"line":269},[12796],{"type":42,"tag":229,"props":12797,"children":12798},{"emptyLinePlaceholder":245},[12799],{"type":48,"value":248},{"type":42,"tag":229,"props":12801,"children":12802},{"class":231,"line":278},[12803],{"type":42,"tag":229,"props":12804,"children":12805},{},[12806],{"type":48,"value":12807},".share => fx.writeClipboard(.{\n",{"type":42,"tag":229,"props":12809,"children":12810},{"class":231,"line":287},[12811],{"type":42,"tag":229,"props":12812,"children":12813},{},[12814],{"type":48,"value":12815},"    .key = share_key,\n",{"type":42,"tag":229,"props":12817,"children":12818},{"class":231,"line":296},[12819],{"type":42,"tag":229,"props":12820,"children":12821},{},[12822],{"type":48,"value":12823},"    .text = model.shareLine(),             \u002F\u002F ≤ 64 KiB, copied at call time\n",{"type":42,"tag":229,"props":12825,"children":12826},{"class":231,"line":305},[12827],{"type":42,"tag":229,"props":12828,"children":12829},{},[12830],{"type":48,"value":12831},"    .on_result = Effects.clipboardMsg(.copied),\n",{"type":42,"tag":229,"props":12833,"children":12834},{"class":231,"line":314},[12835],{"type":42,"tag":229,"props":12836,"children":12837},{},[12838],{"type":48,"value":11486},{"type":42,"tag":229,"props":12840,"children":12841},{"class":231,"line":323},[12842],{"type":42,"tag":229,"props":12843,"children":12844},{},[12845],{"type":48,"value":12846},".copied => |result| model.noteCopied(result.outcome),\n",{"type":42,"tag":51,"props":12848,"children":12849},{},[12850],{"type":48,"value":12851},"Clipboard rules:",{"type":42,"tag":57,"props":12853,"children":12854},{},[12855,12908,12920],{"type":42,"tag":61,"props":12856,"children":12857},{},[12858,12863,12864,12869,12871,12877,12879,12885,12887,12893,12895,12900,12902,12907],{"type":42,"tag":65,"props":12859,"children":12861},{"className":12860},[],[12862],{"type":48,"value":12573},{"type":48,"value":12575},{"type":42,"tag":65,"props":12865,"children":12867},{"className":12866},[],[12868],{"type":48,"value":12024},{"type":48,"value":12870}," (a write is on the clipboard whole; a read's content is in ",{"type":42,"tag":65,"props":12872,"children":12874},{"className":12873},[],[12875],{"type":48,"value":12876},"result.text",{"type":48,"value":12878}," — drain scratch, copy what the model keeps), ",{"type":42,"tag":65,"props":12880,"children":12882},{"className":12881},[],[12883],{"type":48,"value":12884},".failed",{"type":48,"value":12886}," (the platform refused: no clipboard service on the host, read content over the 64 KiB ",{"type":42,"tag":65,"props":12888,"children":12890},{"className":12889},[],[12891],{"type":48,"value":12892},"max_effect_clipboard_bytes",{"type":48,"value":12894},", pasteboard error — a read never arrives cut), ",{"type":42,"tag":65,"props":12896,"children":12898},{"className":12897},[],[12899],{"type":48,"value":11566},{"type":48,"value":12901}," (never ran: slots busy, duplicate key, write text over the bound), ",{"type":42,"tag":65,"props":12903,"children":12905},{"className":12904},[],[12906],{"type":48,"value":11631},{"type":48,"value":123},{"type":42,"tag":61,"props":12909,"children":12910},{},[12911,12913,12919],{"type":48,"value":12912},"Writes are text\u002Fplain and replace the clipboard whole; rich-data clipboard stays on the runtime API (",{"type":42,"tag":65,"props":12914,"children":12916},{"className":12915},[],[12917],{"type":48,"value":12918},"runtime.writeClipboardData",{"type":48,"value":7386},{"type":42,"tag":61,"props":12921,"children":12922},{},[12923,12924,12930,12931,12936,12937,12942,12943,12948,12949,12955,12957,12963,12965,12971],{"type":48,"value":12659},{"type":42,"tag":65,"props":12925,"children":12927},{"className":12926},[],[12928],{"type":48,"value":12929},"pendingClipboardAt(0)",{"type":48,"value":12667},{"type":42,"tag":65,"props":12932,"children":12934},{"className":12933},[],[12935],{"type":48,"value":3595},{"type":48,"value":456},{"type":42,"tag":65,"props":12938,"children":12940},{"className":12939},[],[12941],{"type":48,"value":12679},{"type":48,"value":456},{"type":42,"tag":65,"props":12944,"children":12946},{"className":12945},[],[12947],{"type":48,"value":48},{"type":48,"value":12695},{"type":42,"tag":65,"props":12950,"children":12952},{"className":12951},[],[12953],{"type":48,"value":12954},"feedClipboardResult(key, .ok, \"pasted\")",{"type":48,"value":12956}," answers a read, ",{"type":42,"tag":65,"props":12958,"children":12960},{"className":12959},[],[12961],{"type":48,"value":12962},"feedClipboardResult(key, .ok, \"\")",{"type":48,"value":12964}," acknowledges a write; failure outcomes pass through as fed. Under the real executor the test harness's null platform records the write — assert ",{"type":42,"tag":65,"props":12966,"children":12968},{"className":12967},[],[12969],{"type":48,"value":12970},"harness.null_platform.lastClipboardData()",{"type":48,"value":123},{"type":42,"tag":51,"props":12973,"children":12974},{},[12975,12981,12982,12988,12990,12996,12998,13004],{"type":42,"tag":65,"props":12976,"children":12978},{"className":12977},[],[12979],{"type":48,"value":12980},"fx.startTimer",{"type":48,"value":7747},{"type":42,"tag":65,"props":12983,"children":12985},{"className":12984},[],[12986],{"type":48,"value":12987},"fx.cancelTimer",{"type":48,"value":12989}," are key-based timers on the same channel — an auto-refresh, a poll, a debounce — one-shot or repeating, each fire delivered as one ",{"type":42,"tag":65,"props":12991,"children":12993},{"className":12992},[],[12994],{"type":48,"value":12995},"on_fire",{"type":48,"value":12997}," Msg. Timers are their own fixed table (16, ",{"type":42,"tag":65,"props":12999,"children":13001},{"className":13000},[],[13002],{"type":48,"value":13003},"max_effect_timers",{"type":48,"value":13005},") and their own key namespace: they consume none of the 16 effect slots and never collide with spawn\u002Ffetch\u002Ffile keys:",{"type":42,"tag":219,"props":13007,"children":13009},{"className":221,"code":13008,"language":18,"meta":223,"style":223},"pub const Msg = union(enum) {\n    tick: native_sdk.EffectTimer,    \u002F\u002F the fixed payload type\n    ...\n};\n\nfx.startTimer(.{\n    .key = refresh_key,\n    .interval_ms = 30_000,\n    .mode = .repeating,               \u002F\u002F .one_shot (default) fires once, then retires\n    .on_fire = Effects.timerMsg(.tick),\n}),\n.tick => |timer| switch (timer.outcome) {\n    .fired => model.refresh(),        \u002F\u002F timer.timestamp_ns is the platform fire time\n    .rejected => model.noteTimerRejected(timer.key),\n},\n",[13010],{"type":42,"tag":65,"props":13011,"children":13012},{"__ignoreMap":223},[13013,13020,13028,13036,13043,13050,13058,13066,13074,13082,13090,13097,13105,13113,13121],{"type":42,"tag":229,"props":13014,"children":13015},{"class":231,"line":232},[13016],{"type":42,"tag":229,"props":13017,"children":13018},{},[13019],{"type":48,"value":11384},{"type":42,"tag":229,"props":13021,"children":13022},{"class":231,"line":241},[13023],{"type":42,"tag":229,"props":13024,"children":13025},{},[13026],{"type":48,"value":13027},"    tick: native_sdk.EffectTimer,    \u002F\u002F the fixed payload type\n",{"type":42,"tag":229,"props":13029,"children":13030},{"class":231,"line":251},[13031],{"type":42,"tag":229,"props":13032,"children":13033},{},[13034],{"type":48,"value":13035},"    ...\n",{"type":42,"tag":229,"props":13037,"children":13038},{"class":231,"line":260},[13039],{"type":42,"tag":229,"props":13040,"children":13041},{},[13042],{"type":48,"value":669},{"type":42,"tag":229,"props":13044,"children":13045},{"class":231,"line":269},[13046],{"type":42,"tag":229,"props":13047,"children":13048},{"emptyLinePlaceholder":245},[13049],{"type":48,"value":248},{"type":42,"tag":229,"props":13051,"children":13052},{"class":231,"line":278},[13053],{"type":42,"tag":229,"props":13054,"children":13055},{},[13056],{"type":48,"value":13057},"fx.startTimer(.{\n",{"type":42,"tag":229,"props":13059,"children":13060},{"class":231,"line":287},[13061],{"type":42,"tag":229,"props":13062,"children":13063},{},[13064],{"type":48,"value":13065},"    .key = refresh_key,\n",{"type":42,"tag":229,"props":13067,"children":13068},{"class":231,"line":296},[13069],{"type":42,"tag":229,"props":13070,"children":13071},{},[13072],{"type":48,"value":13073},"    .interval_ms = 30_000,\n",{"type":42,"tag":229,"props":13075,"children":13076},{"class":231,"line":305},[13077],{"type":42,"tag":229,"props":13078,"children":13079},{},[13080],{"type":48,"value":13081},"    .mode = .repeating,               \u002F\u002F .one_shot (default) fires once, then retires\n",{"type":42,"tag":229,"props":13083,"children":13084},{"class":231,"line":314},[13085],{"type":42,"tag":229,"props":13086,"children":13087},{},[13088],{"type":48,"value":13089},"    .on_fire = Effects.timerMsg(.tick),\n",{"type":42,"tag":229,"props":13091,"children":13092},{"class":231,"line":323},[13093],{"type":42,"tag":229,"props":13094,"children":13095},{},[13096],{"type":48,"value":11486},{"type":42,"tag":229,"props":13098,"children":13099},{"class":231,"line":332},[13100],{"type":42,"tag":229,"props":13101,"children":13102},{},[13103],{"type":48,"value":13104},".tick => |timer| switch (timer.outcome) {\n",{"type":42,"tag":229,"props":13106,"children":13107},{"class":231,"line":341},[13108],{"type":42,"tag":229,"props":13109,"children":13110},{},[13111],{"type":48,"value":13112},"    .fired => model.refresh(),        \u002F\u002F timer.timestamp_ns is the platform fire time\n",{"type":42,"tag":229,"props":13114,"children":13115},{"class":231,"line":350},[13116],{"type":42,"tag":229,"props":13117,"children":13118},{},[13119],{"type":48,"value":13120},"    .rejected => model.noteTimerRejected(timer.key),\n",{"type":42,"tag":229,"props":13122,"children":13123},{"class":231,"line":359},[13124],{"type":42,"tag":229,"props":13125,"children":13126},{},[13127],{"type":48,"value":13128},"},\n",{"type":42,"tag":51,"props":13130,"children":13131},{},[13132,13134,13139,13141,13147,13149,13155,13157,13162,13163,13169,13170,13175,13176,13181,13182,13188,13189,13195,13197,13203,13205,13211,13213,13219,13220,13226,13227,13233,13235,13241,13243,13248,13250,13256],{"type":48,"value":13133},"Timer rules: starting a key that is already an active timer REPLACES it (interval\u002Fmode\u002F",{"type":42,"tag":65,"props":13135,"children":13137},{"className":13136},[],[13138],{"type":48,"value":12995},{"type":48,"value":13140}," update in place — the friendly behavior for an auto-refresh whose cadence changes); ",{"type":42,"tag":65,"props":13142,"children":13144},{"className":13143},[],[13145],{"type":48,"value":13146},"fx.cancelTimer(key)",{"type":48,"value":13148}," stops it, unknown keys are a no-op; rejection is never silent — a full timer table, a zero ",{"type":42,"tag":65,"props":13150,"children":13152},{"className":13151},[],[13153],{"type":48,"value":13154},"interval_ms",{"type":48,"value":13156},", or a platform without a timer service delivers exactly one Msg with outcome ",{"type":42,"tag":65,"props":13158,"children":13160},{"className":13159},[],[13161],{"type":48,"value":11566},{"type":48,"value":12338},{"type":42,"tag":65,"props":13164,"children":13166},{"className":13165},[],[13167],{"type":48,"value":13168},"pendingTimerAt(0)",{"type":48,"value":12667},{"type":42,"tag":65,"props":13171,"children":13173},{"className":13172},[],[13174],{"type":48,"value":3595},{"type":48,"value":456},{"type":42,"tag":65,"props":13177,"children":13179},{"className":13178},[],[13180],{"type":48,"value":13154},{"type":48,"value":456},{"type":42,"tag":65,"props":13183,"children":13185},{"className":13184},[],[13186],{"type":48,"value":13187},"mode",{"type":48,"value":2555},{"type":42,"tag":65,"props":13190,"children":13192},{"className":13191},[],[13193],{"type":48,"value":13194},"fireTimer(key)",{"type":48,"value":13196}," fires by hand (one-shot slots retire after the fire), draining through the same ",{"type":42,"tag":65,"props":13198,"children":13200},{"className":13199},[],[13201],{"type":48,"value":13202},".wake",{"type":48,"value":13204}," path as ",{"type":42,"tag":65,"props":13206,"children":13208},{"className":13207},[],[13209],{"type":48,"value":13210},"feedExit",{"type":48,"value":13212},". The mode enum is public as ",{"type":42,"tag":65,"props":13214,"children":13216},{"className":13215},[],[13217],{"type":48,"value":13218},"native_sdk.TimerMode",{"type":48,"value":2620},{"type":42,"tag":65,"props":13221,"children":13223},{"className":13222},[],[13224],{"type":48,"value":13225},".one_shot",{"type":48,"value":456},{"type":42,"tag":65,"props":13228,"children":13230},{"className":13229},[],[13231],{"type":48,"value":13232},".repeating",{"type":48,"value":13234},") — asserting a recorded request's mode must qualify it (",{"type":42,"tag":65,"props":13236,"children":13238},{"className":13237},[],[13239],{"type":48,"value":13240},"try testing.expectEqual(native_sdk.TimerMode.repeating, request.mode)",{"type":48,"value":13242},"; a bare ",{"type":42,"tag":65,"props":13244,"children":13246},{"className":13245},[],[13247],{"type":48,"value":13232},{"type":48,"value":13249}," as ",{"type":42,"tag":65,"props":13251,"children":13253},{"className":13252},[],[13254],{"type":48,"value":13255},"expectEqual",{"type":48,"value":13257},"'s first argument does not infer).",{"type":42,"tag":51,"props":13259,"children":13260},{},[13261,13267,13269,13275,13276,13282,13283,13289,13290,13296,13297,13303,13305,13310,13312,13318,13320,13326,13328,13334,13336,13342,13344,13350],{"type":42,"tag":65,"props":13262,"children":13264},{"className":13263},[],[13265],{"type":48,"value":13266},"fx.playAudio",{"type":48,"value":13268}," plays a track through the platform's single audio player (macOS: AVAudioPlayer for local files, AVPlayer for streamed URLs, both in the AppKit host), and the transport rides the same channel: ",{"type":42,"tag":65,"props":13270,"children":13272},{"className":13271},[],[13273],{"type":48,"value":13274},"fx.pauseAudio()",{"type":48,"value":7747},{"type":42,"tag":65,"props":13277,"children":13279},{"className":13278},[],[13280],{"type":48,"value":13281},"fx.resumeAudio()",{"type":48,"value":7747},{"type":42,"tag":65,"props":13284,"children":13286},{"className":13285},[],[13287],{"type":48,"value":13288},"fx.stopAudio()",{"type":48,"value":7747},{"type":42,"tag":65,"props":13291,"children":13293},{"className":13292},[],[13294],{"type":48,"value":13295},"fx.seekAudio(position_ms)",{"type":48,"value":7747},{"type":42,"tag":65,"props":13298,"children":13300},{"className":13299},[],[13301],{"type":48,"value":13302},"fx.setAudioVolume(0.0—1.0)",{"type":48,"value":13304},". Sources resolve in a fixed order — the local ",{"type":42,"tag":65,"props":13306,"children":13308},{"className":13307},[],[13309],{"type":48,"value":12686},{"type":48,"value":13311}," first; when it is absent or missing, the ",{"type":42,"tag":65,"props":13313,"children":13315},{"className":13314},[],[13316],{"type":48,"value":13317},"url",{"type":48,"value":13319},", where a verified entry at ",{"type":42,"tag":65,"props":13321,"children":13323},{"className":13322},[],[13324],{"type":48,"value":13325},"cache_path",{"type":48,"value":13327}," plays locally (no network) and anything else STREAMS progressively (audible before the download finishes) while the bytes fill the cache for next time (",{"type":42,"tag":65,"props":13329,"children":13331},{"className":13330},[],[13332],{"type":48,"value":13333},"expected_bytes",{"type":48,"value":13335},", the track's known size from a manifest, is the integrity gate: partial or stale cache entries never play). One player is the whole surface — a new ",{"type":42,"tag":65,"props":13337,"children":13339},{"className":13338},[],[13340],{"type":48,"value":13341},"playAudio",{"type":48,"value":13343}," replaces whatever played before, exactly like a music app switching tracks. The audio key is its own namespace (like timer keys) and consumes none of the 16 effect slots; every report arrives as one ",{"type":42,"tag":65,"props":13345,"children":13347},{"className":13346},[],[13348],{"type":48,"value":13349},"on_event",{"type":48,"value":13351}," Msg:",{"type":42,"tag":219,"props":13353,"children":13355},{"className":221,"code":13354,"language":18,"meta":223,"style":223},"pub const Msg = union(enum) {\n    audio_event: native_sdk.EffectAudio,   \u002F\u002F the fixed payload type\n    ...\n};\n\n.play_track => |track| fx.playAudio(.{\n    .key = track.id,                        \u002F\u002F echoed in every event\n    .path = track.path,                     \u002F\u002F local file, tried first\n    .url = track.url,                       \u002F\u002F optional http(s) fallback: cache hit or stream\n    .cache_path = track.cache_path,         \u002F\u002F empty disables caching (stream-only)\n    .expected_bytes = track.bytes,          \u002F\u002F cache integrity gate (0 = unknown)\n    .on_event = Effects.audioMsg(.audio_event),\n}),\n.audio_event => |event| switch (event.kind) {\n    .loaded => model.duration_ms = event.duration_ms,   \u002F\u002F the real decoded duration\n    .position => model.elapsed_ms = event.position_ms,  \u002F\u002F ~every 500ms while playing\n    .completed => model.playNext(fx),                   \u002F\u002F exactly once, at natural end\n    .failed, .rejected => model.noteAudioUnavailable(), \u002F\u002F never a crash, never silence\n},\n",[13356],{"type":42,"tag":65,"props":13357,"children":13358},{"__ignoreMap":223},[13359,13366,13374,13381,13388,13395,13403,13411,13419,13427,13435,13443,13451,13458,13466,13474,13482,13490,13498],{"type":42,"tag":229,"props":13360,"children":13361},{"class":231,"line":232},[13362],{"type":42,"tag":229,"props":13363,"children":13364},{},[13365],{"type":48,"value":11384},{"type":42,"tag":229,"props":13367,"children":13368},{"class":231,"line":241},[13369],{"type":42,"tag":229,"props":13370,"children":13371},{},[13372],{"type":48,"value":13373},"    audio_event: native_sdk.EffectAudio,   \u002F\u002F the fixed payload type\n",{"type":42,"tag":229,"props":13375,"children":13376},{"class":231,"line":251},[13377],{"type":42,"tag":229,"props":13378,"children":13379},{},[13380],{"type":48,"value":13035},{"type":42,"tag":229,"props":13382,"children":13383},{"class":231,"line":260},[13384],{"type":42,"tag":229,"props":13385,"children":13386},{},[13387],{"type":48,"value":669},{"type":42,"tag":229,"props":13389,"children":13390},{"class":231,"line":269},[13391],{"type":42,"tag":229,"props":13392,"children":13393},{"emptyLinePlaceholder":245},[13394],{"type":48,"value":248},{"type":42,"tag":229,"props":13396,"children":13397},{"class":231,"line":278},[13398],{"type":42,"tag":229,"props":13399,"children":13400},{},[13401],{"type":48,"value":13402},".play_track => |track| fx.playAudio(.{\n",{"type":42,"tag":229,"props":13404,"children":13405},{"class":231,"line":287},[13406],{"type":42,"tag":229,"props":13407,"children":13408},{},[13409],{"type":48,"value":13410},"    .key = track.id,                        \u002F\u002F echoed in every event\n",{"type":42,"tag":229,"props":13412,"children":13413},{"class":231,"line":296},[13414],{"type":42,"tag":229,"props":13415,"children":13416},{},[13417],{"type":48,"value":13418},"    .path = track.path,                     \u002F\u002F local file, tried first\n",{"type":42,"tag":229,"props":13420,"children":13421},{"class":231,"line":305},[13422],{"type":42,"tag":229,"props":13423,"children":13424},{},[13425],{"type":48,"value":13426},"    .url = track.url,                       \u002F\u002F optional http(s) fallback: cache hit or stream\n",{"type":42,"tag":229,"props":13428,"children":13429},{"class":231,"line":314},[13430],{"type":42,"tag":229,"props":13431,"children":13432},{},[13433],{"type":48,"value":13434},"    .cache_path = track.cache_path,         \u002F\u002F empty disables caching (stream-only)\n",{"type":42,"tag":229,"props":13436,"children":13437},{"class":231,"line":323},[13438],{"type":42,"tag":229,"props":13439,"children":13440},{},[13441],{"type":48,"value":13442},"    .expected_bytes = track.bytes,          \u002F\u002F cache integrity gate (0 = unknown)\n",{"type":42,"tag":229,"props":13444,"children":13445},{"class":231,"line":332},[13446],{"type":42,"tag":229,"props":13447,"children":13448},{},[13449],{"type":48,"value":13450},"    .on_event = Effects.audioMsg(.audio_event),\n",{"type":42,"tag":229,"props":13452,"children":13453},{"class":231,"line":341},[13454],{"type":42,"tag":229,"props":13455,"children":13456},{},[13457],{"type":48,"value":11486},{"type":42,"tag":229,"props":13459,"children":13460},{"class":231,"line":350},[13461],{"type":42,"tag":229,"props":13462,"children":13463},{},[13464],{"type":48,"value":13465},".audio_event => |event| switch (event.kind) {\n",{"type":42,"tag":229,"props":13467,"children":13468},{"class":231,"line":359},[13469],{"type":42,"tag":229,"props":13470,"children":13471},{},[13472],{"type":48,"value":13473},"    .loaded => model.duration_ms = event.duration_ms,   \u002F\u002F the real decoded duration\n",{"type":42,"tag":229,"props":13475,"children":13476},{"class":231,"line":368},[13477],{"type":42,"tag":229,"props":13478,"children":13479},{},[13480],{"type":48,"value":13481},"    .position => model.elapsed_ms = event.position_ms,  \u002F\u002F ~every 500ms while playing\n",{"type":42,"tag":229,"props":13483,"children":13484},{"class":231,"line":377},[13485],{"type":42,"tag":229,"props":13486,"children":13487},{},[13488],{"type":48,"value":13489},"    .completed => model.playNext(fx),                   \u002F\u002F exactly once, at natural end\n",{"type":42,"tag":229,"props":13491,"children":13492},{"class":231,"line":386},[13493],{"type":42,"tag":229,"props":13494,"children":13495},{},[13496],{"type":48,"value":13497},"    .failed, .rejected => model.noteAudioUnavailable(), \u002F\u002F never a crash, never silence\n",{"type":42,"tag":229,"props":13499,"children":13500},{"class":231,"line":395},[13501],{"type":42,"tag":229,"props":13502,"children":13503},{},[13504],{"type":48,"value":13128},{"type":42,"tag":51,"props":13506,"children":13507},{},[13508,13510,13516,13518,13524,13526,13532,13534,13540,13542,13547,13549,13554,13556,13561,13563,13569,13571,13577,13579,13584,13586,13592,13594,13600,13602,13608,13610,13616,13618,13624,13626,13632,13634,13640,13642,13648,13650,13655,13656,13661,13662,13667,13668,13673,13674,13679,13680,13686,13687,13693,13694,13700,13702,13707,13709,13715,13717,13723,13725,13731,13733,13739,13740,13746,13747,13753,13755,13761,13763,13769,13771,13776],{"type":48,"value":13509},"Audio rules: ",{"type":42,"tag":65,"props":13511,"children":13513},{"className":13512},[],[13514],{"type":48,"value":13515},"event.kind",{"type":48,"value":13517}," is explicit — ",{"type":42,"tag":65,"props":13519,"children":13521},{"className":13520},[],[13522],{"type":48,"value":13523},".loaded",{"type":48,"value":13525}," acknowledges a successful load (position ticks and ",{"type":42,"tag":65,"props":13527,"children":13529},{"className":13528},[],[13530],{"type":48,"value":13531},".completed",{"type":48,"value":13533}," follow only after it), ",{"type":42,"tag":65,"props":13535,"children":13537},{"className":13536},[],[13538],{"type":48,"value":13539},".position",{"type":48,"value":13541}," is a coarse honest readout (~500ms cadence — a readout, not a frame clock; drive animations from your model, not from tick density), ",{"type":42,"tag":65,"props":13543,"children":13545},{"className":13544},[],[13546],{"type":48,"value":13531},{"type":48,"value":13548}," fires exactly once with position pinned to the duration, ",{"type":42,"tag":65,"props":13550,"children":13552},{"className":13551},[],[13553],{"type":48,"value":12884},{"type":48,"value":13555}," reports an unreadable file with no url fallback, an async decode error, a network failure mid-stream (or offline with a cold cache), or a platform without audio playback (GTK\u002FWin32 today — named unsupported, not half-implemented), ",{"type":42,"tag":65,"props":13557,"children":13559},{"className":13558},[],[13560],{"type":48,"value":11566},{"type":48,"value":13562}," reports loop-side validation (path and url both empty, or any string over ",{"type":42,"tag":65,"props":13564,"children":13566},{"className":13565},[],[13567],{"type":48,"value":13568},"max_effect_audio_path_bytes",{"type":48,"value":13570},"). ",{"type":42,"tag":65,"props":13572,"children":13574},{"className":13573},[],[13575],{"type":48,"value":13576},"event.buffering",{"type":48,"value":13578}," is the stream's honest stall flag — true while an un-paused stream waits for network bytes (local files and cache hits never buffer); show it as its own UI state, distinct from playing and paused. All payload fields are plain data — safe to store in the model, no drain-scratch copying. Pause\u002Fstop\u002Fseek\u002Fvolume never echo events (the caller commanded them); seek and volume work mid-stream; volume is remembered across tracks. The streamed bytes cache at ",{"type":42,"tag":65,"props":13580,"children":13582},{"className":13581},[],[13583],{"type":48,"value":13325},{"type":48,"value":13585}," — key it by URL hash with ",{"type":42,"tag":65,"props":13587,"children":13589},{"className":13588},[],[13590],{"type":48,"value":13591},"native_sdk.audioCachePath(&buffer, cache_dir, url)",{"type":48,"value":13593}," under the app_dirs ",{"type":42,"tag":65,"props":13595,"children":13597},{"className":13596},[],[13598],{"type":48,"value":13599},".cache",{"type":48,"value":13601}," directory (macOS ",{"type":42,"tag":65,"props":13603,"children":13605},{"className":13604},[],[13606],{"type":48,"value":13607},"~\u002FLibrary\u002FCaches\u002F\u003Capp>\u002Faudio\u002F",{"type":48,"value":13609},", Linux ",{"type":42,"tag":65,"props":13611,"children":13613},{"className":13612},[],[13614],{"type":48,"value":13615},"$XDG_CACHE_HOME\u002F\u003Capp>\u002Faudio\u002F",{"type":48,"value":13617},"), so clearing the cache is deleting that one directory. The automation snapshot reports playback honestly (",{"type":42,"tag":65,"props":13619,"children":13621},{"className":13620},[],[13622],{"type":48,"value":13623},"audio key=... state=playing|paused|buffering source=local|cache|stream position_ms=... duration_ms=...",{"type":48,"value":13625},") — an advancing ",{"type":42,"tag":65,"props":13627,"children":13629},{"className":13628},[],[13630],{"type":48,"value":13631},"position_ms",{"type":48,"value":13633}," is the automation-visible evidence music is actually playing, and ",{"type":42,"tag":65,"props":13635,"children":13637},{"className":13636},[],[13638],{"type":48,"value":13639},"source=",{"type":48,"value":13641}," proves the resolution order. In the fake executor, ",{"type":42,"tag":65,"props":13643,"children":13645},{"className":13644},[],[13646],{"type":48,"value":13647},"pendingAudio()",{"type":48,"value":13649}," records the single channel's ",{"type":42,"tag":65,"props":13651,"children":13653},{"className":13652},[],[13654],{"type":48,"value":3595},{"type":48,"value":456},{"type":42,"tag":65,"props":13657,"children":13659},{"className":13658},[],[13660],{"type":48,"value":12686},{"type":48,"value":456},{"type":42,"tag":65,"props":13663,"children":13665},{"className":13664},[],[13666],{"type":48,"value":13317},{"type":48,"value":456},{"type":42,"tag":65,"props":13669,"children":13671},{"className":13670},[],[13672],{"type":48,"value":13325},{"type":48,"value":456},{"type":42,"tag":65,"props":13675,"children":13677},{"className":13676},[],[13678],{"type":48,"value":13333},{"type":48,"value":456},{"type":42,"tag":65,"props":13681,"children":13683},{"className":13682},[],[13684],{"type":48,"value":13685},"playing",{"type":48,"value":456},{"type":42,"tag":65,"props":13688,"children":13690},{"className":13689},[],[13691],{"type":48,"value":13692},"volume",{"type":48,"value":1820},{"type":42,"tag":65,"props":13695,"children":13697},{"className":13696},[],[13698],{"type":48,"value":13699},"feedAudioEvent(.position, 1_500, 89_160, true)",{"type":48,"value":13701}," feeds any event by hand (draining through the same ",{"type":42,"tag":65,"props":13703,"children":13705},{"className":13704},[],[13706],{"type":48,"value":13202},{"type":48,"value":13708}," path; ",{"type":42,"tag":65,"props":13710,"children":13712},{"className":13711},[],[13713],{"type":48,"value":13714},"feedAudioEventBuffering",{"type":48,"value":13716}," adds the stall flag), and ",{"type":42,"tag":65,"props":13718,"children":13720},{"className":13719},[],[13721],{"type":48,"value":13722},"audioSnapshot()",{"type":48,"value":13724}," exposes the mirrors; under the real executor the test harness's null platform is a deterministic fake player — ",{"type":42,"tag":65,"props":13726,"children":13728},{"className":13727},[],[13729],{"type":48,"value":13730},"setAudioDuration(suffix, ms)",{"type":48,"value":13732}," seeds durations, ",{"type":42,"tag":65,"props":13734,"children":13736},{"className":13735},[],[13737],{"type":48,"value":13738},"takeAudioLoaded()",{"type":48,"value":7747},{"type":42,"tag":65,"props":13741,"children":13743},{"className":13742},[],[13744],{"type":48,"value":13745},"advanceAudio(delta_ms)",{"type":48,"value":7747},{"type":42,"tag":65,"props":13748,"children":13750},{"className":13749},[],[13751],{"type":48,"value":13752},"stallAudio()",{"type":48,"value":13754}," synthesize the platform events a live host would deliver, position never advances on its own, ",{"type":42,"tag":65,"props":13756,"children":13758},{"className":13757},[],[13759],{"type":48,"value":13760},"audio_local_files = false",{"type":48,"value":13762}," models the assets-absent machine (local loads answer ",{"type":42,"tag":65,"props":13764,"children":13766},{"className":13765},[],[13767],{"type":48,"value":13768},"AudioSourceNotFound",{"type":48,"value":13770},", which is what sends the cascade to the url), and a streamed track that runs to completion flips into the fake cache so the next play of the same url resolves ",{"type":42,"tag":65,"props":13772,"children":13774},{"className":13773},[],[13775],{"type":48,"value":13599},{"type":48,"value":123},{"type":42,"tag":51,"props":13778,"children":13779},{},[13780,13786,13788,13793,13795,13801,13803,13809,13811,13816,13818,13824],{"type":42,"tag":65,"props":13781,"children":13783},{"className":13782},[],[13784],{"type":48,"value":13785},"fx.openChannel",{"type":48,"value":13787}," is the external-source seam — the one sanctioned way an app-owned thread (a socket reader, a file watcher, a sampling worker) feeds the UI. Reach for it when the data originates OUTSIDE update and arrives on its own schedule: open a channel under a caller-chosen ",{"type":42,"tag":65,"props":13789,"children":13791},{"className":13790},[],[13792],{"type":48,"value":4308},{"type":48,"value":13794}," key, hand the returned thread-safe ",{"type":42,"tag":65,"props":13796,"children":13798},{"className":13797},[],[13799],{"type":48,"value":13800},"ChannelHandle",{"type":48,"value":13802}," to the source thread, and every accepted ",{"type":42,"tag":65,"props":13804,"children":13806},{"className":13805},[],[13807],{"type":48,"value":13808},"handle.post(bytes)",{"type":48,"value":13810}," arrives as one ",{"type":42,"tag":65,"props":13812,"children":13814},{"className":13813},[],[13815],{"type":48,"value":13349},{"type":48,"value":13817}," Msg — no timer polling anywhere, the post itself wakes the loop. Channels are their own fixed table (8, ",{"type":42,"tag":65,"props":13819,"children":13821},{"className":13820},[],[13822],{"type":48,"value":13823},"max_effect_channels",{"type":48,"value":13825},") and consume none of the 16 effect slots, but the key shares the effect families' one key space (a same-key fetch is blocked while the channel lives):",{"type":42,"tag":219,"props":13827,"children":13829},{"className":221,"code":13828,"language":18,"meta":223,"style":223},"pub const Msg = union(enum) {\n    sample: native_sdk.EffectChannelEvent,   \u002F\u002F the fixed payload type\n    ...\n};\n\n.start => {\n    const handle = fx.openChannel(.{\n        .key = monitor_key,                   \u002F\u002F echoed in every event\n        .on_event = Effects.channelMsg(.sample),\n        .max_pending = 8,                     \u002F\u002F staging bound, clamped 1..32 (default 32)\n    });\n    \u002F\u002F Launch the source only for a LIVE handle — under session replay\n    \u002F\u002F the open parks and handle.live() answers false, so the producer\n    \u002F\u002F never starts (the journaled events are the whole stream either\n    \u002F\u002F way). Posting from the source thread is thread-safe and never\n    \u002F\u002F blocks (given the platform's conforming enqueue-only wake hook —\n    \u002F\u002F every first-party host; see PlatformServices.wake_fn):\n    \u002F\u002F handle.post(bytes) answers .accepted, .dropped_full,\n    \u002F\u002F .dropped_oversized, or .closed.\n    if (handle.live()) startSampler(handle);\n},\n.stop => fx.closeChannel(monitor_key),\n.sample => |event| switch (event.kind) {\n    .data => model.recordSample(event.bytes),                \u002F\u002F drain scratch — copy what the model keeps\n    .closed => model.noteMonitorStopped(event.dropped_total), \u002F\u002F exactly once, final totals aboard\n    .rejected => model.noteMonitorUnavailable(),              \u002F\u002F duplicate live key or no idle slot\n},\n",[13830],{"type":42,"tag":65,"props":13831,"children":13832},{"__ignoreMap":223},[13833,13840,13848,13855,13862,13869,13877,13885,13893,13901,13909,13916,13924,13932,13940,13948,13956,13964,13972,13980,13988,13995,14003,14012,14021,14030,14039],{"type":42,"tag":229,"props":13834,"children":13835},{"class":231,"line":232},[13836],{"type":42,"tag":229,"props":13837,"children":13838},{},[13839],{"type":48,"value":11384},{"type":42,"tag":229,"props":13841,"children":13842},{"class":231,"line":241},[13843],{"type":42,"tag":229,"props":13844,"children":13845},{},[13846],{"type":48,"value":13847},"    sample: native_sdk.EffectChannelEvent,   \u002F\u002F the fixed payload type\n",{"type":42,"tag":229,"props":13849,"children":13850},{"class":231,"line":251},[13851],{"type":42,"tag":229,"props":13852,"children":13853},{},[13854],{"type":48,"value":13035},{"type":42,"tag":229,"props":13856,"children":13857},{"class":231,"line":260},[13858],{"type":42,"tag":229,"props":13859,"children":13860},{},[13861],{"type":48,"value":669},{"type":42,"tag":229,"props":13863,"children":13864},{"class":231,"line":269},[13865],{"type":42,"tag":229,"props":13866,"children":13867},{"emptyLinePlaceholder":245},[13868],{"type":48,"value":248},{"type":42,"tag":229,"props":13870,"children":13871},{"class":231,"line":278},[13872],{"type":42,"tag":229,"props":13873,"children":13874},{},[13875],{"type":48,"value":13876},".start => {\n",{"type":42,"tag":229,"props":13878,"children":13879},{"class":231,"line":287},[13880],{"type":42,"tag":229,"props":13881,"children":13882},{},[13883],{"type":48,"value":13884},"    const handle = fx.openChannel(.{\n",{"type":42,"tag":229,"props":13886,"children":13887},{"class":231,"line":296},[13888],{"type":42,"tag":229,"props":13889,"children":13890},{},[13891],{"type":48,"value":13892},"        .key = monitor_key,                   \u002F\u002F echoed in every event\n",{"type":42,"tag":229,"props":13894,"children":13895},{"class":231,"line":305},[13896],{"type":42,"tag":229,"props":13897,"children":13898},{},[13899],{"type":48,"value":13900},"        .on_event = Effects.channelMsg(.sample),\n",{"type":42,"tag":229,"props":13902,"children":13903},{"class":231,"line":314},[13904],{"type":42,"tag":229,"props":13905,"children":13906},{},[13907],{"type":48,"value":13908},"        .max_pending = 8,                     \u002F\u002F staging bound, clamped 1..32 (default 32)\n",{"type":42,"tag":229,"props":13910,"children":13911},{"class":231,"line":323},[13912],{"type":42,"tag":229,"props":13913,"children":13914},{},[13915],{"type":48,"value":392},{"type":42,"tag":229,"props":13917,"children":13918},{"class":231,"line":332},[13919],{"type":42,"tag":229,"props":13920,"children":13921},{},[13922],{"type":48,"value":13923},"    \u002F\u002F Launch the source only for a LIVE handle — under session replay\n",{"type":42,"tag":229,"props":13925,"children":13926},{"class":231,"line":341},[13927],{"type":42,"tag":229,"props":13928,"children":13929},{},[13930],{"type":48,"value":13931},"    \u002F\u002F the open parks and handle.live() answers false, so the producer\n",{"type":42,"tag":229,"props":13933,"children":13934},{"class":231,"line":350},[13935],{"type":42,"tag":229,"props":13936,"children":13937},{},[13938],{"type":48,"value":13939},"    \u002F\u002F never starts (the journaled events are the whole stream either\n",{"type":42,"tag":229,"props":13941,"children":13942},{"class":231,"line":359},[13943],{"type":42,"tag":229,"props":13944,"children":13945},{},[13946],{"type":48,"value":13947},"    \u002F\u002F way). Posting from the source thread is thread-safe and never\n",{"type":42,"tag":229,"props":13949,"children":13950},{"class":231,"line":368},[13951],{"type":42,"tag":229,"props":13952,"children":13953},{},[13954],{"type":48,"value":13955},"    \u002F\u002F blocks (given the platform's conforming enqueue-only wake hook —\n",{"type":42,"tag":229,"props":13957,"children":13958},{"class":231,"line":377},[13959],{"type":42,"tag":229,"props":13960,"children":13961},{},[13962],{"type":48,"value":13963},"    \u002F\u002F every first-party host; see PlatformServices.wake_fn):\n",{"type":42,"tag":229,"props":13965,"children":13966},{"class":231,"line":386},[13967],{"type":42,"tag":229,"props":13968,"children":13969},{},[13970],{"type":48,"value":13971},"    \u002F\u002F handle.post(bytes) answers .accepted, .dropped_full,\n",{"type":42,"tag":229,"props":13973,"children":13974},{"class":231,"line":395},[13975],{"type":42,"tag":229,"props":13976,"children":13977},{},[13978],{"type":48,"value":13979},"    \u002F\u002F .dropped_oversized, or .closed.\n",{"type":42,"tag":229,"props":13981,"children":13982},{"class":231,"line":404},[13983],{"type":42,"tag":229,"props":13984,"children":13985},{},[13986],{"type":48,"value":13987},"    if (handle.live()) startSampler(handle);\n",{"type":42,"tag":229,"props":13989,"children":13990},{"class":231,"line":413},[13991],{"type":42,"tag":229,"props":13992,"children":13993},{},[13994],{"type":48,"value":13128},{"type":42,"tag":229,"props":13996,"children":13997},{"class":231,"line":422},[13998],{"type":42,"tag":229,"props":13999,"children":14000},{},[14001],{"type":48,"value":14002},".stop => fx.closeChannel(monitor_key),\n",{"type":42,"tag":229,"props":14004,"children":14006},{"class":231,"line":14005},23,[14007],{"type":42,"tag":229,"props":14008,"children":14009},{},[14010],{"type":48,"value":14011},".sample => |event| switch (event.kind) {\n",{"type":42,"tag":229,"props":14013,"children":14015},{"class":231,"line":14014},24,[14016],{"type":42,"tag":229,"props":14017,"children":14018},{},[14019],{"type":48,"value":14020},"    .data => model.recordSample(event.bytes),                \u002F\u002F drain scratch — copy what the model keeps\n",{"type":42,"tag":229,"props":14022,"children":14024},{"class":231,"line":14023},25,[14025],{"type":42,"tag":229,"props":14026,"children":14027},{},[14028],{"type":48,"value":14029},"    .closed => model.noteMonitorStopped(event.dropped_total), \u002F\u002F exactly once, final totals aboard\n",{"type":42,"tag":229,"props":14031,"children":14033},{"class":231,"line":14032},26,[14034],{"type":42,"tag":229,"props":14035,"children":14036},{},[14037],{"type":48,"value":14038},"    .rejected => model.noteMonitorUnavailable(),              \u002F\u002F duplicate live key or no idle slot\n",{"type":42,"tag":229,"props":14040,"children":14042},{"class":231,"line":14041},27,[14043],{"type":42,"tag":229,"props":14044,"children":14045},{},[14046],{"type":48,"value":13128},{"type":42,"tag":51,"props":14048,"children":14049},{},[14050,14052,14058,14060,14066,14068,14074,14076,14082,14084,14090,14092,14098,14100,14106,14108,14114,14116,14121,14123,14129,14130,14136,14138,14144,14146,14152,14154,14160,14162,14168,14170,14175,14177,14183,14185,14191,14193,14198,14200,14206,14208,14214],{"type":48,"value":14051},"Channel rules: ",{"type":42,"tag":65,"props":14053,"children":14055},{"className":14054},[],[14056],{"type":48,"value":14057},"handle.post",{"type":48,"value":14059}," answers a four-way ",{"type":42,"tag":65,"props":14061,"children":14063},{"className":14062},[],[14064],{"type":48,"value":14065},"PostResult",{"type":48,"value":14067},", and the producer contract reads directly off it — ",{"type":42,"tag":65,"props":14069,"children":14071},{"className":14070},[],[14072],{"type":48,"value":14073},".accepted",{"type":48,"value":14075}," (exactly one ",{"type":42,"tag":65,"props":14077,"children":14079},{"className":14078},[],[14080],{"type":48,"value":14081},".data",{"type":48,"value":14083}," event delivers on the next drain), ",{"type":42,"tag":65,"props":14085,"children":14087},{"className":14086},[],[14088],{"type":48,"value":14089},".dropped_full",{"type":48,"value":14091}," (transient back-pressure: skip this message and keep producing; the consumer's next drain relieves it), ",{"type":42,"tag":65,"props":14093,"children":14095},{"className":14094},[],[14096],{"type":48,"value":14097},".dropped_oversized",{"type":48,"value":14099}," (a programming error no retry fixes: bound your bytes at ",{"type":42,"tag":65,"props":14101,"children":14103},{"className":14102},[],[14104],{"type":48,"value":14105},"max_effect_channel_bytes",{"type":48,"value":14107},", 4 KiB), ",{"type":42,"tag":65,"props":14109,"children":14111},{"className":14110},[],[14112],{"type":48,"value":14113},".closed",{"type":48,"value":14115}," (the occupancy is over: exit the producer loop for good — the handle is generation-stamped, so posting after close, after the slot was reused, or after runtime teardown is always memory-safe and always ",{"type":42,"tag":65,"props":14117,"children":14119},{"className":14118},[],[14120],{"type":48,"value":14113},{"type":48,"value":14122},"). The back-pressure contract in one line: refused posts are never silent — they count into the event's ",{"type":42,"tag":65,"props":14124,"children":14126},{"className":14125},[],[14127],{"type":48,"value":14128},"dropped_pending",{"type":48,"value":456},{"type":42,"tag":65,"props":14131,"children":14133},{"className":14132},[],[14134],{"type":48,"value":14135},"dropped_total",{"type":48,"value":14137}," counters and ride the next delivered event, and a refused post never wakes the host, so a producer storming a full stage cannot grow the loop's queue. ",{"type":42,"tag":65,"props":14139,"children":14141},{"className":14140},[],[14142],{"type":48,"value":14143},"post",{"type":48,"value":14145}," never blocks its producer given a conforming host wake (the platform's ",{"type":42,"tag":65,"props":14147,"children":14149},{"className":14148},[],[14150],{"type":48,"value":14151},"wake_fn",{"type":48,"value":14153}," is contractually a bounded, enqueue-only nudge — every first-party host conforms; see ",{"type":42,"tag":65,"props":14155,"children":14157},{"className":14156},[],[14158],{"type":48,"value":14159},"PlatformServices.wake_fn",{"type":48,"value":14161},"), and the runtime holds no channel lock across the wake, so even a violating embedder hook hangs only its own posting thread. The replay story, honestly: channel events journal at the effect boundary, so a recorded session replays the whole stream from the journal and never NEEDS the source — under replay ",{"type":42,"tag":65,"props":14163,"children":14165},{"className":14164},[],[14166],{"type":48,"value":14167},"openChannel",{"type":48,"value":14169}," parks the occupancy and returns an inert handle whose every post answers ",{"type":42,"tag":65,"props":14171,"children":14173},{"className":14172},[],[14174],{"type":48,"value":14113},{"type":48,"value":14176},". But the opening update re-executes (app code is app code), so a producer launched unconditionally really starts — connects and blocking setup before its first post included — and is only stopped AT that first post; ",{"type":42,"tag":65,"props":14178,"children":14180},{"className":14179},[],[14181],{"type":48,"value":14182},"handle.live()",{"type":48,"value":14184}," is the producer-launch check (false for parked replay handles, refused opens, and closed occupancies — advisory only, the post's own answer stays authoritative), so a producer gated on it never starts and replay stays fully offline. ",{"type":42,"tag":65,"props":14186,"children":14188},{"className":14187},[],[14189],{"type":48,"value":14190},"fx.closeChannel(key)",{"type":48,"value":14192}," flushes the staged backlog, delivers the one ",{"type":42,"tag":65,"props":14194,"children":14196},{"className":14195},[],[14197],{"type":48,"value":14113},{"type":48,"value":14199}," terminal, and frees the key at that delivery; ",{"type":42,"tag":65,"props":14201,"children":14203},{"className":14202},[],[14204],{"type":48,"value":14205},"fx.channelHandle(key)",{"type":48,"value":14207}," re-resolves the open channel's handle loop-side. See ",{"type":42,"tag":65,"props":14209,"children":14211},{"className":14210},[],[14212],{"type":48,"value":14213},"examples\u002Fchannel-monitor",{"type":48,"value":14215}," for the complete worker-loop pattern, including the wind-down discipline.",{"type":42,"tag":51,"props":14217,"children":14218},{},[14219],{"type":48,"value":14220},"Test effects with the fake executor — deterministic, no processes, no network:",{"type":42,"tag":219,"props":14222,"children":14224},{"className":221,"code":14223,"language":18,"meta":223,"style":223},"app_state.effects.executor = .fake;             \u002F\u002F before dispatching (and before\n                                                \u002F\u002F the first frame if using init_fx —\n                                                \u002F\u002F the boot spawn is then recorded too)\ntry app_state.dispatch(&harness.runtime, 1, .start);\nconst request = app_state.effects.pendingSpawnAt(0).?;   \u002F\u002F assert key\u002Fargv\u002Foutput mode\ntry app_state.effects.feedLine(stream_key, \"stream line 1\");\ntry app_state.effects.feedExit(stream_key, 0);\ntry harness.runtime.dispatchPlatformEvent(app, .wake);   \u002F\u002F drain -> update\n\n\u002F\u002F .collect spawns: feedLine accumulates (bytes + newline, like a real child\n\u002F\u002F printing that line), feedStderr fills the tail, feedExit delivers both.\ntry app_state.effects.feedLine(issues_key, \"{\\\"number\\\":1}\");   \u002F\u002F no on_line Msg\ntry app_state.effects.feedStderr(issues_key, \"warning: slow\\n\");\ntry app_state.effects.feedExit(issues_key, 0);                  \u002F\u002F exit.output + exit.stderr_tail\n\nconst fetch_req = app_state.effects.pendingFetchAt(0).?; \u002F\u002F assert key\u002Fmethod\u002Furl\u002Fheaders\u002Fbody\ntry app_state.effects.feedResponse(search_key, 200, \"{\\\"ok\\\":true}\");\ntry harness.runtime.dispatchPlatformEvent(app, .wake);   \u002F\u002F Msg{ .fetched = ... }\n",[14225],{"type":42,"tag":65,"props":14226,"children":14227},{"__ignoreMap":223},[14228,14236,14244,14252,14260,14268,14276,14284,14292,14299,14307,14315,14323,14331,14339,14346,14354,14362],{"type":42,"tag":229,"props":14229,"children":14230},{"class":231,"line":232},[14231],{"type":42,"tag":229,"props":14232,"children":14233},{},[14234],{"type":48,"value":14235},"app_state.effects.executor = .fake;             \u002F\u002F before dispatching (and before\n",{"type":42,"tag":229,"props":14237,"children":14238},{"class":231,"line":241},[14239],{"type":42,"tag":229,"props":14240,"children":14241},{},[14242],{"type":48,"value":14243},"                                                \u002F\u002F the first frame if using init_fx —\n",{"type":42,"tag":229,"props":14245,"children":14246},{"class":231,"line":251},[14247],{"type":42,"tag":229,"props":14248,"children":14249},{},[14250],{"type":48,"value":14251},"                                                \u002F\u002F the boot spawn is then recorded too)\n",{"type":42,"tag":229,"props":14253,"children":14254},{"class":231,"line":260},[14255],{"type":42,"tag":229,"props":14256,"children":14257},{},[14258],{"type":48,"value":14259},"try app_state.dispatch(&harness.runtime, 1, .start);\n",{"type":42,"tag":229,"props":14261,"children":14262},{"class":231,"line":269},[14263],{"type":42,"tag":229,"props":14264,"children":14265},{},[14266],{"type":48,"value":14267},"const request = app_state.effects.pendingSpawnAt(0).?;   \u002F\u002F assert key\u002Fargv\u002Foutput mode\n",{"type":42,"tag":229,"props":14269,"children":14270},{"class":231,"line":278},[14271],{"type":42,"tag":229,"props":14272,"children":14273},{},[14274],{"type":48,"value":14275},"try app_state.effects.feedLine(stream_key, \"stream line 1\");\n",{"type":42,"tag":229,"props":14277,"children":14278},{"class":231,"line":287},[14279],{"type":42,"tag":229,"props":14280,"children":14281},{},[14282],{"type":48,"value":14283},"try app_state.effects.feedExit(stream_key, 0);\n",{"type":42,"tag":229,"props":14285,"children":14286},{"class":231,"line":296},[14287],{"type":42,"tag":229,"props":14288,"children":14289},{},[14290],{"type":48,"value":14291},"try harness.runtime.dispatchPlatformEvent(app, .wake);   \u002F\u002F drain -> update\n",{"type":42,"tag":229,"props":14293,"children":14294},{"class":231,"line":305},[14295],{"type":42,"tag":229,"props":14296,"children":14297},{"emptyLinePlaceholder":245},[14298],{"type":48,"value":248},{"type":42,"tag":229,"props":14300,"children":14301},{"class":231,"line":314},[14302],{"type":42,"tag":229,"props":14303,"children":14304},{},[14305],{"type":48,"value":14306},"\u002F\u002F .collect spawns: feedLine accumulates (bytes + newline, like a real child\n",{"type":42,"tag":229,"props":14308,"children":14309},{"class":231,"line":323},[14310],{"type":42,"tag":229,"props":14311,"children":14312},{},[14313],{"type":48,"value":14314},"\u002F\u002F printing that line), feedStderr fills the tail, feedExit delivers both.\n",{"type":42,"tag":229,"props":14316,"children":14317},{"class":231,"line":332},[14318],{"type":42,"tag":229,"props":14319,"children":14320},{},[14321],{"type":48,"value":14322},"try app_state.effects.feedLine(issues_key, \"{\\\"number\\\":1}\");   \u002F\u002F no on_line Msg\n",{"type":42,"tag":229,"props":14324,"children":14325},{"class":231,"line":341},[14326],{"type":42,"tag":229,"props":14327,"children":14328},{},[14329],{"type":48,"value":14330},"try app_state.effects.feedStderr(issues_key, \"warning: slow\\n\");\n",{"type":42,"tag":229,"props":14332,"children":14333},{"class":231,"line":350},[14334],{"type":42,"tag":229,"props":14335,"children":14336},{},[14337],{"type":48,"value":14338},"try app_state.effects.feedExit(issues_key, 0);                  \u002F\u002F exit.output + exit.stderr_tail\n",{"type":42,"tag":229,"props":14340,"children":14341},{"class":231,"line":359},[14342],{"type":42,"tag":229,"props":14343,"children":14344},{"emptyLinePlaceholder":245},[14345],{"type":48,"value":248},{"type":42,"tag":229,"props":14347,"children":14348},{"class":231,"line":368},[14349],{"type":42,"tag":229,"props":14350,"children":14351},{},[14352],{"type":48,"value":14353},"const fetch_req = app_state.effects.pendingFetchAt(0).?; \u002F\u002F assert key\u002Fmethod\u002Furl\u002Fheaders\u002Fbody\n",{"type":42,"tag":229,"props":14355,"children":14356},{"class":231,"line":377},[14357],{"type":42,"tag":229,"props":14358,"children":14359},{},[14360],{"type":48,"value":14361},"try app_state.effects.feedResponse(search_key, 200, \"{\\\"ok\\\":true}\");\n",{"type":42,"tag":229,"props":14363,"children":14364},{"class":231,"line":386},[14365],{"type":42,"tag":229,"props":14366,"children":14367},{},[14368],{"type":48,"value":14369},"try harness.runtime.dispatchPlatformEvent(app, .wake);   \u002F\u002F Msg{ .fetched = ... }\n",{"type":42,"tag":51,"props":14371,"children":14372},{},[14373,14374,14379,14381,14387,14389,14395,14397,14402,14404,14409,14411,14417,14419,14425,14427,14432,14434,14440],{"type":48,"value":8346},{"type":42,"tag":65,"props":14375,"children":14377},{"className":14376},[],[14378],{"type":48,"value":13202},{"type":48,"value":14380}," platform event is how live platforms marshal worker completions onto the loop thread (macOS main-queue dispatch, GTK ",{"type":42,"tag":65,"props":14382,"children":14384},{"className":14383},[],[14385],{"type":48,"value":14386},"g_idle_add",{"type":48,"value":14388},", Win32 ",{"type":42,"tag":65,"props":14390,"children":14392},{"className":14391},[],[14393],{"type":48,"value":14394},"PostMessage",{"type":48,"value":14396},"); dispatching it in tests exercises the same drain path. Note that after ",{"type":42,"tag":65,"props":14398,"children":14400},{"className":14399},[],[14401],{"type":48,"value":11615},{"type":48,"value":14403}," runs in ",{"type":42,"tag":65,"props":14405,"children":14407},{"className":14406},[],[14408],{"type":48,"value":483},{"type":48,"value":14410},", a subsequent ",{"type":42,"tag":65,"props":14412,"children":14414},{"className":14413},[],[14415],{"type":48,"value":14416},"feedExit(key)",{"type":48,"value":14418}," correctly fails with ",{"type":42,"tag":65,"props":14420,"children":14422},{"className":14421},[],[14423],{"type":48,"value":14424},"error.EffectNotFound",{"type":48,"value":14426}," — the cancel already delivered the terminal ",{"type":42,"tag":65,"props":14428,"children":14430},{"className":14429},[],[14431],{"type":48,"value":11631},{"type":48,"value":14433}," exit, so there is no active effect left to feed. See ",{"type":42,"tag":65,"props":14435,"children":14437},{"className":14436},[],[14438],{"type":48,"value":14439},"examples\u002Feffects-probe",{"type":48,"value":14441}," for the complete pattern, including the live cancel flow.",{"type":42,"tag":14443,"props":14444,"children":14445},"blockquote",{},[14446,14517,14753],{"type":42,"tag":51,"props":14447,"children":14448},{},[14449,14454,14456,14462,14464,14470,14472,14478,14479,14485,14487,14493,14495,14501,14503,14509,14510,14516],{"type":42,"tag":513,"props":14450,"children":14451},{},[14452],{"type":48,"value":14453},"Effects test surface — exact signatures.",{"type":48,"value":14455}," These are the complete fake-executor entry points; do not excavate the SDK source. Harness and switch: ",{"type":42,"tag":65,"props":14457,"children":14459},{"className":14458},[],[14460],{"type":48,"value":14461},"const harness = try native_sdk.TestHarness().create(std.testing.allocator, .{ .size = geometry.SizeF.init(w, h) });",{"type":48,"value":14463}," then ",{"type":42,"tag":65,"props":14465,"children":14467},{"className":14466},[],[14468],{"type":48,"value":14469},"app_state.effects.executor = .fake;",{"type":48,"value":14471}," before dispatching. Every ",{"type":42,"tag":65,"props":14473,"children":14475},{"className":14474},[],[14476],{"type":48,"value":14477},"feed*",{"type":48,"value":456},{"type":42,"tag":65,"props":14480,"children":14482},{"className":14481},[],[14483],{"type":48,"value":14484},"fire*",{"type":48,"value":14486}," returns ",{"type":42,"tag":65,"props":14488,"children":14490},{"className":14489},[],[14491],{"type":48,"value":14492},"error{EffectNotFound}!void",{"type":48,"value":14494},"; every ",{"type":42,"tag":65,"props":14496,"children":14498},{"className":14497},[],[14499],{"type":48,"value":14500},"pending*At(index: usize)",{"type":48,"value":14502}," returns an optional request record; each family's ",{"type":42,"tag":65,"props":14504,"children":14506},{"className":14505},[],[14507],{"type":48,"value":14508},"pending*Count()",{"type":48,"value":14486},{"type":42,"tag":65,"props":14511,"children":14513},{"className":14512},[],[14514],{"type":48,"value":14515},"usize",{"type":48,"value":123},{"type":42,"tag":57,"props":14518,"children":14519},{},[14520,14589,14615,14634,14653,14672,14692,14732],{"type":42,"tag":61,"props":14521,"children":14522},{},[14523,14525,14531,14533,14539,14540,14546,14547,14553,14554,14560,14561,14566,14568,14573,14574,14579,14581,14587],{"type":48,"value":14524},"Spawn: ",{"type":42,"tag":65,"props":14526,"children":14528},{"className":14527},[],[14529],{"type":48,"value":14530},"pendingSpawnAt(index: usize) ?SpawnRequest",{"type":48,"value":14532}," · ",{"type":42,"tag":65,"props":14534,"children":14536},{"className":14535},[],[14537],{"type":48,"value":14538},"feedLine(key: u64, bytes: []const u8)",{"type":48,"value":14532},{"type":42,"tag":65,"props":14541,"children":14543},{"className":14542},[],[14544],{"type":48,"value":14545},"feedStderr(key: u64, bytes: []const u8)",{"type":48,"value":14532},{"type":42,"tag":65,"props":14548,"children":14550},{"className":14549},[],[14551],{"type":48,"value":14552},"feedExit(key: u64, code: i32)",{"type":48,"value":14532},{"type":42,"tag":65,"props":14555,"children":14557},{"className":14556},[],[14558],{"type":48,"value":14559},"feedExitReason(key: u64, code: i32, reason: EffectExitReason)",{"type":48,"value":2620},{"type":42,"tag":65,"props":14562,"children":14564},{"className":14563},[],[14565],{"type":48,"value":13210},{"type":48,"value":14567}," is the clean-exit sugar; the reason enum covers ",{"type":42,"tag":65,"props":14569,"children":14571},{"className":14570},[],[14572],{"type":48,"value":11582},{"type":48,"value":456},{"type":42,"tag":65,"props":14575,"children":14577},{"className":14576},[],[14578],{"type":48,"value":11589},{"type":48,"value":14580},"\u002F...) · ",{"type":42,"tag":65,"props":14582,"children":14584},{"className":14583},[],[14585],{"type":48,"value":14586},"feedOutput(key: u64, bytes: []const u8)",{"type":48,"value":14588}," (raw collect-stdout append, no line framing).",{"type":42,"tag":61,"props":14590,"children":14591},{},[14592,14594,14600,14601,14607,14608,14614],{"type":48,"value":14593},"Fetch: ",{"type":42,"tag":65,"props":14595,"children":14597},{"className":14596},[],[14598],{"type":48,"value":14599},"pendingFetchAt(index: usize) ?FetchRequest",{"type":48,"value":14532},{"type":42,"tag":65,"props":14602,"children":14604},{"className":14603},[],[14605],{"type":48,"value":14606},"feedResponse(key: u64, status: u16, body: []const u8)",{"type":48,"value":14532},{"type":42,"tag":65,"props":14609,"children":14611},{"className":14610},[],[14612],{"type":48,"value":14613},"feedResponseOutcome(key: u64, outcome: EffectFetchOutcome, status: u16, body: []const u8)",{"type":48,"value":123},{"type":42,"tag":61,"props":14616,"children":14617},{},[14618,14620,14626,14627,14633],{"type":48,"value":14619},"Files: ",{"type":42,"tag":65,"props":14621,"children":14623},{"className":14622},[],[14624],{"type":48,"value":14625},"pendingFileAt(index: usize) ?FileRequest",{"type":48,"value":14532},{"type":42,"tag":65,"props":14628,"children":14630},{"className":14629},[],[14631],{"type":48,"value":14632},"feedFileResult(key: u64, outcome: EffectFileOutcome, bytes: []const u8)",{"type":48,"value":123},{"type":42,"tag":61,"props":14635,"children":14636},{},[14637,14639,14645,14646,14652],{"type":48,"value":14638},"Clipboard: ",{"type":42,"tag":65,"props":14640,"children":14642},{"className":14641},[],[14643],{"type":48,"value":14644},"pendingClipboardAt(index: usize) ?ClipboardRequest",{"type":48,"value":14532},{"type":42,"tag":65,"props":14647,"children":14649},{"className":14648},[],[14650],{"type":48,"value":14651},"feedClipboardResult(key: u64, outcome: EffectClipboardOutcome, text: []const u8)",{"type":48,"value":123},{"type":42,"tag":61,"props":14654,"children":14655},{},[14656,14658,14664,14665,14671],{"type":48,"value":14657},"Host commands: ",{"type":42,"tag":65,"props":14659,"children":14661},{"className":14660},[],[14662],{"type":48,"value":14663},"pendingHostAt(index: usize) ?HostRequest",{"type":48,"value":14532},{"type":42,"tag":65,"props":14666,"children":14668},{"className":14667},[],[14669],{"type":48,"value":14670},"feedHostResult(key: u64, ok: bool, bytes: []const u8)",{"type":48,"value":123},{"type":42,"tag":61,"props":14673,"children":14674},{},[14675,14677,14683,14684,14690],{"type":48,"value":14676},"Timers: ",{"type":42,"tag":65,"props":14678,"children":14680},{"className":14679},[],[14681],{"type":48,"value":14682},"pendingTimerAt(index: usize) ?TimerRequest",{"type":48,"value":14532},{"type":42,"tag":65,"props":14685,"children":14687},{"className":14686},[],[14688],{"type":48,"value":14689},"fireTimer(key: u64)",{"type":48,"value":14691}," (one-shot slots retire after the fire).",{"type":42,"tag":61,"props":14693,"children":14694},{},[14695,14697,14703,14704,14710,14711,14717,14718,14724,14725,14731],{"type":48,"value":14696},"Audio (one channel): ",{"type":42,"tag":65,"props":14698,"children":14700},{"className":14699},[],[14701],{"type":48,"value":14702},"pendingAudio() ?AudioRequest",{"type":48,"value":14532},{"type":42,"tag":65,"props":14705,"children":14707},{"className":14706},[],[14708],{"type":48,"value":14709},"feedAudioEvent(kind: EffectAudioEventKind, position_ms: u64, duration_ms: u64, playing: bool)",{"type":48,"value":14532},{"type":42,"tag":65,"props":14712,"children":14714},{"className":14713},[],[14715],{"type":48,"value":14716},"feedAudioEventBuffering(kind, position_ms, duration_ms, playing, buffering: bool)",{"type":48,"value":14532},{"type":42,"tag":65,"props":14719,"children":14721},{"className":14720},[],[14722],{"type":48,"value":14723},"feedAudioSpectrum(bands: [32]u8, position_ms: u64, duration_ms: u64)",{"type":48,"value":14532},{"type":42,"tag":65,"props":14726,"children":14728},{"className":14727},[],[14729],{"type":48,"value":14730},"audioSnapshot() AudioSnapshot",{"type":48,"value":123},{"type":42,"tag":61,"props":14733,"children":14734},{},[14735,14737,14743,14745,14751],{"type":48,"value":14736},"Channels: ",{"type":42,"tag":65,"props":14738,"children":14740},{"className":14739},[],[14741],{"type":48,"value":14742},"channelHandle(key: u64) ?ChannelHandle",{"type":48,"value":14744}," (post from the test exactly as a source thread would — no feed needed for live posts) · ",{"type":42,"tag":65,"props":14746,"children":14748},{"className":14747},[],[14749],{"type":48,"value":14750},"feedChannelEvent(key: u64, kind: EffectChannelEventKind, bytes: []const u8, dropped_pending: u32, dropped_total: u32) error{EffectNotFound, EffectQueueFull}!void",{"type":48,"value":14752}," (hand-feeds an event, the replay path's seam).",{"type":42,"tag":51,"props":14754,"children":14755},{},[14756,14758,14764],{"type":48,"value":14757},"After feeding, drain with ",{"type":42,"tag":65,"props":14759,"children":14761},{"className":14760},[],[14762],{"type":48,"value":14763},"try harness.runtime.dispatchPlatformEvent(app, .wake);",{"type":48,"value":14765}," — results become Msgs through the same path live platforms use.",{"type":42,"tag":212,"props":14767,"children":14769},{"id":14768},"secondary-windows-model-declared-windows_fn-window_view",[14770,14772,14778,14779,14785],{"type":48,"value":14771},"Secondary windows: model-declared (",{"type":42,"tag":65,"props":14773,"children":14775},{"className":14774},[],[14776],{"type":48,"value":14777},"windows_fn",{"type":48,"value":3952},{"type":42,"tag":65,"props":14780,"children":14782},{"className":14781},[],[14783],{"type":48,"value":14784},"window_view",{"type":48,"value":9187},{"type":42,"tag":51,"props":14787,"children":14788},{},[14789,14791,14797,14799,14805,14807,14813],{"type":48,"value":14790},"Windows are model state, like an anchored surface's open flag. ",{"type":42,"tag":65,"props":14792,"children":14794},{"className":14793},[],[14795],{"type":48,"value":14796},"Options.windows_fn",{"type":48,"value":14798}," returns the descriptors that should exist RIGHT NOW (presence is visibility — no ",{"type":42,"tag":65,"props":14800,"children":14802},{"className":14801},[],[14803],{"type":48,"value":14804},"visible",{"type":48,"value":14806}," flag; the platform window channel has no hide); ",{"type":42,"tag":65,"props":14808,"children":14810},{"className":14809},[],[14811],{"type":48,"value":14812},"Options.window_view",{"type":48,"value":14814}," builds each declared window's whole canvas tree by window label. The runtime reconciles after every dispatch: create the newly declared, close the no-longer-declared, rebuild every open window's view from the same model.",{"type":42,"tag":219,"props":14816,"children":14818},{"className":221,"code":14817,"language":18,"meta":223,"style":223},"fn windows(model: *const Model, scratch: *App.WindowsScratch) []const App.WindowDescriptor {\n    var count: usize = 0;\n    if (model.settings_open) {\n        scratch.windows[count] = .{\n            .label = \"settings\", .canvas_label = \"settings-canvas\",\n            .title = \"Settings\", .width = 360, .height = 320,\n            .min_width = 320, .min_height = 280, \u002F\u002F the WINDOW enforces the floor (macOS contentMinSize)\n            .on_close = .settings_closed,   \u002F\u002F the user's close button, as a Msg\n        };\n        count += 1;\n    }\n    return scratch.windows[0..count];\n}\nfn windowView(ui: *App.Ui, model: *const Model, window_label: []const u8) App.Ui.Node { ... }\n\u002F\u002F options: .windows_fn = windows, .window_view = windowView,\n",[14819],{"type":42,"tag":65,"props":14820,"children":14821},{"__ignoreMap":223},[14822,14830,14837,14845,14853,14861,14869,14877,14885,14892,14899,14906,14914,14921,14929],{"type":42,"tag":229,"props":14823,"children":14824},{"class":231,"line":232},[14825],{"type":42,"tag":229,"props":14826,"children":14827},{},[14828],{"type":48,"value":14829},"fn windows(model: *const Model, scratch: *App.WindowsScratch) []const App.WindowDescriptor {\n",{"type":42,"tag":229,"props":14831,"children":14832},{"class":231,"line":241},[14833],{"type":42,"tag":229,"props":14834,"children":14835},{},[14836],{"type":48,"value":10010},{"type":42,"tag":229,"props":14838,"children":14839},{"class":231,"line":251},[14840],{"type":42,"tag":229,"props":14841,"children":14842},{},[14843],{"type":48,"value":14844},"    if (model.settings_open) {\n",{"type":42,"tag":229,"props":14846,"children":14847},{"class":231,"line":260},[14848],{"type":42,"tag":229,"props":14849,"children":14850},{},[14851],{"type":48,"value":14852},"        scratch.windows[count] = .{\n",{"type":42,"tag":229,"props":14854,"children":14855},{"class":231,"line":269},[14856],{"type":42,"tag":229,"props":14857,"children":14858},{},[14859],{"type":48,"value":14860},"            .label = \"settings\", .canvas_label = \"settings-canvas\",\n",{"type":42,"tag":229,"props":14862,"children":14863},{"class":231,"line":278},[14864],{"type":42,"tag":229,"props":14865,"children":14866},{},[14867],{"type":48,"value":14868},"            .title = \"Settings\", .width = 360, .height = 320,\n",{"type":42,"tag":229,"props":14870,"children":14871},{"class":231,"line":287},[14872],{"type":42,"tag":229,"props":14873,"children":14874},{},[14875],{"type":48,"value":14876},"            .min_width = 320, .min_height = 280, \u002F\u002F the WINDOW enforces the floor (macOS contentMinSize)\n",{"type":42,"tag":229,"props":14878,"children":14879},{"class":231,"line":296},[14880],{"type":42,"tag":229,"props":14881,"children":14882},{},[14883],{"type":48,"value":14884},"            .on_close = .settings_closed,   \u002F\u002F the user's close button, as a Msg\n",{"type":42,"tag":229,"props":14886,"children":14887},{"class":231,"line":305},[14888],{"type":42,"tag":229,"props":14889,"children":14890},{},[14891],{"type":48,"value":10066},{"type":42,"tag":229,"props":14893,"children":14894},{"class":231,"line":314},[14895],{"type":42,"tag":229,"props":14896,"children":14897},{},[14898],{"type":48,"value":948},{"type":42,"tag":229,"props":14900,"children":14901},{"class":231,"line":323},[14902],{"type":42,"tag":229,"props":14903,"children":14904},{},[14905],{"type":48,"value":956},{"type":42,"tag":229,"props":14907,"children":14908},{"class":231,"line":332},[14909],{"type":42,"tag":229,"props":14910,"children":14911},{},[14912],{"type":48,"value":14913},"    return scratch.windows[0..count];\n",{"type":42,"tag":229,"props":14915,"children":14916},{"class":231,"line":341},[14917],{"type":42,"tag":229,"props":14918,"children":14919},{},[14920],{"type":48,"value":428},{"type":42,"tag":229,"props":14922,"children":14923},{"class":231,"line":350},[14924],{"type":42,"tag":229,"props":14925,"children":14926},{},[14927],{"type":48,"value":14928},"fn windowView(ui: *App.Ui, model: *const Model, window_label: []const u8) App.Ui.Node { ... }\n",{"type":42,"tag":229,"props":14930,"children":14931},{"class":231,"line":359},[14932],{"type":42,"tag":229,"props":14933,"children":14934},{},[14935],{"type":48,"value":14936},"\u002F\u002F options: .windows_fn = windows, .window_view = windowView,\n",{"type":42,"tag":51,"props":14938,"children":14939},{},[14940],{"type":48,"value":14941},"Rules that matter:",{"type":42,"tag":57,"props":14943,"children":14944},{},[14945,14970,14993,15011,15060,15100,15172,15204,15237],{"type":42,"tag":61,"props":14946,"children":14947},{},[14948,14953,14955,14961,14962,14968],{"type":42,"tag":513,"props":14949,"children":14950},{},[14951],{"type":48,"value":14952},"Every canvas label must be unique across the app",{"type":48,"value":14954}," (main + declared windows); input routes back by it, and automation verbs (",{"type":42,"tag":65,"props":14956,"children":14958},{"className":14957},[],[14959],{"type":48,"value":14960},"widget-click \u003Ccanvas-label> \u003Cid>",{"type":48,"value":1820},{"type":42,"tag":65,"props":14963,"children":14965},{"className":14964},[],[14966],{"type":48,"value":14967},"screenshot",{"type":48,"value":14969},") address any window's canvas the same way.",{"type":42,"tag":61,"props":14971,"children":14972},{},[14973,14984,14986,14991],{"type":42,"tag":513,"props":14974,"children":14975},{},[14976,14978],{"type":48,"value":14977},"A user close dispatches ",{"type":42,"tag":65,"props":14979,"children":14981},{"className":14980},[],[14982],{"type":48,"value":14983},"on_close",{"type":48,"value":14985}," (the dismissal precedent): the window is already gone as the optimistic echo; clear the open flag in ",{"type":42,"tag":65,"props":14987,"children":14989},{"className":14988},[],[14990],{"type":48,"value":483},{"type":48,"value":14992}," — or keep declaring the window and the next rebuild brings it back (source wins). A close the model itself initiated never echoes a Msg.",{"type":42,"tag":61,"props":14994,"children":14995},{},[14996,15001,15003,15009],{"type":42,"tag":513,"props":14997,"children":14998},{},[14999],{"type":48,"value":15000},"Budget",{"type":48,"value":15002},": at most ",{"type":42,"tag":65,"props":15004,"children":15006},{"className":15005},[],[15007],{"type":48,"value":15008},"UiApp.max_ui_windows",{"type":48,"value":15010}," (4) declared windows; excess warns and is ignored. Every dispatched Msg rebuilds every open window's view.",{"type":42,"tag":61,"props":15012,"children":15013},{},[15014,15019,15021,15027,15029,15035,15037,15043,15044,15050,15052,15058],{"type":42,"tag":513,"props":15015,"children":15016},{},[15017],{"type":48,"value":15018},"Present-before-show",{"type":48,"value":15020},": canvas windows (any ",{"type":42,"tag":65,"props":15022,"children":15024},{"className":15023},[],[15025],{"type":48,"value":15026},"gpu_surface",{"type":48,"value":15028}," view — startup, scene, and declared windows alike) are created ordered-out and become visible only after their first canvas frame presents, so opening one never flashes blank. Automatic (",{"type":42,"tag":65,"props":15030,"children":15032},{"className":15031},[],[15033],{"type":48,"value":15034},"WindowOptions.show = .on_first_present",{"type":48,"value":15036},", derived from the views); webview windows show immediately. The null platform records ",{"type":42,"tag":65,"props":15038,"children":15040},{"className":15039},[],[15041],{"type":48,"value":15042},"window_show",{"type":48,"value":1820},{"type":42,"tag":65,"props":15045,"children":15047},{"className":15046},[],[15048],{"type":48,"value":15049},"window_visible",{"type":48,"value":15051},", and present\u002Fshown sequence numbers for ordering assertions; ",{"type":42,"tag":65,"props":15053,"children":15055},{"className":15054},[],[15056],{"type":48,"value":15057},"NATIVE_SDK_WINDOW_TIMING=1",{"type":48,"value":15059}," logs create→show latency on macOS.",{"type":42,"tag":61,"props":15061,"children":15062},{},[15063,15068,15070,15076,15078,15084,15086,15092,15093,15098],{"type":42,"tag":513,"props":15064,"children":15065},{},[15066],{"type":48,"value":15067},"Markup binds ONE window's content",{"type":48,"value":15069}," — there is no ",{"type":42,"tag":65,"props":15071,"children":15073},{"className":15072},[],[15074],{"type":48,"value":15075},"window",{"type":48,"value":15077}," element in the closed grammar. A markup-authored secondary window is a ",{"type":42,"tag":65,"props":15079,"children":15081},{"className":15080},[],[15082],{"type":48,"value":15083},"canvas.CompiledMarkupView",{"type":48,"value":15085}," whose ",{"type":42,"tag":65,"props":15087,"children":15089},{"className":15088},[],[15090],{"type":48,"value":15091},"build",{"type":48,"value":519},{"type":42,"tag":65,"props":15094,"children":15096},{"className":15095},[],[15097],{"type":48,"value":14784},{"type":48,"value":15099}," calls for that label.",{"type":42,"tag":61,"props":15101,"children":15102},{},[15103,15108,15110,15116,15117,15123,15125,15131,15133,15139,15141,15147,15149,15155,15157,15163,15164,15170],{"type":42,"tag":513,"props":15104,"children":15105},{},[15106],{"type":48,"value":15107},"Min size",{"type":48,"value":15109},": descriptors accept ",{"type":42,"tag":65,"props":15111,"children":15113},{"className":15112},[],[15114],{"type":48,"value":15115},".min_width",{"type":48,"value":456},{"type":42,"tag":65,"props":15118,"children":15120},{"className":15119},[],[15121],{"type":48,"value":15122},".min_height",{"type":48,"value":15124}," — a content min-size floor the WINDOW enforces (macOS ",{"type":42,"tag":65,"props":15126,"children":15128},{"className":15127},[],[15129],{"type":48,"value":15130},"contentMinSize",{"type":48,"value":15132},"), so a preferences window's resize stops at its honest floor instead of the layout clamping\u002Fclipping its panes. Same fields on app.zon windows and ",{"type":42,"tag":65,"props":15134,"children":15136},{"className":15135},[],[15137],{"type":48,"value":15138},"ShellWindow",{"type":48,"value":15140}," (the first shell window's declaration threads through the startup create like ",{"type":42,"tag":65,"props":15142,"children":15144},{"className":15143},[],[15145],{"type":48,"value":15146},"titlebar",{"type":48,"value":15148},"; negative values fail ",{"type":42,"tag":65,"props":15150,"children":15152},{"className":15151},[],[15153],{"type":48,"value":15154},"zig build validate",{"type":48,"value":15156},"). The null platform records ",{"type":42,"tag":65,"props":15158,"children":15160},{"className":15159},[],[15161],{"type":48,"value":15162},"window_min_width",{"type":48,"value":456},{"type":42,"tag":65,"props":15165,"children":15167},{"className":15166},[],[15168],{"type":48,"value":15169},"window_min_height",{"type":48,"value":15171}," for seam assertions.",{"type":42,"tag":61,"props":15173,"children":15174},{},[15175,15180,15181,15187,15189,15195,15197,15202],{"type":42,"tag":513,"props":15176,"children":15177},{},[15178],{"type":48,"value":15179},"Titlebar",{"type":48,"value":15109},{"type":42,"tag":65,"props":15182,"children":15184},{"className":15183},[],[15185],{"type":48,"value":15186},".titlebar = .hidden_inset",{"type":48,"value":15188}," (content under a transparent titlebar, macOS keeps the traffic lights) or ",{"type":42,"tag":65,"props":15190,"children":15192},{"className":15191},[],[15193],{"type":48,"value":15194},".hidden_inset_tall",{"type":48,"value":15196}," (the taller unified band; macOS centers the lights in it); give the window's header ",{"type":42,"tag":65,"props":15198,"children":15200},{"className":15199},[],[15201],{"type":48,"value":5046},{"type":48,"value":15203}," so it moves the window. See \"Hidden titlebar\" below.",{"type":42,"tag":61,"props":15205,"children":15206},{},[15207,15212,15214,15220,15222,15227,15229,15235],{"type":42,"tag":513,"props":15208,"children":15209},{},[15210],{"type":48,"value":15211},"Settings windows open the standard way",{"type":48,"value":15213},": the app-menu Settings item and its primary+comma shortcut (an app.zon ",{"type":42,"tag":65,"props":15215,"children":15217},{"className":15216},[],[15218],{"type":48,"value":15219},".shortcuts",{"type":48,"value":15221}," entry mapped in ",{"type":42,"tag":65,"props":15223,"children":15225},{"className":15224},[],[15226],{"type":48,"value":784},{"type":48,"value":15228},"), never an in-window settings button. Ship them fixed-size (",{"type":42,"tag":65,"props":15230,"children":15232},{"className":15231},[],[15233],{"type":48,"value":15234},".resizable = false",{"type":48,"value":15236},") at exactly the content's box, title them \"Settings\", and let changes apply live through the shared model — no Apply\u002FOK row, no copy explaining the window.",{"type":42,"tag":61,"props":15238,"children":15239},{},[15240,15242,15248,15250,15256,15258,15264,15265,15271,15273,15279],{"type":48,"value":15241},"Tests: after the open Msg, deliver the new window's ",{"type":42,"tag":65,"props":15243,"children":15245},{"className":15244},[],[15246],{"type":48,"value":15247},"gpu_surface_frame",{"type":48,"value":15249}," (its window id from ",{"type":42,"tag":65,"props":15251,"children":15253},{"className":15252},[],[15254],{"type":48,"value":15255},"runtime.listWindows",{"type":48,"value":15257},") to install its tree; simulate a user close by dispatching ",{"type":42,"tag":65,"props":15259,"children":15261},{"className":15260},[],[15262],{"type":48,"value":15263},".window_frame_changed",{"type":48,"value":157},{"type":42,"tag":65,"props":15266,"children":15268},{"className":15267},[],[15269],{"type":48,"value":15270},"open = false",{"type":48,"value":15272},". See ",{"type":42,"tag":65,"props":15274,"children":15276},{"className":15275},[],[15277],{"type":48,"value":15278},"examples\u002Fsystem-monitor",{"type":48,"value":15280}," (settings shortcut -> settings window).",{"type":42,"tag":212,"props":15282,"children":15284},{"id":15283},"hidden-titlebar-titlebar-hidden_insethidden_inset_tall-window-drag-on_chrome",[15285,15287,15293,15294,15300,15301,15307,15308],{"type":48,"value":15286},"Hidden titlebar: ",{"type":42,"tag":65,"props":15288,"children":15290},{"className":15289},[],[15291],{"type":48,"value":15292},"titlebar = \"hidden_inset\"",{"type":48,"value":456},{"type":42,"tag":65,"props":15295,"children":15297},{"className":15296},[],[15298],{"type":48,"value":15299},"\"hidden_inset_tall\"",{"type":48,"value":3952},{"type":42,"tag":65,"props":15302,"children":15304},{"className":15303},[],[15305],{"type":48,"value":15306},"window-drag",{"type":48,"value":3952},{"type":42,"tag":65,"props":15309,"children":15311},{"className":15310},[],[15312],{"type":48,"value":15313},"on_chrome",{"type":42,"tag":51,"props":15315,"children":15316},{},[15317,15319,15325,15327,15333],{"type":48,"value":15318},"The modern editor-app shape — content under a transparent titlebar, the app's header as the working titlebar. Two heights: ",{"type":42,"tag":65,"props":15320,"children":15322},{"className":15321},[],[15323],{"type":48,"value":15324},"hidden_inset",{"type":48,"value":15326}," keeps the compact band (~28pt, traffic lights hug the top), ",{"type":42,"tag":65,"props":15328,"children":15330},{"className":15329},[],[15331],{"type":48,"value":15332},"hidden_inset_tall",{"type":48,"value":15334}," switches to the unified-toolbar band (~52pt, macOS vertically centers the traffic lights — the tall unified-toolbar look). Pick tall when the header replacing the titlebar is toolbar-height, so the lights center against it. Three parts, all declared:",{"type":42,"tag":15336,"props":15337,"children":15338},"ol",{},[15339,15389,15406],{"type":42,"tag":61,"props":15340,"children":15341},{},[15342,15347,15348,15354,15355,15360,15362,15367,15368,15373,15375,15380,15382,15387],{"type":42,"tag":513,"props":15343,"children":15344},{},[15345],{"type":48,"value":15346},"app.zon",{"type":48,"value":4178},{"type":42,"tag":65,"props":15349,"children":15351},{"className":15350},[],[15352],{"type":48,"value":15353},".titlebar = \"hidden_inset\"",{"type":48,"value":3426},{"type":42,"tag":65,"props":15356,"children":15358},{"className":15357},[],[15359],{"type":48,"value":15299},{"type":48,"value":15361}," on the shell window (and the matching ",{"type":42,"tag":65,"props":15363,"children":15365},{"className":15364},[],[15366],{"type":48,"value":15186},{"type":48,"value":456},{"type":42,"tag":65,"props":15369,"children":15371},{"className":15370},[],[15372],{"type":48,"value":15194},{"type":48,"value":15374}," on the ",{"type":42,"tag":65,"props":15376,"children":15378},{"className":15377},[],[15379],{"type":48,"value":15138},{"type":48,"value":15381}," in main.zig). The first shell window's declaration threads through the STARTUP window create, so the main window's chrome is right from the first frame; ",{"type":42,"tag":65,"props":15383,"children":15385},{"className":15384},[],[15386],{"type":48,"value":15154},{"type":48,"value":15388}," checks the value.",{"type":42,"tag":61,"props":15390,"children":15391},{},[15392,15397,15399,15404],{"type":42,"tag":513,"props":15393,"children":15394},{},[15395],{"type":48,"value":15396},"The header row",{"type":48,"value":15398}," gets ",{"type":42,"tag":65,"props":15400,"children":15402},{"className":15401},[],[15403],{"type":48,"value":5046},{"type":48,"value":15405},": its background (and plain text\u002Ficons inside) moves the window; buttons inside stay buttons; double-click zooms (macOS honors the user's titlebar double-click preference).",{"type":42,"tag":61,"props":15407,"children":15408},{},[15409,15418,15419,15425,15427,15433,15435,15441,15443,15449,15451,15457,15459,15465],{"type":42,"tag":513,"props":15410,"children":15411},{},[15412],{"type":42,"tag":65,"props":15413,"children":15415},{"className":15414},[],[15416],{"type":48,"value":15417},"Options.on_chrome",{"type":48,"value":2620},{"type":42,"tag":65,"props":15420,"children":15422},{"className":15421},[],[15423],{"type":48,"value":15424},"fn (chrome: platform.WindowChrome) ?Msg",{"type":48,"value":15426},") delivers the chrome overlay geometry — ",{"type":42,"tag":65,"props":15428,"children":15430},{"className":15429},[],[15431],{"type":48,"value":15432},"chrome.insets",{"type":48,"value":15434},": titlebar band height on top (compact or tall), traffic-light extent on the leading edge; ",{"type":42,"tag":65,"props":15436,"children":15438},{"className":15437},[],[15439],{"type":48,"value":15440},"chrome.buttons",{"type":48,"value":15442},": the traffic-light cluster's frame in content coordinates (top-left origin), the vertical truth for centering. All-zero in fullscreen, on standard chrome, and on other platforms. It fires BEFORE the first view build and on changes; store the geometry in the model, pad the header with a leading ",{"type":42,"tag":65,"props":15444,"children":15446},{"className":15445},[],[15447],{"type":48,"value":15448},"\u003Cspacer width=\"{chrome_leading}\" \u002F>",{"type":48,"value":15450},", and with the tall band match the header's height to ",{"type":42,"tag":65,"props":15452,"children":15454},{"className":15453},[],[15455],{"type":48,"value":15456},"insets.top",{"type":48,"value":15458}," (floored at its natural height) so ",{"type":42,"tag":65,"props":15460,"children":15462},{"className":15461},[],[15463],{"type":48,"value":15464},"cross=\"center\"",{"type":48,"value":15466}," puts its controls on the lights' centerline.",{"type":42,"tag":51,"props":15468,"children":15469},{},[15470,15472,15478,15480,15486,15488,15494,15496,15502,15504,15510,15512,15518],{"type":48,"value":15471},"macOS-first like ",{"type":42,"tag":65,"props":15473,"children":15475},{"className":15474},[],[15476],{"type":48,"value":15477},"resizable = false",{"type":48,"value":15479},": GTK\u002FWin32 keep standard chrome and the whole channel is harmless there. Full retrofit: ",{"type":42,"tag":65,"props":15481,"children":15483},{"className":15482},[],[15484],{"type":48,"value":15485},"examples\u002Fmarkdown-viewer",{"type":48,"value":15487}," (tall band; toolbar row is the drag region and tracks the band height). Tests: the null platform records ",{"type":42,"tag":65,"props":15489,"children":15491},{"className":15490},[],[15492],{"type":48,"value":15493},"startWindowDrag",{"type":48,"value":15495}," calls (",{"type":42,"tag":65,"props":15497,"children":15499},{"className":15498},[],[15500],{"type":48,"value":15501},"window_drag_starts",{"type":48,"value":15503},"), per-window ",{"type":42,"tag":65,"props":15505,"children":15507},{"className":15506},[],[15508],{"type":48,"value":15509},"window_titlebar",{"type":48,"value":15511},", and serves settable ",{"type":42,"tag":65,"props":15513,"children":15515},{"className":15514},[],[15516],{"type":48,"value":15517},"window_chrome",{"type":48,"value":15519}," (insets + buttons frame).",{"type":42,"tag":212,"props":15521,"children":15523},{"id":15522},"time-wall-clock-monotonic-with-a-testable-seam",[15524],{"type":48,"value":15525},"Time: wall clock + monotonic, with a testable seam",{"type":42,"tag":51,"props":15527,"children":15528},{},[15529,15531,15537,15539,15545,15547,15552,15554,15560],{"type":48,"value":15530},"Zig 0.16 puts ",{"type":42,"tag":65,"props":15532,"children":15534},{"className":15533},[],[15535],{"type":48,"value":15536},"std.time.milliTimestamp",{"type":48,"value":15538}," behind ",{"type":42,"tag":65,"props":15540,"children":15542},{"className":15541},[],[15543],{"type":48,"value":15544},"std.Io",{"type":48,"value":15546},", which ",{"type":42,"tag":65,"props":15548,"children":15550},{"className":15549},[],[15551],{"type":48,"value":483},{"type":48,"value":15553}," never sees — do NOT call ",{"type":42,"tag":65,"props":15555,"children":15557},{"className":15556},[],[15558],{"type":48,"value":15559},"clock_gettime",{"type":48,"value":15561}," yourself. The facade owns the clocks:",{"type":42,"tag":219,"props":15563,"children":15565},{"className":221,"code":15564,"language":18,"meta":223,"style":223},"native_sdk.nowMs()                  \u002F\u002F wall ms since the Unix epoch (i64) — ledger timestamps\nnative_sdk.nowNanoseconds()         \u002F\u002F wall ns (i128)\nnative_sdk.monotonicMs()            \u002F\u002F duration clock (u64, arbitrary origin, never goes backwards)\nnative_sdk.monotonicNanoseconds()   \u002F\u002F subtract two reads for an elapsed time\n",[15566],{"type":42,"tag":65,"props":15567,"children":15568},{"__ignoreMap":223},[15569,15577,15585,15593],{"type":42,"tag":229,"props":15570,"children":15571},{"class":231,"line":232},[15572],{"type":42,"tag":229,"props":15573,"children":15574},{},[15575],{"type":48,"value":15576},"native_sdk.nowMs()                  \u002F\u002F wall ms since the Unix epoch (i64) — ledger timestamps\n",{"type":42,"tag":229,"props":15578,"children":15579},{"class":231,"line":241},[15580],{"type":42,"tag":229,"props":15581,"children":15582},{},[15583],{"type":48,"value":15584},"native_sdk.nowNanoseconds()         \u002F\u002F wall ns (i128)\n",{"type":42,"tag":229,"props":15586,"children":15587},{"class":231,"line":251},[15588],{"type":42,"tag":229,"props":15589,"children":15590},{},[15591],{"type":48,"value":15592},"native_sdk.monotonicMs()            \u002F\u002F duration clock (u64, arbitrary origin, never goes backwards)\n",{"type":42,"tag":229,"props":15594,"children":15595},{"class":231,"line":260},[15596],{"type":42,"tag":229,"props":15597,"children":15598},{},[15599],{"type":48,"value":15600},"native_sdk.monotonicNanoseconds()   \u002F\u002F subtract two reads for an elapsed time\n",{"type":42,"tag":51,"props":15602,"children":15603},{},[15604],{"type":48,"value":15605},"Time-DEPENDENT logic (elapsed-time display, timeouts driven from update) should hold the seam in the model instead of calling the free functions, so tests stay deterministic:",{"type":42,"tag":219,"props":15607,"children":15609},{"className":221,"code":15608,"language":18,"meta":223,"style":223},"pub const Model = struct { clock: native_sdk.Clock = .system, ... };\n.step_started => model.entry.started_ms = model.clock.wallMs(),\n\n\u002F\u002F in tests:\nvar test_clock: native_sdk.TestClock = .{};\nmodel.clock = test_clock.clock();\ntest_clock.advanceMs(1500);          \u002F\u002F moves wall + monotonic together\ntest_clock.setWallMs(1_700_000_000_000);  \u002F\u002F NTP-style wall jump, monotonic untouched\n",[15610],{"type":42,"tag":65,"props":15611,"children":15612},{"__ignoreMap":223},[15613,15621,15629,15636,15644,15652,15660,15668],{"type":42,"tag":229,"props":15614,"children":15615},{"class":231,"line":232},[15616],{"type":42,"tag":229,"props":15617,"children":15618},{},[15619],{"type":48,"value":15620},"pub const Model = struct { clock: native_sdk.Clock = .system, ... };\n",{"type":42,"tag":229,"props":15622,"children":15623},{"class":231,"line":241},[15624],{"type":42,"tag":229,"props":15625,"children":15626},{},[15627],{"type":48,"value":15628},".step_started => model.entry.started_ms = model.clock.wallMs(),\n",{"type":42,"tag":229,"props":15630,"children":15631},{"class":231,"line":251},[15632],{"type":42,"tag":229,"props":15633,"children":15634},{"emptyLinePlaceholder":245},[15635],{"type":48,"value":248},{"type":42,"tag":229,"props":15637,"children":15638},{"class":231,"line":260},[15639],{"type":42,"tag":229,"props":15640,"children":15641},{},[15642],{"type":48,"value":15643},"\u002F\u002F in tests:\n",{"type":42,"tag":229,"props":15645,"children":15646},{"class":231,"line":269},[15647],{"type":42,"tag":229,"props":15648,"children":15649},{},[15650],{"type":48,"value":15651},"var test_clock: native_sdk.TestClock = .{};\n",{"type":42,"tag":229,"props":15653,"children":15654},{"class":231,"line":278},[15655],{"type":42,"tag":229,"props":15656,"children":15657},{},[15658],{"type":48,"value":15659},"model.clock = test_clock.clock();\n",{"type":42,"tag":229,"props":15661,"children":15662},{"class":231,"line":287},[15663],{"type":42,"tag":229,"props":15664,"children":15665},{},[15666],{"type":48,"value":15667},"test_clock.advanceMs(1500);          \u002F\u002F moves wall + monotonic together\n",{"type":42,"tag":229,"props":15669,"children":15670},{"class":231,"line":296},[15671],{"type":42,"tag":229,"props":15672,"children":15673},{},[15674],{"type":48,"value":15675},"test_clock.setWallMs(1_700_000_000_000);  \u002F\u002F NTP-style wall jump, monotonic untouched\n",{"type":42,"tag":51,"props":15677,"children":15678},{},[15679],{"type":48,"value":15680},"Wall answers \"what time is it?\" (jumps with OS clock adjustments); monotonic answers \"how long did it take?\". Don't subtract wall timestamps for durations.",{"type":42,"tag":212,"props":15682,"children":15684},{"id":15683},"images-runtime-registered-pixels-the-avatar-pattern",[15685],{"type":48,"value":15686},"Images: runtime-registered pixels + the avatar pattern",{"type":42,"tag":51,"props":15688,"children":15689},{},[15690,15692,15698,15699,15704,15706,15712],{"type":48,"value":15691},"Image pixels are runtime-registered resources keyed by a caller-chosen ",{"type":42,"tag":65,"props":15693,"children":15695},{"className":15694},[],[15696],{"type":48,"value":15697},"ImageId",{"type":48,"value":2620},{"type":42,"tag":65,"props":15700,"children":15702},{"className":15701},[],[15703],{"type":48,"value":4308},{"type":48,"value":15705}," in the model, effect-key style; 0 = no image). The framework bundles NO codecs — encoded bytes decode through the platform (CGImageSource \u002F gdk-pixbuf \u002F WIC) via ",{"type":42,"tag":65,"props":15707,"children":15709},{"className":15708},[],[15710],{"type":48,"value":15711},"PlatformServices.decode_image_fn",{"type":48,"value":15713},". Registration lives on the effects channel (synchronous calls, not effects — no Msg follows):",{"type":42,"tag":219,"props":15715,"children":15717},{"className":221,"code":15716,"language":18,"meta":223,"style":223},"\u002F\u002F The fetch-avatar path, one update arm; id reaches the model ONLY on success,\n\u002F\u002F so the avatar shows initials while loading and after failure.\n.fetched => |response| {\n    if (response.outcome == .ok and response.status == 200) {\n        _ = fx.registerImageBytes(avatar_image_id, response.body) catch return;\n        model.avatar_image = avatar_image_id;\n    }\n},\n",[15718],{"type":42,"tag":65,"props":15719,"children":15720},{"__ignoreMap":223},[15721,15729,15737,15745,15753,15761,15769,15776],{"type":42,"tag":229,"props":15722,"children":15723},{"class":231,"line":232},[15724],{"type":42,"tag":229,"props":15725,"children":15726},{},[15727],{"type":48,"value":15728},"\u002F\u002F The fetch-avatar path, one update arm; id reaches the model ONLY on success,\n",{"type":42,"tag":229,"props":15730,"children":15731},{"class":231,"line":241},[15732],{"type":42,"tag":229,"props":15733,"children":15734},{},[15735],{"type":48,"value":15736},"\u002F\u002F so the avatar shows initials while loading and after failure.\n",{"type":42,"tag":229,"props":15738,"children":15739},{"class":231,"line":251},[15740],{"type":42,"tag":229,"props":15741,"children":15742},{},[15743],{"type":48,"value":15744},".fetched => |response| {\n",{"type":42,"tag":229,"props":15746,"children":15747},{"class":231,"line":260},[15748],{"type":42,"tag":229,"props":15749,"children":15750},{},[15751],{"type":48,"value":15752},"    if (response.outcome == .ok and response.status == 200) {\n",{"type":42,"tag":229,"props":15754,"children":15755},{"class":231,"line":269},[15756],{"type":42,"tag":229,"props":15757,"children":15758},{},[15759],{"type":48,"value":15760},"        _ = fx.registerImageBytes(avatar_image_id, response.body) catch return;\n",{"type":42,"tag":229,"props":15762,"children":15763},{"class":231,"line":278},[15764],{"type":42,"tag":229,"props":15765,"children":15766},{},[15767],{"type":48,"value":15768},"        model.avatar_image = avatar_image_id;\n",{"type":42,"tag":229,"props":15770,"children":15771},{"class":231,"line":287},[15772],{"type":42,"tag":229,"props":15773,"children":15774},{},[15775],{"type":48,"value":956},{"type":42,"tag":229,"props":15777,"children":15778},{"class":231,"line":296},[15779],{"type":42,"tag":229,"props":15780,"children":15781},{},[15782],{"type":48,"value":13128},{"type":42,"tag":219,"props":15784,"children":15786},{"className":221,"code":15785,"language":18,"meta":223,"style":223},"\u002F\u002F Zig views (image and icon content is markup-excluded):\nui.avatar(.{ .image = model.avatar_image, .semantics = .{ .label = \"Octocat\" } }, \"OC\"),\nui.image(.{ .image = model.chart_image, .width = 120, .height = 80, .semantics = .{ .label = \"Chart\" } }),\n",[15787],{"type":42,"tag":65,"props":15788,"children":15789},{"__ignoreMap":223},[15790,15798,15806],{"type":42,"tag":229,"props":15791,"children":15792},{"class":231,"line":232},[15793],{"type":42,"tag":229,"props":15794,"children":15795},{},[15796],{"type":48,"value":15797},"\u002F\u002F Zig views (image and icon content is markup-excluded):\n",{"type":42,"tag":229,"props":15799,"children":15800},{"class":231,"line":241},[15801],{"type":42,"tag":229,"props":15802,"children":15803},{},[15804],{"type":48,"value":15805},"ui.avatar(.{ .image = model.avatar_image, .semantics = .{ .label = \"Octocat\" } }, \"OC\"),\n",{"type":42,"tag":229,"props":15807,"children":15808},{"class":231,"line":251},[15809],{"type":42,"tag":229,"props":15810,"children":15811},{},[15812],{"type":48,"value":15813},"ui.image(.{ .image = model.chart_image, .width = 120, .height = 80, .semantics = .{ .label = \"Chart\" } }),\n",{"type":42,"tag":219,"props":15815,"children":15817},{"className":1248,"code":15816,"language":1250,"meta":223,"style":223},"\u003C!-- Markup avatars bind the same model id: one {binding} to the u64 ImageId\n     (a field or pub fn — never a literal); 0 renders the initials fallback. -->\n\u003Cavatar image=\"{avatar_image}\" label=\"Octocat\">OC\u003C\u002Favatar>\n",[15818],{"type":42,"tag":65,"props":15819,"children":15820},{"__ignoreMap":223},[15821,15830,15838],{"type":42,"tag":229,"props":15822,"children":15823},{"class":231,"line":232},[15824],{"type":42,"tag":229,"props":15825,"children":15827},{"style":15826},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[15828],{"type":48,"value":15829},"\u003C!-- Markup avatars bind the same model id: one {binding} to the u64 ImageId\n",{"type":42,"tag":229,"props":15831,"children":15832},{"class":231,"line":241},[15833],{"type":42,"tag":229,"props":15834,"children":15835},{"style":15826},[15836],{"type":48,"value":15837},"     (a field or pub fn — never a literal); 0 renders the initials fallback. -->\n",{"type":42,"tag":229,"props":15839,"children":15840},{"class":231,"line":251},[15841,15845,15849,15854,15858,15862,15867,15871,15875,15879,15883,15888,15892,15896,15901,15905,15909],{"type":42,"tag":229,"props":15842,"children":15843},{"style":1260},[15844],{"type":48,"value":1263},{"type":42,"tag":229,"props":15846,"children":15847},{"style":1266},[15848],{"type":48,"value":2842},{"type":42,"tag":229,"props":15850,"children":15851},{"style":1272},[15852],{"type":48,"value":15853}," image",{"type":42,"tag":229,"props":15855,"children":15856},{"style":1260},[15857],{"type":48,"value":1280},{"type":42,"tag":229,"props":15859,"children":15860},{"style":1260},[15861],{"type":48,"value":1285},{"type":42,"tag":229,"props":15863,"children":15864},{"style":1288},[15865],{"type":48,"value":15866},"{avatar_image}",{"type":42,"tag":229,"props":15868,"children":15869},{"style":1260},[15870],{"type":48,"value":1285},{"type":42,"tag":229,"props":15872,"children":15873},{"style":1272},[15874],{"type":48,"value":1300},{"type":42,"tag":229,"props":15876,"children":15877},{"style":1260},[15878],{"type":48,"value":1280},{"type":42,"tag":229,"props":15880,"children":15881},{"style":1260},[15882],{"type":48,"value":1285},{"type":42,"tag":229,"props":15884,"children":15885},{"style":1288},[15886],{"type":48,"value":15887},"Octocat",{"type":42,"tag":229,"props":15889,"children":15890},{"style":1260},[15891],{"type":48,"value":1285},{"type":42,"tag":229,"props":15893,"children":15894},{"style":1260},[15895],{"type":48,"value":1361},{"type":42,"tag":229,"props":15897,"children":15898},{"style":1364},[15899],{"type":48,"value":15900},"OC",{"type":42,"tag":229,"props":15902,"children":15903},{"style":1260},[15904],{"type":48,"value":1371},{"type":42,"tag":229,"props":15906,"children":15907},{"style":1266},[15908],{"type":48,"value":2842},{"type":42,"tag":229,"props":15910,"children":15911},{"style":1260},[15912],{"type":48,"value":1322},{"type":42,"tag":51,"props":15914,"children":15915},{},[15916],{"type":48,"value":8059},{"type":42,"tag":57,"props":15918,"children":15919},{},[15920,15976,15989,16061,16082,16095],{"type":42,"tag":61,"props":15921,"children":15922},{},[15923,15929,15931,15937,15939,15945,15947,15953,15955,15961,15962,15968,15969,15975],{"type":42,"tag":65,"props":15924,"children":15926},{"className":15925},[],[15927],{"type":48,"value":15928},"fx.registerImage(id, w, h, rgba8)",{"type":48,"value":15930}," takes already-decoded straight-alpha RGBA8 (exactly ",{"type":42,"tag":65,"props":15932,"children":15934},{"className":15933},[],[15935],{"type":48,"value":15936},"w*h*4",{"type":48,"value":15938}," bytes; the runtime copies — your buffer is free on return). ",{"type":42,"tag":65,"props":15940,"children":15942},{"className":15941},[],[15943],{"type":48,"value":15944},"fx.registerImageBytes(id, bytes)",{"type":48,"value":15946}," decodes first. ",{"type":42,"tag":65,"props":15948,"children":15950},{"className":15949},[],[15951],{"type":48,"value":15952},"fx.unregisterImage(id)",{"type":48,"value":15954}," frees the slot. Outside UiApp: ",{"type":42,"tag":65,"props":15956,"children":15958},{"className":15957},[],[15959],{"type":48,"value":15960},"Runtime.registerCanvasImage",{"type":48,"value":7747},{"type":42,"tag":65,"props":15963,"children":15965},{"className":15964},[],[15966],{"type":48,"value":15967},"registerCanvasImageBytes",{"type":48,"value":7747},{"type":42,"tag":65,"props":15970,"children":15972},{"className":15971},[],[15973],{"type":48,"value":15974},"unregisterCanvasImage",{"type":48,"value":123},{"type":42,"tag":61,"props":15977,"children":15978},{},[15979,15981,15987],{"type":48,"value":15980},"Re-registering an id replaces the pixels; every view repaints and GPU caches re-upload off the changed content fingerprint — no invalidation calls. For caches, mint fresh ids (effect-key style, monotonically increasing) and ",{"type":42,"tag":65,"props":15982,"children":15984},{"className":15983},[],[15985],{"type":48,"value":15986},"unregisterImage",{"type":48,"value":15988}," the evictee — never re-key different content onto a live id.",{"type":42,"tag":61,"props":15990,"children":15991},{},[15992,15994,16000,16002,16008,16010,16016,16018,16024,16025,16031,16032,16038,16039,16045,16046,16052,16053,16059],{"type":48,"value":15993},"Bounded and loud (",{"type":42,"tag":65,"props":15995,"children":15997},{"className":15996},[],[15998],{"type":48,"value":15999},"canvas_limits",{"type":48,"value":16001},"): 16 slots (",{"type":42,"tag":65,"props":16003,"children":16005},{"className":16004},[],[16006],{"type":48,"value":16007},"max_registered_canvas_images",{"type":48,"value":16009},"), 1 MiB per image (",{"type":42,"tag":65,"props":16011,"children":16013},{"className":16012},[],[16014],{"type":48,"value":16015},"max_registered_canvas_image_pixel_bytes",{"type":48,"value":16017},", 512×512 RGBA8 — avatar\u002Ficon scale). Errors: ",{"type":42,"tag":65,"props":16019,"children":16021},{"className":16020},[],[16022],{"type":48,"value":16023},"error.ImageRegistryFull",{"type":48,"value":1820},{"type":42,"tag":65,"props":16026,"children":16028},{"className":16027},[],[16029],{"type":48,"value":16030},"error.ImageTooLarge",{"type":48,"value":1820},{"type":42,"tag":65,"props":16033,"children":16035},{"className":16034},[],[16036],{"type":48,"value":16037},"error.ImageDecodeFailed",{"type":48,"value":1820},{"type":42,"tag":65,"props":16040,"children":16042},{"className":16041},[],[16043],{"type":48,"value":16044},"error.InvalidImageId",{"type":48,"value":456},{"type":42,"tag":65,"props":16047,"children":16049},{"className":16048},[],[16050],{"type":48,"value":16051},"InvalidImageDimensions",{"type":48,"value":1820},{"type":42,"tag":65,"props":16054,"children":16056},{"className":16055},[],[16057],{"type":48,"value":16058},"error.UnsupportedService",{"type":48,"value":16060}," (codec-less platform).",{"type":42,"tag":61,"props":16062,"children":16063},{},[16064,16066,16072,16074,16080],{"type":48,"value":16065},"A draw referencing an unregistered id skips — a transient loading state can never fail presentation. ",{"type":42,"tag":65,"props":16067,"children":16069},{"className":16068},[],[16070],{"type":48,"value":16071},"ui.avatar",{"type":48,"value":16073}," clips a set image to the circle (",{"type":42,"tag":65,"props":16075,"children":16077},{"className":16076},[],[16078],{"type":48,"value":16079},"cover",{"type":48,"value":16081}," fit) and renders the initials argument otherwise.",{"type":42,"tag":61,"props":16083,"children":16084},{},[16085,16087,16093],{"type":48,"value":16086},"Registered images render in live presentation AND ",{"type":42,"tag":65,"props":16088,"children":16090},{"className":16089},[],[16091],{"type":48,"value":16092},"renderCanvasScreenshot",{"type":48,"value":16094},"\u002Fautomation screenshots, so goldens can assert on them.",{"type":42,"tag":61,"props":16096,"children":16097},{},[16098,16100,16106,16108,16114,16116,16122],{"type":48,"value":16099},"Deterministic tests: ",{"type":42,"tag":65,"props":16101,"children":16103},{"className":16102},[],[16104],{"type":48,"value":16105},"harness.null_platform.image_decode = true",{"type":48,"value":16107}," enables a strict decoder for the exact PNG subset ",{"type":42,"tag":65,"props":16109,"children":16111},{"className":16110},[],[16112],{"type":48,"value":16113},"canvas.png.writeRgba8",{"type":48,"value":16115}," emits — encode a raw RGBA fixture with the canvas PNG writer and drive the full decode→register→draw path with no bundled codec (",{"type":42,"tag":65,"props":16117,"children":16119},{"className":16118},[],[16120],{"type":48,"value":16121},"src\u002Fruntime\u002Fcanvas_image_tests.zig",{"type":48,"value":16123}," is the reference).",{"type":42,"tag":212,"props":16125,"children":16127},{"id":16126},"structure-tags",[16128],{"type":48,"value":16129},"Structure tags",{"type":42,"tag":219,"props":16131,"children":16133},{"className":1248,"code":16132,"language":1250,"meta":223,"style":223},"\u003Cfor each=\"visible\" key=\"id\" as=\"t\"> \u003Crow>...\u003C\u002Frow> \u003C\u002Ffor>   \u003C!-- one or more element children; key names an item field -->\n\u003Cif test=\"{c.movable}\"> \u003Cbutton ...>Move\u003C\u002Fbutton> \u003C\u002Fif>\n\u003Celse> \u003Ctext>Done!\u003C\u002Ftext> \u003C\u002Felse>                             \u003C!-- must directly follow the if -->\n",[16134],{"type":42,"tag":65,"props":16135,"children":16136},{"__ignoreMap":223},[16137,16261,16343],{"type":42,"tag":229,"props":16138,"children":16139},{"class":231,"line":232},[16140,16144,16148,16152,16156,16160,16164,16168,16172,16176,16180,16184,16188,16192,16196,16200,16205,16209,16213,16218,16222,16226,16231,16235,16239,16243,16248,16252,16256],{"type":42,"tag":229,"props":16141,"children":16142},{"style":1260},[16143],{"type":48,"value":1263},{"type":42,"tag":229,"props":16145,"children":16146},{"style":1266},[16147],{"type":48,"value":1236},{"type":42,"tag":229,"props":16149,"children":16150},{"style":1272},[16151],{"type":48,"value":5277},{"type":42,"tag":229,"props":16153,"children":16154},{"style":1260},[16155],{"type":48,"value":1280},{"type":42,"tag":229,"props":16157,"children":16158},{"style":1260},[16159],{"type":48,"value":1285},{"type":42,"tag":229,"props":16161,"children":16162},{"style":1288},[16163],{"type":48,"value":14804},{"type":42,"tag":229,"props":16165,"children":16166},{"style":1260},[16167],{"type":48,"value":1285},{"type":42,"tag":229,"props":16169,"children":16170},{"style":1272},[16171],{"type":48,"value":5880},{"type":42,"tag":229,"props":16173,"children":16174},{"style":1260},[16175],{"type":48,"value":1280},{"type":42,"tag":229,"props":16177,"children":16178},{"style":1260},[16179],{"type":48,"value":1285},{"type":42,"tag":229,"props":16181,"children":16182},{"style":1288},[16183],{"type":48,"value":6979},{"type":42,"tag":229,"props":16185,"children":16186},{"style":1260},[16187],{"type":48,"value":1285},{"type":42,"tag":229,"props":16189,"children":16190},{"style":1272},[16191],{"type":48,"value":5299},{"type":42,"tag":229,"props":16193,"children":16194},{"style":1260},[16195],{"type":48,"value":1280},{"type":42,"tag":229,"props":16197,"children":16198},{"style":1260},[16199],{"type":48,"value":1285},{"type":42,"tag":229,"props":16201,"children":16202},{"style":1288},[16203],{"type":48,"value":16204},"t",{"type":42,"tag":229,"props":16206,"children":16207},{"style":1260},[16208],{"type":48,"value":1285},{"type":42,"tag":229,"props":16210,"children":16211},{"style":1260},[16212],{"type":48,"value":1361},{"type":42,"tag":229,"props":16214,"children":16215},{"style":1260},[16216],{"type":48,"value":16217}," \u003C",{"type":42,"tag":229,"props":16219,"children":16220},{"style":1266},[16221],{"type":48,"value":1963},{"type":42,"tag":229,"props":16223,"children":16224},{"style":1260},[16225],{"type":48,"value":1361},{"type":42,"tag":229,"props":16227,"children":16228},{"style":1364},[16229],{"type":48,"value":16230},"...",{"type":42,"tag":229,"props":16232,"children":16233},{"style":1260},[16234],{"type":48,"value":1371},{"type":42,"tag":229,"props":16236,"children":16237},{"style":1266},[16238],{"type":48,"value":1963},{"type":42,"tag":229,"props":16240,"children":16241},{"style":1260},[16242],{"type":48,"value":1361},{"type":42,"tag":229,"props":16244,"children":16245},{"style":1260},[16246],{"type":48,"value":16247}," \u003C\u002F",{"type":42,"tag":229,"props":16249,"children":16250},{"style":1266},[16251],{"type":48,"value":1236},{"type":42,"tag":229,"props":16253,"children":16254},{"style":1260},[16255],{"type":48,"value":1361},{"type":42,"tag":229,"props":16257,"children":16258},{"style":15826},[16259],{"type":48,"value":16260},"   \u003C!-- one or more element children; key names an item field -->\n",{"type":42,"tag":229,"props":16262,"children":16263},{"class":231,"line":241},[16264,16268,16272,16276,16280,16284,16289,16293,16297,16301,16305,16310,16314,16319,16323,16327,16331,16335,16339],{"type":42,"tag":229,"props":16265,"children":16266},{"style":1260},[16267],{"type":48,"value":1263},{"type":42,"tag":229,"props":16269,"children":16270},{"style":1266},[16271],{"type":48,"value":1222},{"type":42,"tag":229,"props":16273,"children":16274},{"style":1272},[16275],{"type":48,"value":1412},{"type":42,"tag":229,"props":16277,"children":16278},{"style":1260},[16279],{"type":48,"value":1280},{"type":42,"tag":229,"props":16281,"children":16282},{"style":1260},[16283],{"type":48,"value":1285},{"type":42,"tag":229,"props":16285,"children":16286},{"style":1288},[16287],{"type":48,"value":16288},"{c.movable}",{"type":42,"tag":229,"props":16290,"children":16291},{"style":1260},[16292],{"type":48,"value":1285},{"type":42,"tag":229,"props":16294,"children":16295},{"style":1260},[16296],{"type":48,"value":1361},{"type":42,"tag":229,"props":16298,"children":16299},{"style":1260},[16300],{"type":48,"value":16217},{"type":42,"tag":229,"props":16302,"children":16303},{"style":1266},[16304],{"type":48,"value":2795},{"type":42,"tag":229,"props":16306,"children":16307},{"style":1272},[16308],{"type":48,"value":16309}," ...",{"type":42,"tag":229,"props":16311,"children":16312},{"style":1260},[16313],{"type":48,"value":1361},{"type":42,"tag":229,"props":16315,"children":16316},{"style":1364},[16317],{"type":48,"value":16318},"Move",{"type":42,"tag":229,"props":16320,"children":16321},{"style":1260},[16322],{"type":48,"value":1371},{"type":42,"tag":229,"props":16324,"children":16325},{"style":1266},[16326],{"type":48,"value":2795},{"type":42,"tag":229,"props":16328,"children":16329},{"style":1260},[16330],{"type":48,"value":1361},{"type":42,"tag":229,"props":16332,"children":16333},{"style":1260},[16334],{"type":48,"value":16247},{"type":42,"tag":229,"props":16336,"children":16337},{"style":1266},[16338],{"type":48,"value":1222},{"type":42,"tag":229,"props":16340,"children":16341},{"style":1260},[16342],{"type":48,"value":1322},{"type":42,"tag":229,"props":16344,"children":16345},{"class":231,"line":251},[16346,16350,16354,16358,16362,16366,16370,16375,16379,16383,16387,16391,16395,16399],{"type":42,"tag":229,"props":16347,"children":16348},{"style":1260},[16349],{"type":48,"value":1263},{"type":42,"tag":229,"props":16351,"children":16352},{"style":1266},[16353],{"type":48,"value":1229},{"type":42,"tag":229,"props":16355,"children":16356},{"style":1260},[16357],{"type":48,"value":1361},{"type":42,"tag":229,"props":16359,"children":16360},{"style":1260},[16361],{"type":48,"value":16217},{"type":42,"tag":229,"props":16363,"children":16364},{"style":1266},[16365],{"type":48,"value":48},{"type":42,"tag":229,"props":16367,"children":16368},{"style":1260},[16369],{"type":48,"value":1361},{"type":42,"tag":229,"props":16371,"children":16372},{"style":1364},[16373],{"type":48,"value":16374},"Done!",{"type":42,"tag":229,"props":16376,"children":16377},{"style":1260},[16378],{"type":48,"value":1371},{"type":42,"tag":229,"props":16380,"children":16381},{"style":1266},[16382],{"type":48,"value":48},{"type":42,"tag":229,"props":16384,"children":16385},{"style":1260},[16386],{"type":48,"value":1361},{"type":42,"tag":229,"props":16388,"children":16389},{"style":1260},[16390],{"type":48,"value":16247},{"type":42,"tag":229,"props":16392,"children":16393},{"style":1266},[16394],{"type":48,"value":1229},{"type":42,"tag":229,"props":16396,"children":16397},{"style":1260},[16398],{"type":48,"value":1361},{"type":42,"tag":229,"props":16400,"children":16401},{"style":15826},[16402],{"type":48,"value":16403},"                             \u003C!-- must directly follow the if -->\n",{"type":42,"tag":51,"props":16405,"children":16406},{},[16407,16408,16413,16415,16421,16422,16427,16428,16434,16436,16441,16443,16448,16449,16454,16456,16461,16463,16468,16469,16474,16476,16481,16483,16488,16490,16496],{"type":48,"value":5452},{"type":42,"tag":65,"props":16409,"children":16411},{"className":16410},[],[16412],{"type":48,"value":6310},{"type":48,"value":16414}," body takes one or more children — elements, ",{"type":42,"tag":65,"props":16416,"children":16418},{"className":16417},[],[16419],{"type":48,"value":16420},"\u003Cuse>",{"type":48,"value":1820},{"type":42,"tag":65,"props":16423,"children":16425},{"className":16424},[],[16426],{"type":48,"value":2422},{"type":48,"value":456},{"type":42,"tag":65,"props":16429,"children":16431},{"className":16430},[],[16432],{"type":48,"value":16433},"\u003Celse>",{"type":48,"value":16435}," arms, or nested ",{"type":42,"tag":65,"props":16437,"children":16439},{"className":16438},[],[16440],{"type":48,"value":6310},{"type":48,"value":16442},"s — so polymorphic rows need no wrapper node: put the ",{"type":42,"tag":65,"props":16444,"children":16446},{"className":16445},[],[16447],{"type":48,"value":2422},{"type":48,"value":456},{"type":42,"tag":65,"props":16450,"children":16452},{"className":16451},[],[16453],{"type":48,"value":16433},{"type":48,"value":16455}," arms directly in the body and each item emits whichever arm wins. With ",{"type":42,"tag":65,"props":16457,"children":16459},{"className":16458},[],[16460],{"type":48,"value":3595},{"type":48,"value":16462},", every node an item emits shares the item's identity (same-kind siblings within one item are disambiguated automatically); a node's own ",{"type":42,"tag":65,"props":16464,"children":16466},{"className":16465},[],[16467],{"type":48,"value":3595},{"type":48,"value":456},{"type":42,"tag":65,"props":16470,"children":16472},{"className":16471},[],[16473],{"type":48,"value":3602},{"type":48,"value":16475}," still wins. Unkeyed same-kind siblings take POSITIONAL identity (sibling index), so an ",{"type":42,"tag":65,"props":16477,"children":16479},{"className":16478},[],[16480],{"type":48,"value":2422},{"type":48,"value":16482}," that inserts or removes an earlier same-kind sibling re-disambiguates the trailing ones — engine-owned state like carets and scroll offsets can hop; keyed items and keyed ancestors hold identity. An ",{"type":42,"tag":65,"props":16484,"children":16486},{"className":16485},[],[16487],{"type":48,"value":16433},{"type":48,"value":16489}," directly after a ",{"type":42,"tag":65,"props":16491,"children":16493},{"className":16492},[],[16494],{"type":48,"value":16495},"\u003C\u002Ffor>",{"type":48,"value":16497}," renders the empty state when the iterable has no items:",{"type":42,"tag":219,"props":16499,"children":16501},{"className":1248,"code":16500,"language":1250,"meta":223,"style":223},"\u003Cfor each=\"visible\" key=\"id\" as=\"t\">\n  \u003Cif test=\"{t.done}\"> \u003Cbadge>done\u003C\u002Fbadge> \u003C\u002Fif>\n  \u003Celse> \u003Ctext>{t.title}\u003C\u002Ftext> \u003C\u002Felse>\n\u003C\u002Ffor>\n\u003Celse> \u003Ctext>Nothing yet\u003C\u002Ftext> \u003C\u002Felse>                       \u003C!-- renders when visible is empty -->\n",[16502],{"type":42,"tag":65,"props":16503,"children":16504},{"__ignoreMap":223},[16505,16580,16657,16713,16728],{"type":42,"tag":229,"props":16506,"children":16507},{"class":231,"line":232},[16508,16512,16516,16520,16524,16528,16532,16536,16540,16544,16548,16552,16556,16560,16564,16568,16572,16576],{"type":42,"tag":229,"props":16509,"children":16510},{"style":1260},[16511],{"type":48,"value":1263},{"type":42,"tag":229,"props":16513,"children":16514},{"style":1266},[16515],{"type":48,"value":1236},{"type":42,"tag":229,"props":16517,"children":16518},{"style":1272},[16519],{"type":48,"value":5277},{"type":42,"tag":229,"props":16521,"children":16522},{"style":1260},[16523],{"type":48,"value":1280},{"type":42,"tag":229,"props":16525,"children":16526},{"style":1260},[16527],{"type":48,"value":1285},{"type":42,"tag":229,"props":16529,"children":16530},{"style":1288},[16531],{"type":48,"value":14804},{"type":42,"tag":229,"props":16533,"children":16534},{"style":1260},[16535],{"type":48,"value":1285},{"type":42,"tag":229,"props":16537,"children":16538},{"style":1272},[16539],{"type":48,"value":5880},{"type":42,"tag":229,"props":16541,"children":16542},{"style":1260},[16543],{"type":48,"value":1280},{"type":42,"tag":229,"props":16545,"children":16546},{"style":1260},[16547],{"type":48,"value":1285},{"type":42,"tag":229,"props":16549,"children":16550},{"style":1288},[16551],{"type":48,"value":6979},{"type":42,"tag":229,"props":16553,"children":16554},{"style":1260},[16555],{"type":48,"value":1285},{"type":42,"tag":229,"props":16557,"children":16558},{"style":1272},[16559],{"type":48,"value":5299},{"type":42,"tag":229,"props":16561,"children":16562},{"style":1260},[16563],{"type":48,"value":1280},{"type":42,"tag":229,"props":16565,"children":16566},{"style":1260},[16567],{"type":48,"value":1285},{"type":42,"tag":229,"props":16569,"children":16570},{"style":1288},[16571],{"type":48,"value":16204},{"type":42,"tag":229,"props":16573,"children":16574},{"style":1260},[16575],{"type":48,"value":1285},{"type":42,"tag":229,"props":16577,"children":16578},{"style":1260},[16579],{"type":48,"value":1322},{"type":42,"tag":229,"props":16581,"children":16582},{"class":231,"line":241},[16583,16587,16591,16595,16599,16603,16608,16612,16616,16620,16624,16628,16633,16637,16641,16645,16649,16653],{"type":42,"tag":229,"props":16584,"children":16585},{"style":1260},[16586],{"type":48,"value":1330},{"type":42,"tag":229,"props":16588,"children":16589},{"style":1266},[16590],{"type":48,"value":1222},{"type":42,"tag":229,"props":16592,"children":16593},{"style":1272},[16594],{"type":48,"value":1412},{"type":42,"tag":229,"props":16596,"children":16597},{"style":1260},[16598],{"type":48,"value":1280},{"type":42,"tag":229,"props":16600,"children":16601},{"style":1260},[16602],{"type":48,"value":1285},{"type":42,"tag":229,"props":16604,"children":16605},{"style":1288},[16606],{"type":48,"value":16607},"{t.done}",{"type":42,"tag":229,"props":16609,"children":16610},{"style":1260},[16611],{"type":48,"value":1285},{"type":42,"tag":229,"props":16613,"children":16614},{"style":1260},[16615],{"type":48,"value":1361},{"type":42,"tag":229,"props":16617,"children":16618},{"style":1260},[16619],{"type":48,"value":16217},{"type":42,"tag":229,"props":16621,"children":16622},{"style":1266},[16623],{"type":48,"value":2580},{"type":42,"tag":229,"props":16625,"children":16626},{"style":1260},[16627],{"type":48,"value":1361},{"type":42,"tag":229,"props":16629,"children":16630},{"style":1364},[16631],{"type":48,"value":16632},"done",{"type":42,"tag":229,"props":16634,"children":16635},{"style":1260},[16636],{"type":48,"value":1371},{"type":42,"tag":229,"props":16638,"children":16639},{"style":1266},[16640],{"type":48,"value":2580},{"type":42,"tag":229,"props":16642,"children":16643},{"style":1260},[16644],{"type":48,"value":1361},{"type":42,"tag":229,"props":16646,"children":16647},{"style":1260},[16648],{"type":48,"value":16247},{"type":42,"tag":229,"props":16650,"children":16651},{"style":1266},[16652],{"type":48,"value":1222},{"type":42,"tag":229,"props":16654,"children":16655},{"style":1260},[16656],{"type":48,"value":1322},{"type":42,"tag":229,"props":16658,"children":16659},{"class":231,"line":251},[16660,16664,16668,16672,16676,16680,16684,16689,16693,16697,16701,16705,16709],{"type":42,"tag":229,"props":16661,"children":16662},{"style":1260},[16663],{"type":48,"value":1330},{"type":42,"tag":229,"props":16665,"children":16666},{"style":1266},[16667],{"type":48,"value":1229},{"type":42,"tag":229,"props":16669,"children":16670},{"style":1260},[16671],{"type":48,"value":1361},{"type":42,"tag":229,"props":16673,"children":16674},{"style":1260},[16675],{"type":48,"value":16217},{"type":42,"tag":229,"props":16677,"children":16678},{"style":1266},[16679],{"type":48,"value":48},{"type":42,"tag":229,"props":16681,"children":16682},{"style":1260},[16683],{"type":48,"value":1361},{"type":42,"tag":229,"props":16685,"children":16686},{"style":1364},[16687],{"type":48,"value":16688},"{t.title}",{"type":42,"tag":229,"props":16690,"children":16691},{"style":1260},[16692],{"type":48,"value":1371},{"type":42,"tag":229,"props":16694,"children":16695},{"style":1266},[16696],{"type":48,"value":48},{"type":42,"tag":229,"props":16698,"children":16699},{"style":1260},[16700],{"type":48,"value":1361},{"type":42,"tag":229,"props":16702,"children":16703},{"style":1260},[16704],{"type":48,"value":16247},{"type":42,"tag":229,"props":16706,"children":16707},{"style":1266},[16708],{"type":48,"value":1229},{"type":42,"tag":229,"props":16710,"children":16711},{"style":1260},[16712],{"type":48,"value":1322},{"type":42,"tag":229,"props":16714,"children":16715},{"class":231,"line":260},[16716,16720,16724],{"type":42,"tag":229,"props":16717,"children":16718},{"style":1260},[16719],{"type":48,"value":1371},{"type":42,"tag":229,"props":16721,"children":16722},{"style":1266},[16723],{"type":48,"value":1236},{"type":42,"tag":229,"props":16725,"children":16726},{"style":1260},[16727],{"type":48,"value":1322},{"type":42,"tag":229,"props":16729,"children":16730},{"class":231,"line":269},[16731,16735,16739,16743,16747,16751,16755,16760,16764,16768,16772,16776,16780,16784],{"type":42,"tag":229,"props":16732,"children":16733},{"style":1260},[16734],{"type":48,"value":1263},{"type":42,"tag":229,"props":16736,"children":16737},{"style":1266},[16738],{"type":48,"value":1229},{"type":42,"tag":229,"props":16740,"children":16741},{"style":1260},[16742],{"type":48,"value":1361},{"type":42,"tag":229,"props":16744,"children":16745},{"style":1260},[16746],{"type":48,"value":16217},{"type":42,"tag":229,"props":16748,"children":16749},{"style":1266},[16750],{"type":48,"value":48},{"type":42,"tag":229,"props":16752,"children":16753},{"style":1260},[16754],{"type":48,"value":1361},{"type":42,"tag":229,"props":16756,"children":16757},{"style":1364},[16758],{"type":48,"value":16759},"Nothing yet",{"type":42,"tag":229,"props":16761,"children":16762},{"style":1260},[16763],{"type":48,"value":1371},{"type":42,"tag":229,"props":16765,"children":16766},{"style":1266},[16767],{"type":48,"value":48},{"type":42,"tag":229,"props":16769,"children":16770},{"style":1260},[16771],{"type":48,"value":1361},{"type":42,"tag":229,"props":16773,"children":16774},{"style":1260},[16775],{"type":48,"value":16247},{"type":42,"tag":229,"props":16777,"children":16778},{"style":1266},[16779],{"type":48,"value":1229},{"type":42,"tag":229,"props":16781,"children":16782},{"style":1260},[16783],{"type":48,"value":1361},{"type":42,"tag":229,"props":16785,"children":16786},{"style":15826},[16787],{"type":48,"value":16788},"                       \u003C!-- renders when visible is empty -->\n",{"type":42,"tag":51,"props":16790,"children":16791},{},[16792,16794,16800,16802,16807,16808,16813,16815,16820,16822,16827],{"type":48,"value":16793},"There is no ",{"type":42,"tag":65,"props":16795,"children":16797},{"className":16796},[],[16798],{"type":48,"value":16799},"else-if",{"type":48,"value":16801}," chain tag: nest an ",{"type":42,"tag":65,"props":16803,"children":16805},{"className":16804},[],[16806],{"type":48,"value":2422},{"type":48,"value":456},{"type":42,"tag":65,"props":16809,"children":16811},{"className":16810},[],[16812],{"type":48,"value":16433},{"type":48,"value":16814}," inside the ",{"type":42,"tag":65,"props":16816,"children":16818},{"className":16817},[],[16819],{"type":48,"value":16433},{"type":48,"value":16821}," body instead. ",{"type":42,"tag":65,"props":16823,"children":16825},{"className":16824},[],[16826],{"type":48,"value":2422},{"type":48,"value":16828}," has no negation operator either — prefer an explicit boolean predicate method on the model per arm.",{"type":42,"tag":212,"props":16830,"children":16832},{"id":16831},"templates-template-use",[16833,16835,16841,16842],{"type":48,"value":16834},"Templates: ",{"type":42,"tag":65,"props":16836,"children":16838},{"className":16837},[],[16839],{"type":48,"value":16840},"\u003Ctemplate>",{"type":48,"value":3952},{"type":42,"tag":65,"props":16843,"children":16845},{"className":16844},[],[16846],{"type":48,"value":16420},{"type":42,"tag":51,"props":16848,"children":16849},{},[16850,16852,16858,16860,16865],{"type":48,"value":16851},"When the same subtree repeats with different data (board columns, dashboard sections), define it ONCE at the top of the file — zero or more ",{"type":42,"tag":65,"props":16853,"children":16855},{"className":16854},[],[16856],{"type":48,"value":16857},"\u003Cimport>",{"type":48,"value":16859}," lines, then zero or more ",{"type":42,"tag":65,"props":16861,"children":16863},{"className":16862},[],[16864],{"type":48,"value":16840},{"type":48,"value":16866}," definitions, then the view root (a file that is ALL templates is a component file, valid only as an import target):",{"type":42,"tag":219,"props":16868,"children":16870},{"className":1248,"code":16869,"language":1250,"meta":223,"style":223},"\u003Ctemplate name=\"board-column\" args=\"title cards\">\n  \u003Ccolumn grow=\"1\" gap=\"8\" label=\"{title}\">\n    \u003Ctext foreground=\"text_muted\">{title}\u003C\u002Ftext>\n    \u003Cfor each=\"cards\" key=\"id\" as=\"c\">\n      \u003Crow global-key=\"{c.id}\">\u003Ctext>{c.title}\u003C\u002Ftext>\u003C\u002Frow>\n    \u003C\u002Ffor>\n  \u003C\u002Fcolumn>\n\u003C\u002Ftemplate>\n\u003Crow grow=\"1\" gap=\"12\">\n  \u003Cuse template=\"board-column\" title=\"Todo\"  cards=\"{todoCards}\" \u002F>\n  \u003Cuse template=\"board-column\" title=\"Doing\" cards=\"{doingCards}\" \u002F>\n  \u003Cuse template=\"board-column\" title=\"Done\"  cards=\"{doneCards}\" \u002F>\n\u003C\u002Frow>\n",[16871],{"type":42,"tag":65,"props":16872,"children":16873},{"__ignoreMap":223},[16874,16933,17009,17060,17137,17206,17221,17236,17251,17307,17388,17466,17543],{"type":42,"tag":229,"props":16875,"children":16876},{"class":231,"line":232},[16877,16881,16886,16890,16894,16898,16903,16907,16912,16916,16920,16925,16929],{"type":42,"tag":229,"props":16878,"children":16879},{"style":1260},[16880],{"type":48,"value":1263},{"type":42,"tag":229,"props":16882,"children":16883},{"style":1266},[16884],{"type":48,"value":16885},"template",{"type":42,"tag":229,"props":16887,"children":16888},{"style":1272},[16889],{"type":48,"value":7197},{"type":42,"tag":229,"props":16891,"children":16892},{"style":1260},[16893],{"type":48,"value":1280},{"type":42,"tag":229,"props":16895,"children":16896},{"style":1260},[16897],{"type":48,"value":1285},{"type":42,"tag":229,"props":16899,"children":16900},{"style":1288},[16901],{"type":48,"value":16902},"board-column",{"type":42,"tag":229,"props":16904,"children":16905},{"style":1260},[16906],{"type":48,"value":1285},{"type":42,"tag":229,"props":16908,"children":16909},{"style":1272},[16910],{"type":48,"value":16911}," args",{"type":42,"tag":229,"props":16913,"children":16914},{"style":1260},[16915],{"type":48,"value":1280},{"type":42,"tag":229,"props":16917,"children":16918},{"style":1260},[16919],{"type":48,"value":1285},{"type":42,"tag":229,"props":16921,"children":16922},{"style":1288},[16923],{"type":48,"value":16924},"title cards",{"type":42,"tag":229,"props":16926,"children":16927},{"style":1260},[16928],{"type":48,"value":1285},{"type":42,"tag":229,"props":16930,"children":16931},{"style":1260},[16932],{"type":48,"value":1322},{"type":42,"tag":229,"props":16934,"children":16935},{"class":231,"line":241},[16936,16940,16944,16948,16952,16956,16960,16964,16968,16972,16976,16980,16984,16988,16992,16996,17001,17005],{"type":42,"tag":229,"props":16937,"children":16938},{"style":1260},[16939],{"type":48,"value":1330},{"type":42,"tag":229,"props":16941,"children":16942},{"style":1266},[16943],{"type":48,"value":1970},{"type":42,"tag":229,"props":16945,"children":16946},{"style":1272},[16947],{"type":48,"value":1339},{"type":42,"tag":229,"props":16949,"children":16950},{"style":1260},[16951],{"type":48,"value":1280},{"type":42,"tag":229,"props":16953,"children":16954},{"style":1260},[16955],{"type":48,"value":1285},{"type":42,"tag":229,"props":16957,"children":16958},{"style":1288},[16959],{"type":48,"value":1352},{"type":42,"tag":229,"props":16961,"children":16962},{"style":1260},[16963],{"type":48,"value":1285},{"type":42,"tag":229,"props":16965,"children":16966},{"style":1272},[16967],{"type":48,"value":5219},{"type":42,"tag":229,"props":16969,"children":16970},{"style":1260},[16971],{"type":48,"value":1280},{"type":42,"tag":229,"props":16973,"children":16974},{"style":1260},[16975],{"type":48,"value":1285},{"type":42,"tag":229,"props":16977,"children":16978},{"style":1288},[16979],{"type":48,"value":7157},{"type":42,"tag":229,"props":16981,"children":16982},{"style":1260},[16983],{"type":48,"value":1285},{"type":42,"tag":229,"props":16985,"children":16986},{"style":1272},[16987],{"type":48,"value":1300},{"type":42,"tag":229,"props":16989,"children":16990},{"style":1260},[16991],{"type":48,"value":1280},{"type":42,"tag":229,"props":16993,"children":16994},{"style":1260},[16995],{"type":48,"value":1285},{"type":42,"tag":229,"props":16997,"children":16998},{"style":1288},[16999],{"type":48,"value":17000},"{title}",{"type":42,"tag":229,"props":17002,"children":17003},{"style":1260},[17004],{"type":48,"value":1285},{"type":42,"tag":229,"props":17006,"children":17007},{"style":1260},[17008],{"type":48,"value":1322},{"type":42,"tag":229,"props":17010,"children":17011},{"class":231,"line":251},[17012,17016,17020,17024,17028,17032,17036,17040,17044,17048,17052,17056],{"type":42,"tag":229,"props":17013,"children":17014},{"style":1260},[17015],{"type":48,"value":1403},{"type":42,"tag":229,"props":17017,"children":17018},{"style":1266},[17019],{"type":48,"value":48},{"type":42,"tag":229,"props":17021,"children":17022},{"style":1272},[17023],{"type":48,"value":8742},{"type":42,"tag":229,"props":17025,"children":17026},{"style":1260},[17027],{"type":48,"value":1280},{"type":42,"tag":229,"props":17029,"children":17030},{"style":1260},[17031],{"type":48,"value":1285},{"type":42,"tag":229,"props":17033,"children":17034},{"style":1288},[17035],{"type":48,"value":8477},{"type":42,"tag":229,"props":17037,"children":17038},{"style":1260},[17039],{"type":48,"value":1285},{"type":42,"tag":229,"props":17041,"children":17042},{"style":1260},[17043],{"type":48,"value":1361},{"type":42,"tag":229,"props":17045,"children":17046},{"style":1364},[17047],{"type":48,"value":17000},{"type":42,"tag":229,"props":17049,"children":17050},{"style":1260},[17051],{"type":48,"value":1371},{"type":42,"tag":229,"props":17053,"children":17054},{"style":1266},[17055],{"type":48,"value":48},{"type":42,"tag":229,"props":17057,"children":17058},{"style":1260},[17059],{"type":48,"value":1322},{"type":42,"tag":229,"props":17061,"children":17062},{"class":231,"line":260},[17063,17067,17071,17075,17079,17083,17088,17092,17096,17100,17104,17108,17112,17116,17120,17124,17129,17133],{"type":42,"tag":229,"props":17064,"children":17065},{"style":1260},[17066],{"type":48,"value":1403},{"type":42,"tag":229,"props":17068,"children":17069},{"style":1266},[17070],{"type":48,"value":1236},{"type":42,"tag":229,"props":17072,"children":17073},{"style":1272},[17074],{"type":48,"value":5277},{"type":42,"tag":229,"props":17076,"children":17077},{"style":1260},[17078],{"type":48,"value":1280},{"type":42,"tag":229,"props":17080,"children":17081},{"style":1260},[17082],{"type":48,"value":1285},{"type":42,"tag":229,"props":17084,"children":17085},{"style":1288},[17086],{"type":48,"value":17087},"cards",{"type":42,"tag":229,"props":17089,"children":17090},{"style":1260},[17091],{"type":48,"value":1285},{"type":42,"tag":229,"props":17093,"children":17094},{"style":1272},[17095],{"type":48,"value":5880},{"type":42,"tag":229,"props":17097,"children":17098},{"style":1260},[17099],{"type":48,"value":1280},{"type":42,"tag":229,"props":17101,"children":17102},{"style":1260},[17103],{"type":48,"value":1285},{"type":42,"tag":229,"props":17105,"children":17106},{"style":1288},[17107],{"type":48,"value":6979},{"type":42,"tag":229,"props":17109,"children":17110},{"style":1260},[17111],{"type":48,"value":1285},{"type":42,"tag":229,"props":17113,"children":17114},{"style":1272},[17115],{"type":48,"value":5299},{"type":42,"tag":229,"props":17117,"children":17118},{"style":1260},[17119],{"type":48,"value":1280},{"type":42,"tag":229,"props":17121,"children":17122},{"style":1260},[17123],{"type":48,"value":1285},{"type":42,"tag":229,"props":17125,"children":17126},{"style":1288},[17127],{"type":48,"value":17128},"c",{"type":42,"tag":229,"props":17130,"children":17131},{"style":1260},[17132],{"type":48,"value":1285},{"type":42,"tag":229,"props":17134,"children":17135},{"style":1260},[17136],{"type":48,"value":1322},{"type":42,"tag":229,"props":17138,"children":17139},{"class":231,"line":269},[17140,17144,17148,17153,17157,17161,17166,17170,17174,17178,17182,17186,17190,17194,17198,17202],{"type":42,"tag":229,"props":17141,"children":17142},{"style":1260},[17143],{"type":48,"value":1441},{"type":42,"tag":229,"props":17145,"children":17146},{"style":1266},[17147],{"type":48,"value":1963},{"type":42,"tag":229,"props":17149,"children":17150},{"style":1272},[17151],{"type":48,"value":17152}," global-key",{"type":42,"tag":229,"props":17154,"children":17155},{"style":1260},[17156],{"type":48,"value":1280},{"type":42,"tag":229,"props":17158,"children":17159},{"style":1260},[17160],{"type":48,"value":1285},{"type":42,"tag":229,"props":17162,"children":17163},{"style":1288},[17164],{"type":48,"value":17165},"{c.id}",{"type":42,"tag":229,"props":17167,"children":17168},{"style":1260},[17169],{"type":48,"value":1285},{"type":42,"tag":229,"props":17171,"children":17172},{"style":1260},[17173],{"type":48,"value":7188},{"type":42,"tag":229,"props":17175,"children":17176},{"style":1266},[17177],{"type":48,"value":48},{"type":42,"tag":229,"props":17179,"children":17180},{"style":1260},[17181],{"type":48,"value":1361},{"type":42,"tag":229,"props":17183,"children":17184},{"style":1364},[17185],{"type":48,"value":8927},{"type":42,"tag":229,"props":17187,"children":17188},{"style":1260},[17189],{"type":48,"value":1371},{"type":42,"tag":229,"props":17191,"children":17192},{"style":1266},[17193],{"type":48,"value":48},{"type":42,"tag":229,"props":17195,"children":17196},{"style":1260},[17197],{"type":48,"value":7265},{"type":42,"tag":229,"props":17199,"children":17200},{"style":1266},[17201],{"type":48,"value":1963},{"type":42,"tag":229,"props":17203,"children":17204},{"style":1260},[17205],{"type":48,"value":1322},{"type":42,"tag":229,"props":17207,"children":17208},{"class":231,"line":278},[17209,17213,17217],{"type":42,"tag":229,"props":17210,"children":17211},{"style":1260},[17212],{"type":48,"value":1548},{"type":42,"tag":229,"props":17214,"children":17215},{"style":1266},[17216],{"type":48,"value":1236},{"type":42,"tag":229,"props":17218,"children":17219},{"style":1260},[17220],{"type":48,"value":1322},{"type":42,"tag":229,"props":17222,"children":17223},{"class":231,"line":287},[17224,17228,17232],{"type":42,"tag":229,"props":17225,"children":17226},{"style":1260},[17227],{"type":48,"value":1700},{"type":42,"tag":229,"props":17229,"children":17230},{"style":1266},[17231],{"type":48,"value":1970},{"type":42,"tag":229,"props":17233,"children":17234},{"style":1260},[17235],{"type":48,"value":1322},{"type":42,"tag":229,"props":17237,"children":17238},{"class":231,"line":296},[17239,17243,17247],{"type":42,"tag":229,"props":17240,"children":17241},{"style":1260},[17242],{"type":48,"value":1371},{"type":42,"tag":229,"props":17244,"children":17245},{"style":1266},[17246],{"type":48,"value":16885},{"type":42,"tag":229,"props":17248,"children":17249},{"style":1260},[17250],{"type":48,"value":1322},{"type":42,"tag":229,"props":17252,"children":17253},{"class":231,"line":305},[17254,17258,17262,17266,17270,17274,17278,17282,17286,17290,17294,17299,17303],{"type":42,"tag":229,"props":17255,"children":17256},{"style":1260},[17257],{"type":48,"value":1263},{"type":42,"tag":229,"props":17259,"children":17260},{"style":1266},[17261],{"type":48,"value":1963},{"type":42,"tag":229,"props":17263,"children":17264},{"style":1272},[17265],{"type":48,"value":1339},{"type":42,"tag":229,"props":17267,"children":17268},{"style":1260},[17269],{"type":48,"value":1280},{"type":42,"tag":229,"props":17271,"children":17272},{"style":1260},[17273],{"type":48,"value":1285},{"type":42,"tag":229,"props":17275,"children":17276},{"style":1288},[17277],{"type":48,"value":1352},{"type":42,"tag":229,"props":17279,"children":17280},{"style":1260},[17281],{"type":48,"value":1285},{"type":42,"tag":229,"props":17283,"children":17284},{"style":1272},[17285],{"type":48,"value":5219},{"type":42,"tag":229,"props":17287,"children":17288},{"style":1260},[17289],{"type":48,"value":1280},{"type":42,"tag":229,"props":17291,"children":17292},{"style":1260},[17293],{"type":48,"value":1285},{"type":42,"tag":229,"props":17295,"children":17296},{"style":1288},[17297],{"type":48,"value":17298},"12",{"type":42,"tag":229,"props":17300,"children":17301},{"style":1260},[17302],{"type":48,"value":1285},{"type":42,"tag":229,"props":17304,"children":17305},{"style":1260},[17306],{"type":48,"value":1322},{"type":42,"tag":229,"props":17308,"children":17309},{"class":231,"line":314},[17310,17314,17319,17324,17328,17332,17336,17340,17345,17349,17353,17358,17362,17367,17371,17375,17380,17384],{"type":42,"tag":229,"props":17311,"children":17312},{"style":1260},[17313],{"type":48,"value":1330},{"type":42,"tag":229,"props":17315,"children":17316},{"style":1266},[17317],{"type":48,"value":17318},"use",{"type":42,"tag":229,"props":17320,"children":17321},{"style":1272},[17322],{"type":48,"value":17323}," template",{"type":42,"tag":229,"props":17325,"children":17326},{"style":1260},[17327],{"type":48,"value":1280},{"type":42,"tag":229,"props":17329,"children":17330},{"style":1260},[17331],{"type":48,"value":1285},{"type":42,"tag":229,"props":17333,"children":17334},{"style":1288},[17335],{"type":48,"value":16902},{"type":42,"tag":229,"props":17337,"children":17338},{"style":1260},[17339],{"type":48,"value":1285},{"type":42,"tag":229,"props":17341,"children":17342},{"style":1272},[17343],{"type":48,"value":17344}," title",{"type":42,"tag":229,"props":17346,"children":17347},{"style":1260},[17348],{"type":48,"value":1280},{"type":42,"tag":229,"props":17350,"children":17351},{"style":1260},[17352],{"type":48,"value":1285},{"type":42,"tag":229,"props":17354,"children":17355},{"style":1288},[17356],{"type":48,"value":17357},"Todo",{"type":42,"tag":229,"props":17359,"children":17360},{"style":1260},[17361],{"type":48,"value":1285},{"type":42,"tag":229,"props":17363,"children":17364},{"style":1272},[17365],{"type":48,"value":17366},"  cards",{"type":42,"tag":229,"props":17368,"children":17369},{"style":1260},[17370],{"type":48,"value":1280},{"type":42,"tag":229,"props":17372,"children":17373},{"style":1260},[17374],{"type":48,"value":1285},{"type":42,"tag":229,"props":17376,"children":17377},{"style":1288},[17378],{"type":48,"value":17379},"{todoCards}",{"type":42,"tag":229,"props":17381,"children":17382},{"style":1260},[17383],{"type":48,"value":1285},{"type":42,"tag":229,"props":17385,"children":17386},{"style":1260},[17387],{"type":48,"value":10935},{"type":42,"tag":229,"props":17389,"children":17390},{"class":231,"line":323},[17391,17395,17399,17403,17407,17411,17415,17419,17423,17427,17431,17436,17440,17445,17449,17453,17458,17462],{"type":42,"tag":229,"props":17392,"children":17393},{"style":1260},[17394],{"type":48,"value":1330},{"type":42,"tag":229,"props":17396,"children":17397},{"style":1266},[17398],{"type":48,"value":17318},{"type":42,"tag":229,"props":17400,"children":17401},{"style":1272},[17402],{"type":48,"value":17323},{"type":42,"tag":229,"props":17404,"children":17405},{"style":1260},[17406],{"type":48,"value":1280},{"type":42,"tag":229,"props":17408,"children":17409},{"style":1260},[17410],{"type":48,"value":1285},{"type":42,"tag":229,"props":17412,"children":17413},{"style":1288},[17414],{"type":48,"value":16902},{"type":42,"tag":229,"props":17416,"children":17417},{"style":1260},[17418],{"type":48,"value":1285},{"type":42,"tag":229,"props":17420,"children":17421},{"style":1272},[17422],{"type":48,"value":17344},{"type":42,"tag":229,"props":17424,"children":17425},{"style":1260},[17426],{"type":48,"value":1280},{"type":42,"tag":229,"props":17428,"children":17429},{"style":1260},[17430],{"type":48,"value":1285},{"type":42,"tag":229,"props":17432,"children":17433},{"style":1288},[17434],{"type":48,"value":17435},"Doing",{"type":42,"tag":229,"props":17437,"children":17438},{"style":1260},[17439],{"type":48,"value":1285},{"type":42,"tag":229,"props":17441,"children":17442},{"style":1272},[17443],{"type":48,"value":17444}," cards",{"type":42,"tag":229,"props":17446,"children":17447},{"style":1260},[17448],{"type":48,"value":1280},{"type":42,"tag":229,"props":17450,"children":17451},{"style":1260},[17452],{"type":48,"value":1285},{"type":42,"tag":229,"props":17454,"children":17455},{"style":1288},[17456],{"type":48,"value":17457},"{doingCards}",{"type":42,"tag":229,"props":17459,"children":17460},{"style":1260},[17461],{"type":48,"value":1285},{"type":42,"tag":229,"props":17463,"children":17464},{"style":1260},[17465],{"type":48,"value":10935},{"type":42,"tag":229,"props":17467,"children":17468},{"class":231,"line":332},[17469,17473,17477,17481,17485,17489,17493,17497,17501,17505,17509,17514,17518,17522,17526,17530,17535,17539],{"type":42,"tag":229,"props":17470,"children":17471},{"style":1260},[17472],{"type":48,"value":1330},{"type":42,"tag":229,"props":17474,"children":17475},{"style":1266},[17476],{"type":48,"value":17318},{"type":42,"tag":229,"props":17478,"children":17479},{"style":1272},[17480],{"type":48,"value":17323},{"type":42,"tag":229,"props":17482,"children":17483},{"style":1260},[17484],{"type":48,"value":1280},{"type":42,"tag":229,"props":17486,"children":17487},{"style":1260},[17488],{"type":48,"value":1285},{"type":42,"tag":229,"props":17490,"children":17491},{"style":1288},[17492],{"type":48,"value":16902},{"type":42,"tag":229,"props":17494,"children":17495},{"style":1260},[17496],{"type":48,"value":1285},{"type":42,"tag":229,"props":17498,"children":17499},{"style":1272},[17500],{"type":48,"value":17344},{"type":42,"tag":229,"props":17502,"children":17503},{"style":1260},[17504],{"type":48,"value":1280},{"type":42,"tag":229,"props":17506,"children":17507},{"style":1260},[17508],{"type":48,"value":1285},{"type":42,"tag":229,"props":17510,"children":17511},{"style":1288},[17512],{"type":48,"value":17513},"Done",{"type":42,"tag":229,"props":17515,"children":17516},{"style":1260},[17517],{"type":48,"value":1285},{"type":42,"tag":229,"props":17519,"children":17520},{"style":1272},[17521],{"type":48,"value":17366},{"type":42,"tag":229,"props":17523,"children":17524},{"style":1260},[17525],{"type":48,"value":1280},{"type":42,"tag":229,"props":17527,"children":17528},{"style":1260},[17529],{"type":48,"value":1285},{"type":42,"tag":229,"props":17531,"children":17532},{"style":1288},[17533],{"type":48,"value":17534},"{doneCards}",{"type":42,"tag":229,"props":17536,"children":17537},{"style":1260},[17538],{"type":48,"value":1285},{"type":42,"tag":229,"props":17540,"children":17541},{"style":1260},[17542],{"type":48,"value":10935},{"type":42,"tag":229,"props":17544,"children":17545},{"class":231,"line":341},[17546,17550,17554],{"type":42,"tag":229,"props":17547,"children":17548},{"style":1260},[17549],{"type":48,"value":1371},{"type":42,"tag":229,"props":17551,"children":17552},{"style":1266},[17553],{"type":48,"value":1963},{"type":42,"tag":229,"props":17555,"children":17556},{"style":1260},[17557],{"type":48,"value":1322},{"type":42,"tag":51,"props":17559,"children":17560},{},[17561],{"type":48,"value":17562},"Rules and semantics:",{"type":42,"tag":57,"props":17564,"children":17565},{},[17566,17616,17652,17664,17720,17725,17752],{"type":42,"tag":61,"props":17567,"children":17568},{},[17569,17571,17576,17578,17584,17586,17592,17594,17600,17602,17607,17609,17614],{"type":48,"value":17570},"A template takes ",{"type":42,"tag":65,"props":17572,"children":17574},{"className":17573},[],[17575],{"type":48,"value":3315},{"type":48,"value":17577}," (kebab-case), optional ",{"type":42,"tag":65,"props":17579,"children":17581},{"className":17580},[],[17582],{"type":48,"value":17583},"args",{"type":48,"value":17585}," (space-separated names, each optionally ",{"type":42,"tag":65,"props":17587,"children":17589},{"className":17588},[],[17590],{"type":48,"value":17591},"name=default",{"type":48,"value":17593},"), and exactly one element child. ",{"type":42,"tag":65,"props":17595,"children":17597},{"className":17596},[],[17598],{"type":48,"value":17599},"\u003Cuse template=\"name\">",{"type":48,"value":17601}," is allowed anywhere an element is (including as a ",{"type":42,"tag":65,"props":17603,"children":17605},{"className":17604},[],[17606],{"type":48,"value":1236},{"type":48,"value":17608}," child or the view root); its other attributes must match the template's ",{"type":42,"tag":65,"props":17610,"children":17612},{"className":17611},[],[17613],{"type":48,"value":17583},{"type":48,"value":17615}," exactly — missing args without a default and extra args are errors.",{"type":42,"tag":61,"props":17617,"children":17618},{},[17619,17621,17627,17629,17634,17636,17642,17644,17650],{"type":48,"value":17620},"Arg defaults are LITERALS only (",{"type":42,"tag":65,"props":17622,"children":17624},{"className":17623},[],[17625],{"type":48,"value":17626},"args=\"title trend=flat count=0\"",{"type":48,"value":17628},"): a default evaluates in no scope, so ",{"type":42,"tag":65,"props":17630,"children":17632},{"className":17631},[],[17633],{"type":48,"value":3355},{"type":48,"value":17635}," defaults are errors. A use site may omit any defaulted arg. ",{"type":42,"tag":65,"props":17637,"children":17639},{"className":17638},[],[17640],{"type":48,"value":17641},"args=\"name=\"",{"type":48,"value":17643}," declares an EMPTY-STRING default; defaults are unquoted — quotes in a default would be literal characters, so a quoted default (",{"type":42,"tag":65,"props":17645,"children":17647},{"className":17646},[],[17648],{"type":48,"value":17649},"name='x'",{"type":48,"value":17651},") is a teaching error.",{"type":42,"tag":61,"props":17653,"children":17654},{},[17655,17657,17662],{"type":48,"value":17656},"The template body is built IN PLACE of the ",{"type":42,"tag":65,"props":17658,"children":17660},{"className":17659},[],[17661],{"type":48,"value":16420},{"type":48,"value":17663},": structural widget ids hash through the parent chain at the expansion site, exactly as if you had written the body inline. Two uses at different sites get different ids; the same site is stable across rebuilds. Rewriting copy-pasted markup as a template does not change any widget id.",{"type":42,"tag":61,"props":17665,"children":17666},{},[17667,17669,17674,17676,17681,17683,17688,17690,17696,17698,17703,17704,17710,17712,17718],{"type":48,"value":17668},"Args bind like ",{"type":42,"tag":65,"props":17670,"children":17672},{"className":17671},[],[17673],{"type":48,"value":1236},{"type":48,"value":17675}," variables: an arg whose value is a ",{"type":42,"tag":65,"props":17677,"children":17679},{"className":17678},[],[17680],{"type":48,"value":3355},{"type":48,"value":17682}," naming an iterable (model slice\u002Farray field, pub decl, or model fn — the same set ",{"type":42,"tag":65,"props":17684,"children":17686},{"className":17685},[],[17687],{"type":48,"value":9484},{"type":48,"value":17689}," accepts) is iterable inside the template (",{"type":42,"tag":65,"props":17691,"children":17693},{"className":17692},[],[17694],{"type":48,"value":17695},"\u003Cfor each=\"cards\" ...>",{"type":48,"value":17697},"); any other arg (literal or scalar binding) is a value usable in bindings, interpolation, and equality (",{"type":42,"tag":65,"props":17699,"children":17701},{"className":17700},[],[17702],{"type":48,"value":17000},{"type":48,"value":1820},{"type":42,"tag":65,"props":17705,"children":17707},{"className":17706},[],[17708],{"type":48,"value":17709},"label=\"{title}\"",{"type":48,"value":17711},"). Args are evaluated at the use site; inside the body only the args, the model, and the body's own loop variables are in scope. Value args are scalars — ",{"type":42,"tag":65,"props":17713,"children":17715},{"className":17714},[],[17716],{"type":48,"value":17717},"{arg.field}",{"type":48,"value":17719}," is an error.",{"type":42,"tag":61,"props":17721,"children":17722},{},[17723],{"type":48,"value":17724},"Uses inside a template body may only reference templates defined EARLIER in the file (this also makes recursion impossible). Bindings stay zero-argument: the template deduplicates the view, the per-case query stays a named model function.",{"type":42,"tag":61,"props":17726,"children":17727},{},[17728,17730,17736,17738,17743,17745,17750],{"type":48,"value":17729},"SLOTS: a template body may contain one ",{"type":42,"tag":65,"props":17731,"children":17733},{"className":17732},[],[17734],{"type":48,"value":17735},"\u003Cslot\u002F>",{"type":48,"value":17737}," (attribute-less, childless; named slots do not exist). The ",{"type":42,"tag":65,"props":17739,"children":17741},{"className":17740},[],[17742],{"type":48,"value":16420},{"type":48,"value":17744}," site's children build IN THE CONSUMER'S SCOPE — they see the model paths and loop variables where the use is written — and land at the slot's position; ids hash as if inlined. A use with no children renders the slot empty; children on a slotless template are an error; a ",{"type":42,"tag":65,"props":17746,"children":17748},{"className":17747},[],[17749],{"type":48,"value":17735},{"type":48,"value":17751}," inside use-site children (forwarding) is an error.",{"type":42,"tag":61,"props":17753,"children":17754},{},[17755,17757,17763],{"type":48,"value":17756},"IMPORTS: ",{"type":42,"tag":65,"props":17758,"children":17760},{"className":17759},[],[17761],{"type":48,"value":17762},"\u003Cimport src=\"components\u002Fcards.native\"\u002F>",{"type":48,"value":17764}," lines go at the very top of a file, before its templates. Paths are relative to the importing file (subdirectories and transitive imports fine, always under the root view file's directory — absolute paths and escapes are errors). An imported file defines templates ONLY (a component file; a view root inside one is an error, and a component file checks standalone). Importing splices the file's templates (transitively) BEFORE yours, in import order — as if pasted at the import site — so define-before-use stays the only ordering rule. Cycles are reported with the cycle path; duplicate template names are an error naming both definition sites.",{"type":42,"tag":51,"props":17766,"children":17767},{},[17768,17770,17776,17778,17784,17786,17792,17794,17800,17802,17808,17810,17816,17817,17823],{"type":48,"value":17769},"Both engines implement templates, defaults, slots, and imports: the interpreter expands at build time (hot reload re-resolves imports from disk, so edits to imported files reload), and the compiled engine inlines at comptime with the identical result. A document with imports compiles through ",{"type":42,"tag":65,"props":17771,"children":17773},{"className":17772},[],[17774],{"type":48,"value":17775},"canvas.CompiledMarkupImports(Model, Msg, \"root.native\", &sources)",{"type":48,"value":17777}," where ",{"type":42,"tag":65,"props":17779,"children":17781},{"className":17780},[],[17782],{"type":48,"value":17783},"sources",{"type":48,"value":17785}," is a ",{"type":42,"tag":65,"props":17787,"children":17789},{"className":17788},[],[17790],{"type":48,"value":17791},"canvas.ui_markup.SourceFile",{"type":48,"value":17793}," set (",{"type":42,"tag":65,"props":17795,"children":17797},{"className":17796},[],[17798],{"type":48,"value":17799},".{ .path = \"components\u002Fcards.native\", .source = @embedFile(\"components\u002Fcards.native\") }",{"type":48,"value":17801},", paths relative to the root file's directory); pass the same set on ",{"type":42,"tag":65,"props":17803,"children":17805},{"className":17804},[],[17806],{"type":48,"value":17807},"MarkupOptions.sources",{"type":48,"value":17809}," for the runtime engine. See ",{"type":42,"tag":65,"props":17811,"children":17813},{"className":17812},[],[17814],{"type":48,"value":17815},"examples\u002Fkanban\u002Fsrc\u002Fboard.native",{"type":48,"value":3952},{"type":42,"tag":65,"props":17818,"children":17820},{"className":17819},[],[17821],{"type":48,"value":17822},"examples\u002Fkanban\u002Fsrc\u002Fcomponents\u002Fboard-column.native",{"type":48,"value":123},{"type":42,"tag":212,"props":17825,"children":17827},{"id":17826},"markdown-in-markup-markdown",[17828,17830],{"type":48,"value":17829},"Markdown in markup: ",{"type":42,"tag":65,"props":17831,"children":17833},{"className":17832},[],[17834],{"type":48,"value":17835},"\u003Cmarkdown>",{"type":42,"tag":51,"props":17837,"children":17838},{},[17839,17841,17847],{"type":48,"value":17840},"A leaf element that renders a markdown string (the GFM subset below) as ordinary widgets, wiring ",{"type":42,"tag":65,"props":17842,"children":17844},{"className":17843},[],[17845],{"type":48,"value":17846},"native_sdk.markdown",{"type":48,"value":17848}," for you — both engines implement it identically:",{"type":42,"tag":219,"props":17850,"children":17852},{"className":1248,"code":17851,"language":1250,"meta":223,"style":223},"\u003Cmarkdown source=\"{issue_body}\" on-link=\"open_url\" on-details=\"toggle_details\" details-expanded=\"{details_expanded}\" \u002F>\n",[17853],{"type":42,"tag":65,"props":17854,"children":17855},{"__ignoreMap":223},[17856],{"type":42,"tag":229,"props":17857,"children":17858},{"class":231,"line":232},[17859,17863,17867,17872,17876,17880,17885,17889,17894,17898,17902,17907,17911,17916,17920,17924,17929,17933,17938,17942,17946,17951,17955],{"type":42,"tag":229,"props":17860,"children":17861},{"style":1260},[17862],{"type":48,"value":1263},{"type":42,"tag":229,"props":17864,"children":17865},{"style":1266},[17866],{"type":48,"value":3529},{"type":42,"tag":229,"props":17868,"children":17869},{"style":1272},[17870],{"type":48,"value":17871}," source",{"type":42,"tag":229,"props":17873,"children":17874},{"style":1260},[17875],{"type":48,"value":1280},{"type":42,"tag":229,"props":17877,"children":17878},{"style":1260},[17879],{"type":48,"value":1285},{"type":42,"tag":229,"props":17881,"children":17882},{"style":1288},[17883],{"type":48,"value":17884},"{issue_body}",{"type":42,"tag":229,"props":17886,"children":17887},{"style":1260},[17888],{"type":48,"value":1285},{"type":42,"tag":229,"props":17890,"children":17891},{"style":1272},[17892],{"type":48,"value":17893}," on-link",{"type":42,"tag":229,"props":17895,"children":17896},{"style":1260},[17897],{"type":48,"value":1280},{"type":42,"tag":229,"props":17899,"children":17900},{"style":1260},[17901],{"type":48,"value":1285},{"type":42,"tag":229,"props":17903,"children":17904},{"style":1288},[17905],{"type":48,"value":17906},"open_url",{"type":42,"tag":229,"props":17908,"children":17909},{"style":1260},[17910],{"type":48,"value":1285},{"type":42,"tag":229,"props":17912,"children":17913},{"style":1272},[17914],{"type":48,"value":17915}," on-details",{"type":42,"tag":229,"props":17917,"children":17918},{"style":1260},[17919],{"type":48,"value":1280},{"type":42,"tag":229,"props":17921,"children":17922},{"style":1260},[17923],{"type":48,"value":1285},{"type":42,"tag":229,"props":17925,"children":17926},{"style":1288},[17927],{"type":48,"value":17928},"toggle_details",{"type":42,"tag":229,"props":17930,"children":17931},{"style":1260},[17932],{"type":48,"value":1285},{"type":42,"tag":229,"props":17934,"children":17935},{"style":1272},[17936],{"type":48,"value":17937}," details-expanded",{"type":42,"tag":229,"props":17939,"children":17940},{"style":1260},[17941],{"type":48,"value":1280},{"type":42,"tag":229,"props":17943,"children":17944},{"style":1260},[17945],{"type":48,"value":1285},{"type":42,"tag":229,"props":17947,"children":17948},{"style":1288},[17949],{"type":48,"value":17950},"{details_expanded}",{"type":42,"tag":229,"props":17952,"children":17953},{"style":1260},[17954],{"type":48,"value":1285},{"type":42,"tag":229,"props":17956,"children":17957},{"style":1260},[17958],{"type":48,"value":10935},{"type":42,"tag":57,"props":17960,"children":17961},{},[17962,17986,18019,18045,18092,18156],{"type":42,"tag":61,"props":17963,"children":17964},{},[17965,17970,17972,17977,17979,17984],{"type":42,"tag":65,"props":17966,"children":17968},{"className":17967},[],[17969],{"type":48,"value":3545},{"type":48,"value":17971}," (required): one ",{"type":42,"tag":65,"props":17973,"children":17975},{"className":17974},[],[17976],{"type":48,"value":3355},{"type":48,"value":17978}," producing the markdown text — a ",{"type":42,"tag":65,"props":17980,"children":17982},{"className":17981},[],[17983],{"type":48,"value":10339},{"type":48,"value":17985}," field, zero-arg fn, or arena-taking fn (compose the document into the build arena at view time).",{"type":42,"tag":61,"props":17987,"children":17988},{},[17989,17995,17997,18003,18005,18011,18013,18018],{"type":42,"tag":65,"props":17990,"children":17992},{"className":17991},[],[17993],{"type":48,"value":17994},"on-link",{"type":48,"value":17996}," (optional): a BARE Msg tag — no ",{"type":42,"tag":65,"props":17998,"children":18000},{"className":17999},[],[18001],{"type":48,"value":18002},":{payload}",{"type":48,"value":18004}," — whose payload is the pressed link URL; declare ",{"type":42,"tag":65,"props":18006,"children":18008},{"className":18007},[],[18009],{"type":48,"value":18010},"open_url: []const u8",{"type":48,"value":18012}," in ",{"type":42,"tag":65,"props":18014,"children":18016},{"className":18015},[],[18017],{"type":48,"value":97},{"type":48,"value":123},{"type":42,"tag":61,"props":18020,"children":18021},{},[18022,18028,18030,18036,18038,18044],{"type":42,"tag":65,"props":18023,"children":18025},{"className":18024},[],[18026],{"type":48,"value":18027},"on-details",{"type":48,"value":18029}," (optional): a bare Msg tag whose payload is the ",{"type":42,"tag":65,"props":18031,"children":18033},{"className":18032},[],[18034],{"type":48,"value":18035},"\u003Cdetails>",{"type":48,"value":18037}," block's document-order index; declare ",{"type":42,"tag":65,"props":18039,"children":18041},{"className":18040},[],[18042],{"type":48,"value":18043},"toggle_details: usize",{"type":48,"value":123},{"type":42,"tag":61,"props":18046,"children":18047},{},[18048,18054,18056,18061,18063,18069,18071,18076,18078,18084,18086,18091],{"type":42,"tag":65,"props":18049,"children":18051},{"className":18050},[],[18052],{"type":48,"value":18053},"details-expanded",{"type":48,"value":18055}," (optional): one ",{"type":42,"tag":65,"props":18057,"children":18059},{"className":18058},[],[18060],{"type":48,"value":3355},{"type":48,"value":18062}," naming a ",{"type":42,"tag":65,"props":18064,"children":18066},{"className":18065},[],[18067],{"type":48,"value":18068},"[]const bool",{"type":48,"value":18070}," iterable (a model field, pub decl, or fn — the same sources ",{"type":42,"tag":65,"props":18072,"children":18074},{"className":18073},[],[18075],{"type":48,"value":9484},{"type":48,"value":18077}," accepts); flags are read in details-block document order. Keep a bounded ",{"type":42,"tag":65,"props":18079,"children":18081},{"className":18080},[],[18082],{"type":48,"value":18083},"details_expanded: [8]bool",{"type":48,"value":18085}," in the model and toggle it in ",{"type":42,"tag":65,"props":18087,"children":18089},{"className":18088},[],[18090],{"type":48,"value":483},{"type":48,"value":123},{"type":42,"tag":61,"props":18093,"children":18094},{},[18095,18101,18103,18108,18110,18116,18118,18124,18126,18131,18133,18139,18141,18146,18148,18154],{"type":42,"tag":65,"props":18096,"children":18098},{"className":18097},[],[18099],{"type":48,"value":18100},"issue-link-base",{"type":48,"value":18102}," (optional): a literal URL prefix or one ",{"type":42,"tag":65,"props":18104,"children":18106},{"className":18105},[],[18107],{"type":48,"value":3355},{"type":48,"value":18109}," producing it; ",{"type":42,"tag":65,"props":18111,"children":18113},{"className":18112},[],[18114],{"type":48,"value":18115},"#123",{"type":48,"value":18117}," references at word boundaries become links to base ++ number (",{"type":42,"tag":65,"props":18119,"children":18121},{"className":18120},[],[18122],{"type":48,"value":18123},"issue-link-base=\"ghissue:\u002F\u002F\"",{"type":48,"value":18125}," links ",{"type":42,"tag":65,"props":18127,"children":18129},{"className":18128},[],[18130],{"type":48,"value":18115},{"type":48,"value":18132}," to ",{"type":42,"tag":65,"props":18134,"children":18136},{"className":18135},[],[18137],{"type":48,"value":18138},"ghissue:\u002F\u002F123",{"type":48,"value":18140}," — an app scheme your ",{"type":42,"tag":65,"props":18142,"children":18144},{"className":18143},[],[18145],{"type":48,"value":17994},{"type":48,"value":18147}," handler intercepts, or a web base like ",{"type":42,"tag":65,"props":18149,"children":18151},{"className":18150},[],[18152],{"type":48,"value":18153},"https:\u002F\u002Fgithub.com\u002Fowner\u002Frepo\u002Fissues\u002F",{"type":48,"value":18155},"). Off by default: resolving a ref needs repo context.",{"type":42,"tag":61,"props":18157,"children":18158},{},[18159,18161,18166,18168,18173],{"type":48,"value":18160},"No children, no text content, no other attributes (teaching errors point at misuse). Without the details wiring, ",{"type":42,"tag":65,"props":18162,"children":18164},{"className":18163},[],[18165],{"type":48,"value":18035},{"type":48,"value":18167}," blocks render collapsed and inert; without ",{"type":42,"tag":65,"props":18169,"children":18171},{"className":18170},[],[18172],{"type":48,"value":17994},{"type":48,"value":18174},", links render styled but inert.",{"type":42,"tag":212,"props":18176,"children":18178},{"id":18177},"pipeline-composites-stepper-timeline-nav",[18179],{"type":48,"value":18180},"Pipeline composites: stepper, timeline, nav",{"type":42,"tag":51,"props":18182,"children":18183},{},[18184,18186,18191],{"type":48,"value":18185},"Three composites for pipeline\u002Frun UIs — pure compositions of existing widgets (no new kinds), identical from markup and ",{"type":42,"tag":65,"props":18187,"children":18189},{"className":18188},[],[18190],{"type":48,"value":4117},{"type":48,"value":5625},{"type":42,"tag":219,"props":18193,"children":18195},{"className":1248,"code":18194,"language":1250,"meta":223,"style":223},"\u003Cstepper active=\"{stage_index}\">\n  \u003Cstep>Work\u003C\u002Fstep>\u003Cstep>Triage\u003C\u002Fstep>\u003Cstep>Review · {round}\u003C\u002Fstep>\u003Cstep>Fix\u003C\u002Fstep>\u003Cstep>Ready\u003C\u002Fstep>\n\u003C\u002Fstepper>\n\u003Ctimeline gap=\"4\">\n  \u003Cfor each=\"ledger\" key=\"slot\" as=\"entry\">\n    \u003Ctimeline-item title=\"{entry.title}\" description=\"{entry.summary}\" meta=\"{entry.meta}\" variant=\"{entry.tone}\" on-press=\"open_step:{entry.slot}\" \u002F>\n  \u003C\u002Ffor>\n\u003C\u002Ftimeline>\n",[18196],{"type":42,"tag":65,"props":18197,"children":18198},{"__ignoreMap":223},[18199,18236,18368,18383,18419,18497,18620,18635],{"type":42,"tag":229,"props":18200,"children":18201},{"class":231,"line":232},[18202,18206,18210,18215,18219,18223,18228,18232],{"type":42,"tag":229,"props":18203,"children":18204},{"style":1260},[18205],{"type":48,"value":1263},{"type":42,"tag":229,"props":18207,"children":18208},{"style":1266},[18209],{"type":48,"value":3566},{"type":42,"tag":229,"props":18211,"children":18212},{"style":1272},[18213],{"type":48,"value":18214}," active",{"type":42,"tag":229,"props":18216,"children":18217},{"style":1260},[18218],{"type":48,"value":1280},{"type":42,"tag":229,"props":18220,"children":18221},{"style":1260},[18222],{"type":48,"value":1285},{"type":42,"tag":229,"props":18224,"children":18225},{"style":1288},[18226],{"type":48,"value":18227},"{stage_index}",{"type":42,"tag":229,"props":18229,"children":18230},{"style":1260},[18231],{"type":48,"value":1285},{"type":42,"tag":229,"props":18233,"children":18234},{"style":1260},[18235],{"type":48,"value":1322},{"type":42,"tag":229,"props":18237,"children":18238},{"class":231,"line":241},[18239,18243,18247,18251,18256,18260,18264,18268,18272,18276,18281,18285,18289,18293,18297,18301,18306,18310,18314,18318,18322,18326,18331,18335,18339,18343,18347,18351,18356,18360,18364],{"type":42,"tag":229,"props":18240,"children":18241},{"style":1260},[18242],{"type":48,"value":1330},{"type":42,"tag":229,"props":18244,"children":18245},{"style":1266},[18246],{"type":48,"value":3573},{"type":42,"tag":229,"props":18248,"children":18249},{"style":1260},[18250],{"type":48,"value":1361},{"type":42,"tag":229,"props":18252,"children":18253},{"style":1364},[18254],{"type":48,"value":18255},"Work",{"type":42,"tag":229,"props":18257,"children":18258},{"style":1260},[18259],{"type":48,"value":1371},{"type":42,"tag":229,"props":18261,"children":18262},{"style":1266},[18263],{"type":48,"value":3573},{"type":42,"tag":229,"props":18265,"children":18266},{"style":1260},[18267],{"type":48,"value":7188},{"type":42,"tag":229,"props":18269,"children":18270},{"style":1266},[18271],{"type":48,"value":3573},{"type":42,"tag":229,"props":18273,"children":18274},{"style":1260},[18275],{"type":48,"value":1361},{"type":42,"tag":229,"props":18277,"children":18278},{"style":1364},[18279],{"type":48,"value":18280},"Triage",{"type":42,"tag":229,"props":18282,"children":18283},{"style":1260},[18284],{"type":48,"value":1371},{"type":42,"tag":229,"props":18286,"children":18287},{"style":1266},[18288],{"type":48,"value":3573},{"type":42,"tag":229,"props":18290,"children":18291},{"style":1260},[18292],{"type":48,"value":7188},{"type":42,"tag":229,"props":18294,"children":18295},{"style":1266},[18296],{"type":48,"value":3573},{"type":42,"tag":229,"props":18298,"children":18299},{"style":1260},[18300],{"type":48,"value":1361},{"type":42,"tag":229,"props":18302,"children":18303},{"style":1364},[18304],{"type":48,"value":18305},"Review · {round}",{"type":42,"tag":229,"props":18307,"children":18308},{"style":1260},[18309],{"type":48,"value":1371},{"type":42,"tag":229,"props":18311,"children":18312},{"style":1266},[18313],{"type":48,"value":3573},{"type":42,"tag":229,"props":18315,"children":18316},{"style":1260},[18317],{"type":48,"value":7188},{"type":42,"tag":229,"props":18319,"children":18320},{"style":1266},[18321],{"type":48,"value":3573},{"type":42,"tag":229,"props":18323,"children":18324},{"style":1260},[18325],{"type":48,"value":1361},{"type":42,"tag":229,"props":18327,"children":18328},{"style":1364},[18329],{"type":48,"value":18330},"Fix",{"type":42,"tag":229,"props":18332,"children":18333},{"style":1260},[18334],{"type":48,"value":1371},{"type":42,"tag":229,"props":18336,"children":18337},{"style":1266},[18338],{"type":48,"value":3573},{"type":42,"tag":229,"props":18340,"children":18341},{"style":1260},[18342],{"type":48,"value":7188},{"type":42,"tag":229,"props":18344,"children":18345},{"style":1266},[18346],{"type":48,"value":3573},{"type":42,"tag":229,"props":18348,"children":18349},{"style":1260},[18350],{"type":48,"value":1361},{"type":42,"tag":229,"props":18352,"children":18353},{"style":1364},[18354],{"type":48,"value":18355},"Ready",{"type":42,"tag":229,"props":18357,"children":18358},{"style":1260},[18359],{"type":48,"value":1371},{"type":42,"tag":229,"props":18361,"children":18362},{"style":1266},[18363],{"type":48,"value":3573},{"type":42,"tag":229,"props":18365,"children":18366},{"style":1260},[18367],{"type":48,"value":1322},{"type":42,"tag":229,"props":18369,"children":18370},{"class":231,"line":251},[18371,18375,18379],{"type":42,"tag":229,"props":18372,"children":18373},{"style":1260},[18374],{"type":48,"value":1371},{"type":42,"tag":229,"props":18376,"children":18377},{"style":1266},[18378],{"type":48,"value":3566},{"type":42,"tag":229,"props":18380,"children":18381},{"style":1260},[18382],{"type":48,"value":1322},{"type":42,"tag":229,"props":18384,"children":18385},{"class":231,"line":260},[18386,18390,18394,18398,18402,18406,18411,18415],{"type":42,"tag":229,"props":18387,"children":18388},{"style":1260},[18389],{"type":48,"value":1263},{"type":42,"tag":229,"props":18391,"children":18392},{"style":1266},[18393],{"type":48,"value":3620},{"type":42,"tag":229,"props":18395,"children":18396},{"style":1272},[18397],{"type":48,"value":5219},{"type":42,"tag":229,"props":18399,"children":18400},{"style":1260},[18401],{"type":48,"value":1280},{"type":42,"tag":229,"props":18403,"children":18404},{"style":1260},[18405],{"type":48,"value":1285},{"type":42,"tag":229,"props":18407,"children":18408},{"style":1288},[18409],{"type":48,"value":18410},"4",{"type":42,"tag":229,"props":18412,"children":18413},{"style":1260},[18414],{"type":48,"value":1285},{"type":42,"tag":229,"props":18416,"children":18417},{"style":1260},[18418],{"type":48,"value":1322},{"type":42,"tag":229,"props":18420,"children":18421},{"class":231,"line":269},[18422,18426,18430,18434,18438,18442,18447,18451,18455,18459,18463,18468,18472,18476,18480,18484,18489,18493],{"type":42,"tag":229,"props":18423,"children":18424},{"style":1260},[18425],{"type":48,"value":1330},{"type":42,"tag":229,"props":18427,"children":18428},{"style":1266},[18429],{"type":48,"value":1236},{"type":42,"tag":229,"props":18431,"children":18432},{"style":1272},[18433],{"type":48,"value":5277},{"type":42,"tag":229,"props":18435,"children":18436},{"style":1260},[18437],{"type":48,"value":1280},{"type":42,"tag":229,"props":18439,"children":18440},{"style":1260},[18441],{"type":48,"value":1285},{"type":42,"tag":229,"props":18443,"children":18444},{"style":1288},[18445],{"type":48,"value":18446},"ledger",{"type":42,"tag":229,"props":18448,"children":18449},{"style":1260},[18450],{"type":48,"value":1285},{"type":42,"tag":229,"props":18452,"children":18453},{"style":1272},[18454],{"type":48,"value":5880},{"type":42,"tag":229,"props":18456,"children":18457},{"style":1260},[18458],{"type":48,"value":1280},{"type":42,"tag":229,"props":18460,"children":18461},{"style":1260},[18462],{"type":48,"value":1285},{"type":42,"tag":229,"props":18464,"children":18465},{"style":1288},[18466],{"type":48,"value":18467},"slot",{"type":42,"tag":229,"props":18469,"children":18470},{"style":1260},[18471],{"type":48,"value":1285},{"type":42,"tag":229,"props":18473,"children":18474},{"style":1272},[18475],{"type":48,"value":5299},{"type":42,"tag":229,"props":18477,"children":18478},{"style":1260},[18479],{"type":48,"value":1280},{"type":42,"tag":229,"props":18481,"children":18482},{"style":1260},[18483],{"type":48,"value":1285},{"type":42,"tag":229,"props":18485,"children":18486},{"style":1288},[18487],{"type":48,"value":18488},"entry",{"type":42,"tag":229,"props":18490,"children":18491},{"style":1260},[18492],{"type":48,"value":1285},{"type":42,"tag":229,"props":18494,"children":18495},{"style":1260},[18496],{"type":48,"value":1322},{"type":42,"tag":229,"props":18498,"children":18499},{"class":231,"line":278},[18500,18504,18508,18512,18516,18520,18525,18529,18534,18538,18542,18547,18551,18556,18560,18564,18569,18573,18578,18582,18586,18591,18595,18599,18603,18607,18612,18616],{"type":42,"tag":229,"props":18501,"children":18502},{"style":1260},[18503],{"type":48,"value":1403},{"type":42,"tag":229,"props":18505,"children":18506},{"style":1266},[18507],{"type":48,"value":3627},{"type":42,"tag":229,"props":18509,"children":18510},{"style":1272},[18511],{"type":48,"value":17344},{"type":42,"tag":229,"props":18513,"children":18514},{"style":1260},[18515],{"type":48,"value":1280},{"type":42,"tag":229,"props":18517,"children":18518},{"style":1260},[18519],{"type":48,"value":1285},{"type":42,"tag":229,"props":18521,"children":18522},{"style":1288},[18523],{"type":48,"value":18524},"{entry.title}",{"type":42,"tag":229,"props":18526,"children":18527},{"style":1260},[18528],{"type":48,"value":1285},{"type":42,"tag":229,"props":18530,"children":18531},{"style":1272},[18532],{"type":48,"value":18533}," description",{"type":42,"tag":229,"props":18535,"children":18536},{"style":1260},[18537],{"type":48,"value":1280},{"type":42,"tag":229,"props":18539,"children":18540},{"style":1260},[18541],{"type":48,"value":1285},{"type":42,"tag":229,"props":18543,"children":18544},{"style":1288},[18545],{"type":48,"value":18546},"{entry.summary}",{"type":42,"tag":229,"props":18548,"children":18549},{"style":1260},[18550],{"type":48,"value":1285},{"type":42,"tag":229,"props":18552,"children":18553},{"style":1272},[18554],{"type":48,"value":18555}," meta",{"type":42,"tag":229,"props":18557,"children":18558},{"style":1260},[18559],{"type":48,"value":1280},{"type":42,"tag":229,"props":18561,"children":18562},{"style":1260},[18563],{"type":48,"value":1285},{"type":42,"tag":229,"props":18565,"children":18566},{"style":1288},[18567],{"type":48,"value":18568},"{entry.meta}",{"type":42,"tag":229,"props":18570,"children":18571},{"style":1260},[18572],{"type":48,"value":1285},{"type":42,"tag":229,"props":18574,"children":18575},{"style":1272},[18576],{"type":48,"value":18577}," variant",{"type":42,"tag":229,"props":18579,"children":18580},{"style":1260},[18581],{"type":48,"value":1280},{"type":42,"tag":229,"props":18583,"children":18584},{"style":1260},[18585],{"type":48,"value":1285},{"type":42,"tag":229,"props":18587,"children":18588},{"style":1288},[18589],{"type":48,"value":18590},"{entry.tone}",{"type":42,"tag":229,"props":18592,"children":18593},{"style":1260},[18594],{"type":48,"value":1285},{"type":42,"tag":229,"props":18596,"children":18597},{"style":1272},[18598],{"type":48,"value":1275},{"type":42,"tag":229,"props":18600,"children":18601},{"style":1260},[18602],{"type":48,"value":1280},{"type":42,"tag":229,"props":18604,"children":18605},{"style":1260},[18606],{"type":48,"value":1285},{"type":42,"tag":229,"props":18608,"children":18609},{"style":1288},[18610],{"type":48,"value":18611},"open_step:{entry.slot}",{"type":42,"tag":229,"props":18613,"children":18614},{"style":1260},[18615],{"type":48,"value":1285},{"type":42,"tag":229,"props":18617,"children":18618},{"style":1260},[18619],{"type":48,"value":10935},{"type":42,"tag":229,"props":18621,"children":18622},{"class":231,"line":287},[18623,18627,18631],{"type":42,"tag":229,"props":18624,"children":18625},{"style":1260},[18626],{"type":48,"value":1700},{"type":42,"tag":229,"props":18628,"children":18629},{"style":1266},[18630],{"type":48,"value":1236},{"type":42,"tag":229,"props":18632,"children":18633},{"style":1260},[18634],{"type":48,"value":1322},{"type":42,"tag":229,"props":18636,"children":18637},{"class":231,"line":296},[18638,18642,18646],{"type":42,"tag":229,"props":18639,"children":18640},{"style":1260},[18641],{"type":48,"value":1371},{"type":42,"tag":229,"props":18643,"children":18644},{"style":1266},[18645],{"type":48,"value":3620},{"type":42,"tag":229,"props":18647,"children":18648},{"style":1260},[18649],{"type":48,"value":1322},{"type":42,"tag":57,"props":18651,"children":18652},{},[18653,18687,18735,18761],{"type":42,"tag":61,"props":18654,"children":18655},{},[18656,18658,18663,18664,18670,18672,18677,18679,18685],{"type":48,"value":18657},"Stepper semantics: a ",{"type":42,"tag":65,"props":18659,"children":18661},{"className":18660},[],[18662],{"type":48,"value":2077},{"type":48,"value":5175},{"type":42,"tag":65,"props":18665,"children":18667},{"className":18666},[],[18668],{"type":48,"value":18669},"listitem",{"type":48,"value":18671},"s; the active step is ",{"type":42,"tag":65,"props":18673,"children":18675},{"className":18674},[],[18676],{"type":48,"value":2269},{"type":48,"value":18678}," and every label carries its state (",{"type":42,"tag":65,"props":18680,"children":18682},{"className":18681},[],[18683],{"type":48,"value":18684},"\"Review (active)\"",{"type":48,"value":18686},") plus list position — assert pipeline stage from automation snapshots by label.",{"type":42,"tag":61,"props":18688,"children":18689},{},[18690,18692,18697,18699,18704,18706,18712,18714,18719,18721,18726,18728,18733],{"type":48,"value":18691},"Timeline item: leading badge (dot colored by ",{"type":42,"tag":65,"props":18693,"children":18695},{"className":18694},[],[18696],{"type":48,"value":3672},{"type":48,"value":18698},", or ",{"type":42,"tag":65,"props":18700,"children":18702},{"className":18701},[],[18703],{"type":48,"value":3665},{"type":48,"value":18705}," text like ",{"type":42,"tag":65,"props":18707,"children":18709},{"className":18708},[],[18710],{"type":48,"value":18711},"\"✓\"",{"type":48,"value":18713},"), connector rail (",{"type":42,"tag":65,"props":18715,"children":18717},{"className":18716},[],[18718],{"type":48,"value":3679},{"type":48,"value":18720}," ends it), bold title, wrapped muted description, muted meta line. With ",{"type":42,"tag":65,"props":18722,"children":18724},{"className":18723},[],[18725],{"type":48,"value":1176},{"type":48,"value":18727}," the item gains a trailing chevron and the press binds to the item's root (role ",{"type":42,"tag":65,"props":18729,"children":18731},{"className":18730},[],[18732],{"type":48,"value":18669},{"type":48,"value":18734},", focusable, labeled by the title) — clicks on the title\u002Fdescription\u002Fmeta fall through to it, so a click anywhere dispatches and dragging still selects the text. No hover fill or description line-clamp in v1.",{"type":42,"tag":61,"props":18736,"children":18737},{},[18738,18740,18746,18747,18753,18754,18760],{"type":48,"value":18739},"Zig: ",{"type":42,"tag":65,"props":18741,"children":18743},{"className":18742},[],[18744],{"type":48,"value":18745},"ui.stepper(.{ .active = ... }, &.{ .{ .label = \"Work\" }, ... })",{"type":48,"value":1820},{"type":42,"tag":65,"props":18748,"children":18750},{"className":18749},[],[18751],{"type":48,"value":18752},"ui.timeline(options, items)",{"type":48,"value":1820},{"type":42,"tag":65,"props":18755,"children":18757},{"className":18756},[],[18758],{"type":48,"value":18759},"ui.timelineItem(.{ .title = ..., .on_press = ... })",{"type":48,"value":123},{"type":42,"tag":61,"props":18762,"children":18763},{},[18764,18766,18771,18772,18778,18780,18786,18788,18793],{"type":48,"value":18765},"Nav (Zig-only; markup swaps with ",{"type":42,"tag":65,"props":18767,"children":18769},{"className":18768},[],[18770],{"type":48,"value":2422},{"type":48,"value":4119},{"type":42,"tag":65,"props":18773,"children":18775},{"className":18774},[],[18776],{"type":48,"value":18777},"ui.nav(.{ .active = model.nav_depth, .retain = true }, .{ pageA, pageB })",{"type":48,"value":18779}," — the model owns the stack; pages are index-keyed so widget ids (and engine scroll\u002Ftext state) are stable across swaps; ",{"type":42,"tag":65,"props":18781,"children":18783},{"className":18782},[],[18784],{"type":48,"value":18785},"retain=true",{"type":48,"value":18787}," keeps inactive pages mounted-but-hidden (state preserved, excluded from render\u002Fhit-test\u002Ffocus\u002Fsemantics), default unmounts. Instant swap, no animation in v1; move focus in ",{"type":42,"tag":65,"props":18789,"children":18791},{"className":18790},[],[18792],{"type":48,"value":483},{"type":48,"value":18794}," when pushing\u002Fpopping if the focused widget lives on the outgoing page.",{"type":42,"tag":212,"props":18796,"children":18798},{"id":18797},"charts",[18799],{"type":48,"value":18800},"Charts",{"type":42,"tag":51,"props":18802,"children":18803},{},[18804,18809,18811,18817,18819,18824,18826,18831],{"type":42,"tag":65,"props":18805,"children":18807},{"className":18806},[],[18808],{"type":48,"value":4200},{"type":48,"value":18810}," is the data-visualization composite: ",{"type":42,"tag":65,"props":18812,"children":18814},{"className":18813},[],[18815],{"type":48,"value":18816},"\u003Cseries>",{"type":48,"value":18818}," children bind model ",{"type":42,"tag":65,"props":18820,"children":18822},{"className":18821},[],[18823],{"type":48,"value":3737},{"type":48,"value":18825}," iterables and draw through the vector path pipeline with token colors — charts retheme with the palette, repaint exactly when their data changes (value equality, not identity), and report series semantics to automation. Both engines lower it through ",{"type":42,"tag":65,"props":18827,"children":18829},{"className":18828},[],[18830],{"type":48,"value":4223},{"type":48,"value":18832},", so markup charts and Zig charts are pixel- and semantics-identical.",{"type":42,"tag":219,"props":18834,"children":18836},{"className":1248,"code":18835,"language":1250,"meta":223,"style":223},"\u003C!-- Star-history: cumulative stars per repo. 10k-point series are fine —\n     charts downsample deterministically past 256 points per series. -->\n\u003Cchart grow=\"1\" height=\"220\" y-min=\"0\" grid-lines=\"3\" label=\"Star history\">\n  \u003Cseries kind=\"area\" values=\"{sdkStars}\" color=\"accent\" label=\"native-sdk\" \u002F>\n  \u003Cseries kind=\"line\" values=\"{examplesStars}\" color=\"info\" label=\"examples\" \u002F>\n\u003C\u002Fchart>\n\u003C!-- Sparkline tile: zero-baseline bars pinned to an absolute 0..1 domain. -->\n\u003Cchart width=\"239\" height=\"32\" y-min=\"0\" y-max=\"1\" label=\"CPU history\">\n  \u003Cseries kind=\"bar\" values=\"{cpuSpark}\" \u002F>\n\u003C\u002Fchart>\n",[18837],{"type":42,"tag":65,"props":18838,"children":18839},{"__ignoreMap":223},[18840,18848,18856,18976,19076,19173,19188,19196,19316,19372],{"type":42,"tag":229,"props":18841,"children":18842},{"class":231,"line":232},[18843],{"type":42,"tag":229,"props":18844,"children":18845},{"style":15826},[18846],{"type":48,"value":18847},"\u003C!-- Star-history: cumulative stars per repo. 10k-point series are fine —\n",{"type":42,"tag":229,"props":18849,"children":18850},{"class":231,"line":241},[18851],{"type":42,"tag":229,"props":18852,"children":18853},{"style":15826},[18854],{"type":48,"value":18855},"     charts downsample deterministically past 256 points per series. -->\n",{"type":42,"tag":229,"props":18857,"children":18858},{"class":231,"line":251},[18859,18863,18867,18871,18875,18879,18883,18887,18892,18896,18900,18904,18908,18913,18917,18921,18926,18930,18935,18939,18943,18947,18951,18955,18959,18963,18968,18972],{"type":42,"tag":229,"props":18860,"children":18861},{"style":1260},[18862],{"type":48,"value":1263},{"type":42,"tag":229,"props":18864,"children":18865},{"style":1266},[18866],{"type":48,"value":3706},{"type":42,"tag":229,"props":18868,"children":18869},{"style":1272},[18870],{"type":48,"value":1339},{"type":42,"tag":229,"props":18872,"children":18873},{"style":1260},[18874],{"type":48,"value":1280},{"type":42,"tag":229,"props":18876,"children":18877},{"style":1260},[18878],{"type":48,"value":1285},{"type":42,"tag":229,"props":18880,"children":18881},{"style":1288},[18882],{"type":48,"value":1352},{"type":42,"tag":229,"props":18884,"children":18885},{"style":1260},[18886],{"type":48,"value":1285},{"type":42,"tag":229,"props":18888,"children":18889},{"style":1272},[18890],{"type":48,"value":18891}," height",{"type":42,"tag":229,"props":18893,"children":18894},{"style":1260},[18895],{"type":48,"value":1280},{"type":42,"tag":229,"props":18897,"children":18898},{"style":1260},[18899],{"type":48,"value":1285},{"type":42,"tag":229,"props":18901,"children":18902},{"style":1288},[18903],{"type":48,"value":6547},{"type":42,"tag":229,"props":18905,"children":18906},{"style":1260},[18907],{"type":48,"value":1285},{"type":42,"tag":229,"props":18909,"children":18910},{"style":1272},[18911],{"type":48,"value":18912}," y-min",{"type":42,"tag":229,"props":18914,"children":18915},{"style":1260},[18916],{"type":48,"value":1280},{"type":42,"tag":229,"props":18918,"children":18919},{"style":1260},[18920],{"type":48,"value":1285},{"type":42,"tag":229,"props":18922,"children":18923},{"style":1288},[18924],{"type":48,"value":18925},"0",{"type":42,"tag":229,"props":18927,"children":18928},{"style":1260},[18929],{"type":48,"value":1285},{"type":42,"tag":229,"props":18931,"children":18932},{"style":1272},[18933],{"type":48,"value":18934}," grid-lines",{"type":42,"tag":229,"props":18936,"children":18937},{"style":1260},[18938],{"type":48,"value":1280},{"type":42,"tag":229,"props":18940,"children":18941},{"style":1260},[18942],{"type":48,"value":1285},{"type":42,"tag":229,"props":18944,"children":18945},{"style":1288},[18946],{"type":48,"value":8935},{"type":42,"tag":229,"props":18948,"children":18949},{"style":1260},[18950],{"type":48,"value":1285},{"type":42,"tag":229,"props":18952,"children":18953},{"style":1272},[18954],{"type":48,"value":1300},{"type":42,"tag":229,"props":18956,"children":18957},{"style":1260},[18958],{"type":48,"value":1280},{"type":42,"tag":229,"props":18960,"children":18961},{"style":1260},[18962],{"type":48,"value":1285},{"type":42,"tag":229,"props":18964,"children":18965},{"style":1288},[18966],{"type":48,"value":18967},"Star history",{"type":42,"tag":229,"props":18969,"children":18970},{"style":1260},[18971],{"type":48,"value":1285},{"type":42,"tag":229,"props":18973,"children":18974},{"style":1260},[18975],{"type":48,"value":1322},{"type":42,"tag":229,"props":18977,"children":18978},{"class":231,"line":260},[18979,18983,18987,18992,18996,19000,19004,19008,19013,19017,19021,19026,19030,19035,19039,19043,19047,19051,19055,19059,19063,19068,19072],{"type":42,"tag":229,"props":18980,"children":18981},{"style":1260},[18982],{"type":48,"value":1330},{"type":42,"tag":229,"props":18984,"children":18985},{"style":1266},[18986],{"type":48,"value":3713},{"type":42,"tag":229,"props":18988,"children":18989},{"style":1272},[18990],{"type":48,"value":18991}," kind",{"type":42,"tag":229,"props":18993,"children":18994},{"style":1260},[18995],{"type":48,"value":1280},{"type":42,"tag":229,"props":18997,"children":18998},{"style":1260},[18999],{"type":48,"value":1285},{"type":42,"tag":229,"props":19001,"children":19002},{"style":1288},[19003],{"type":48,"value":3759},{"type":42,"tag":229,"props":19005,"children":19006},{"style":1260},[19007],{"type":48,"value":1285},{"type":42,"tag":229,"props":19009,"children":19010},{"style":1272},[19011],{"type":48,"value":19012}," values",{"type":42,"tag":229,"props":19014,"children":19015},{"style":1260},[19016],{"type":48,"value":1280},{"type":42,"tag":229,"props":19018,"children":19019},{"style":1260},[19020],{"type":48,"value":1285},{"type":42,"tag":229,"props":19022,"children":19023},{"style":1288},[19024],{"type":48,"value":19025},"{sdkStars}",{"type":42,"tag":229,"props":19027,"children":19028},{"style":1260},[19029],{"type":48,"value":1285},{"type":42,"tag":229,"props":19031,"children":19032},{"style":1272},[19033],{"type":48,"value":19034}," color",{"type":42,"tag":229,"props":19036,"children":19037},{"style":1260},[19038],{"type":48,"value":1280},{"type":42,"tag":229,"props":19040,"children":19041},{"style":1260},[19042],{"type":48,"value":1285},{"type":42,"tag":229,"props":19044,"children":19045},{"style":1288},[19046],{"type":48,"value":8407},{"type":42,"tag":229,"props":19048,"children":19049},{"style":1260},[19050],{"type":48,"value":1285},{"type":42,"tag":229,"props":19052,"children":19053},{"style":1272},[19054],{"type":48,"value":1300},{"type":42,"tag":229,"props":19056,"children":19057},{"style":1260},[19058],{"type":48,"value":1280},{"type":42,"tag":229,"props":19060,"children":19061},{"style":1260},[19062],{"type":48,"value":1285},{"type":42,"tag":229,"props":19064,"children":19065},{"style":1288},[19066],{"type":48,"value":19067},"native-sdk",{"type":42,"tag":229,"props":19069,"children":19070},{"style":1260},[19071],{"type":48,"value":1285},{"type":42,"tag":229,"props":19073,"children":19074},{"style":1260},[19075],{"type":48,"value":10935},{"type":42,"tag":229,"props":19077,"children":19078},{"class":231,"line":269},[19079,19083,19087,19091,19095,19099,19103,19107,19111,19115,19119,19124,19128,19132,19136,19140,19144,19148,19152,19156,19160,19165,19169],{"type":42,"tag":229,"props":19080,"children":19081},{"style":1260},[19082],{"type":48,"value":1330},{"type":42,"tag":229,"props":19084,"children":19085},{"style":1266},[19086],{"type":48,"value":3713},{"type":42,"tag":229,"props":19088,"children":19089},{"style":1272},[19090],{"type":48,"value":18991},{"type":42,"tag":229,"props":19092,"children":19093},{"style":1260},[19094],{"type":48,"value":1280},{"type":42,"tag":229,"props":19096,"children":19097},{"style":1260},[19098],{"type":48,"value":1285},{"type":42,"tag":229,"props":19100,"children":19101},{"style":1288},[19102],{"type":48,"value":231},{"type":42,"tag":229,"props":19104,"children":19105},{"style":1260},[19106],{"type":48,"value":1285},{"type":42,"tag":229,"props":19108,"children":19109},{"style":1272},[19110],{"type":48,"value":19012},{"type":42,"tag":229,"props":19112,"children":19113},{"style":1260},[19114],{"type":48,"value":1280},{"type":42,"tag":229,"props":19116,"children":19117},{"style":1260},[19118],{"type":48,"value":1285},{"type":42,"tag":229,"props":19120,"children":19121},{"style":1288},[19122],{"type":48,"value":19123},"{examplesStars}",{"type":42,"tag":229,"props":19125,"children":19126},{"style":1260},[19127],{"type":48,"value":1285},{"type":42,"tag":229,"props":19129,"children":19130},{"style":1272},[19131],{"type":48,"value":19034},{"type":42,"tag":229,"props":19133,"children":19134},{"style":1260},[19135],{"type":48,"value":1280},{"type":42,"tag":229,"props":19137,"children":19138},{"style":1260},[19139],{"type":48,"value":1285},{"type":42,"tag":229,"props":19141,"children":19142},{"style":1288},[19143],{"type":48,"value":8546},{"type":42,"tag":229,"props":19145,"children":19146},{"style":1260},[19147],{"type":48,"value":1285},{"type":42,"tag":229,"props":19149,"children":19150},{"style":1272},[19151],{"type":48,"value":1300},{"type":42,"tag":229,"props":19153,"children":19154},{"style":1260},[19155],{"type":48,"value":1280},{"type":42,"tag":229,"props":19157,"children":19158},{"style":1260},[19159],{"type":48,"value":1285},{"type":42,"tag":229,"props":19161,"children":19162},{"style":1288},[19163],{"type":48,"value":19164},"examples",{"type":42,"tag":229,"props":19166,"children":19167},{"style":1260},[19168],{"type":48,"value":1285},{"type":42,"tag":229,"props":19170,"children":19171},{"style":1260},[19172],{"type":48,"value":10935},{"type":42,"tag":229,"props":19174,"children":19175},{"class":231,"line":278},[19176,19180,19184],{"type":42,"tag":229,"props":19177,"children":19178},{"style":1260},[19179],{"type":48,"value":1371},{"type":42,"tag":229,"props":19181,"children":19182},{"style":1266},[19183],{"type":48,"value":3706},{"type":42,"tag":229,"props":19185,"children":19186},{"style":1260},[19187],{"type":48,"value":1322},{"type":42,"tag":229,"props":19189,"children":19190},{"class":231,"line":287},[19191],{"type":42,"tag":229,"props":19192,"children":19193},{"style":15826},[19194],{"type":48,"value":19195},"\u003C!-- Sparkline tile: zero-baseline bars pinned to an absolute 0..1 domain. -->\n",{"type":42,"tag":229,"props":19197,"children":19198},{"class":231,"line":296},[19199,19203,19207,19212,19216,19220,19225,19229,19233,19237,19241,19246,19250,19254,19258,19262,19266,19270,19275,19279,19283,19287,19291,19295,19299,19303,19308,19312],{"type":42,"tag":229,"props":19200,"children":19201},{"style":1260},[19202],{"type":48,"value":1263},{"type":42,"tag":229,"props":19204,"children":19205},{"style":1266},[19206],{"type":48,"value":3706},{"type":42,"tag":229,"props":19208,"children":19209},{"style":1272},[19210],{"type":48,"value":19211}," width",{"type":42,"tag":229,"props":19213,"children":19214},{"style":1260},[19215],{"type":48,"value":1280},{"type":42,"tag":229,"props":19217,"children":19218},{"style":1260},[19219],{"type":48,"value":1285},{"type":42,"tag":229,"props":19221,"children":19222},{"style":1288},[19223],{"type":48,"value":19224},"239",{"type":42,"tag":229,"props":19226,"children":19227},{"style":1260},[19228],{"type":48,"value":1285},{"type":42,"tag":229,"props":19230,"children":19231},{"style":1272},[19232],{"type":48,"value":18891},{"type":42,"tag":229,"props":19234,"children":19235},{"style":1260},[19236],{"type":48,"value":1280},{"type":42,"tag":229,"props":19238,"children":19239},{"style":1260},[19240],{"type":48,"value":1285},{"type":42,"tag":229,"props":19242,"children":19243},{"style":1288},[19244],{"type":48,"value":19245},"32",{"type":42,"tag":229,"props":19247,"children":19248},{"style":1260},[19249],{"type":48,"value":1285},{"type":42,"tag":229,"props":19251,"children":19252},{"style":1272},[19253],{"type":48,"value":18912},{"type":42,"tag":229,"props":19255,"children":19256},{"style":1260},[19257],{"type":48,"value":1280},{"type":42,"tag":229,"props":19259,"children":19260},{"style":1260},[19261],{"type":48,"value":1285},{"type":42,"tag":229,"props":19263,"children":19264},{"style":1288},[19265],{"type":48,"value":18925},{"type":42,"tag":229,"props":19267,"children":19268},{"style":1260},[19269],{"type":48,"value":1285},{"type":42,"tag":229,"props":19271,"children":19272},{"style":1272},[19273],{"type":48,"value":19274}," y-max",{"type":42,"tag":229,"props":19276,"children":19277},{"style":1260},[19278],{"type":48,"value":1280},{"type":42,"tag":229,"props":19280,"children":19281},{"style":1260},[19282],{"type":48,"value":1285},{"type":42,"tag":229,"props":19284,"children":19285},{"style":1288},[19286],{"type":48,"value":1352},{"type":42,"tag":229,"props":19288,"children":19289},{"style":1260},[19290],{"type":48,"value":1285},{"type":42,"tag":229,"props":19292,"children":19293},{"style":1272},[19294],{"type":48,"value":1300},{"type":42,"tag":229,"props":19296,"children":19297},{"style":1260},[19298],{"type":48,"value":1280},{"type":42,"tag":229,"props":19300,"children":19301},{"style":1260},[19302],{"type":48,"value":1285},{"type":42,"tag":229,"props":19304,"children":19305},{"style":1288},[19306],{"type":48,"value":19307},"CPU history",{"type":42,"tag":229,"props":19309,"children":19310},{"style":1260},[19311],{"type":48,"value":1285},{"type":42,"tag":229,"props":19313,"children":19314},{"style":1260},[19315],{"type":48,"value":1322},{"type":42,"tag":229,"props":19317,"children":19318},{"class":231,"line":305},[19319,19323,19327,19331,19335,19339,19343,19347,19351,19355,19359,19364,19368],{"type":42,"tag":229,"props":19320,"children":19321},{"style":1260},[19322],{"type":48,"value":1330},{"type":42,"tag":229,"props":19324,"children":19325},{"style":1266},[19326],{"type":48,"value":3713},{"type":42,"tag":229,"props":19328,"children":19329},{"style":1272},[19330],{"type":48,"value":18991},{"type":42,"tag":229,"props":19332,"children":19333},{"style":1260},[19334],{"type":48,"value":1280},{"type":42,"tag":229,"props":19336,"children":19337},{"style":1260},[19338],{"type":48,"value":1285},{"type":42,"tag":229,"props":19340,"children":19341},{"style":1288},[19342],{"type":48,"value":3766},{"type":42,"tag":229,"props":19344,"children":19345},{"style":1260},[19346],{"type":48,"value":1285},{"type":42,"tag":229,"props":19348,"children":19349},{"style":1272},[19350],{"type":48,"value":19012},{"type":42,"tag":229,"props":19352,"children":19353},{"style":1260},[19354],{"type":48,"value":1280},{"type":42,"tag":229,"props":19356,"children":19357},{"style":1260},[19358],{"type":48,"value":1285},{"type":42,"tag":229,"props":19360,"children":19361},{"style":1288},[19362],{"type":48,"value":19363},"{cpuSpark}",{"type":42,"tag":229,"props":19365,"children":19366},{"style":1260},[19367],{"type":48,"value":1285},{"type":42,"tag":229,"props":19369,"children":19370},{"style":1260},[19371],{"type":48,"value":10935},{"type":42,"tag":229,"props":19373,"children":19374},{"class":231,"line":314},[19375,19379,19383],{"type":42,"tag":229,"props":19376,"children":19377},{"style":1260},[19378],{"type":48,"value":1371},{"type":42,"tag":229,"props":19380,"children":19381},{"style":1266},[19382],{"type":48,"value":3706},{"type":42,"tag":229,"props":19384,"children":19385},{"style":1260},[19386],{"type":48,"value":1322},{"type":42,"tag":57,"props":19388,"children":19389},{},[19390,19423,19465,19506,19534,19547,19604,19646,19651,19678,19697],{"type":42,"tag":61,"props":19391,"children":19392},{},[19393,19395,19400,19402,19407,19409,19415,19416,19421],{"type":48,"value":19394},"Kinds (literal): ",{"type":42,"tag":65,"props":19396,"children":19398},{"className":19397},[],[19399],{"type":48,"value":231},{"type":48,"value":19401}," (polyline; one sample draws a dot), ",{"type":42,"tag":65,"props":19403,"children":19405},{"className":19404},[],[19406],{"type":48,"value":3759},{"type":48,"value":19408}," (a line filled to the baseline — the builder's ",{"type":42,"tag":65,"props":19410,"children":19412},{"className":19411},[],[19413],{"type":48,"value":19414},"fill = true",{"type":48,"value":4517},{"type":42,"tag":65,"props":19417,"children":19419},{"className":19418},[],[19420],{"type":48,"value":3766},{"type":48,"value":19422}," (one bar per value, ALWAYS anchored at zero — the auto domain forces 0 in, negatives hang below; a zero value draws nothing).",{"type":42,"tag":61,"props":19424,"children":19425},{},[19426,19428,19434,19436,19441,19443,19448,19450,19456,19458,19463],{"type":48,"value":19427},"Data: ",{"type":42,"tag":65,"props":19429,"children":19431},{"className":19430},[],[19432],{"type":48,"value":19433},"values",{"type":48,"value":19435}," takes one ",{"type":42,"tag":65,"props":19437,"children":19439},{"className":19438},[],[19440],{"type":48,"value":3355},{"type":48,"value":19442}," naming an f32 iterable — a model field (slice or array), pub decl, or fn (arena fns work), the SAME resolution set as ",{"type":42,"tag":65,"props":19444,"children":19446},{"className":19445},[],[19447],{"type":48,"value":9484},{"type":48,"value":19449}," (slice-valued template args included). Values are y samples at uniform x steps, oldest first. ",{"type":42,"tag":65,"props":19451,"children":19453},{"className":19452},[],[19454],{"type":48,"value":19455},"NaN",{"type":48,"value":19457}," = missing sample, draws a gap — pad a filling window with leading ",{"type":42,"tag":65,"props":19459,"children":19461},{"className":19460},[],[19462],{"type":48,"value":19455},{"type":48,"value":19464}," in a model fn so the trace enters from the right (see examples\u002Fsystem-monitor). The series SET is static: the data varies through bindings, and dynamic series composition stays with the Zig builder.",{"type":42,"tag":61,"props":19466,"children":19467},{},[19468,19470,19475,19476,19481,19483,19489,19491,19497,19499,19504],{"type":48,"value":19469},"Domain: derived per side from the data unless ",{"type":42,"tag":65,"props":19471,"children":19473},{"className":19472},[],[19474],{"type":48,"value":3789},{"type":48,"value":456},{"type":42,"tag":65,"props":19477,"children":19479},{"className":19478},[],[19480],{"type":48,"value":3796},{"type":48,"value":19482}," pin it (literals or scalar bindings); a flat series expands symmetrically. ",{"type":42,"tag":65,"props":19484,"children":19486},{"className":19485},[],[19487],{"type":48,"value":19488},"grid-lines=\"N\"",{"type":48,"value":19490}," draws N horizontal token hairlines (opt-in, none by default); ",{"type":42,"tag":65,"props":19492,"children":19494},{"className":19493},[],[19495],{"type":48,"value":19496},"baseline=\"true\"",{"type":48,"value":19498}," marks the zero line; ",{"type":42,"tag":65,"props":19500,"children":19502},{"className":19501},[],[19503],{"type":48,"value":3838},{"type":48,"value":19505}," overrides the 1.5 default.",{"type":42,"tag":61,"props":19507,"children":19508},{},[19509,19511,19517,19519,19525,19527,19532],{"type":48,"value":19510},"Axis labels (opt-in, muted register, reserved gutters): ",{"type":42,"tag":65,"props":19512,"children":19514},{"className":19513},[],[19515],{"type":48,"value":19516},"x-labels=\"{binding}\"",{"type":48,"value":19518}," names a model iterable of strings — one category label per sample, oldest first, thinned deterministically to fit the width (dropped entirely when a series downsamples: bucketed indices no longer name the labeled samples). ",{"type":42,"tag":65,"props":19520,"children":19522},{"className":19521},[],[19523],{"type":48,"value":19524},"y-labels=\"true\"",{"type":48,"value":19526}," adds numeric ticks on a nice-step lattice (1\u002F2\u002F5 × 10^k — labels exact at their precision); with ",{"type":42,"tag":65,"props":19528,"children":19530},{"className":19529},[],[19531],{"type":48,"value":3803},{"type":48,"value":19533}," set the gridlines ride the same lattice so grid and labels agree.",{"type":42,"tag":61,"props":19535,"children":19536},{},[19537,19539,19545],{"type":48,"value":19538},"Hover details: ",{"type":42,"tag":65,"props":19540,"children":19542},{"className":19541},[],[19543],{"type":48,"value":19544},"hover-details=\"true\"",{"type":48,"value":19546}," snaps the pointer to the nearest sample and floats a card (sample label + every series' name and value, hairline cursor, dots on line points). Interaction-only chrome — static renders never show it; the chart becomes a hover target but still never claims presses.",{"type":42,"tag":61,"props":19548,"children":19549},{},[19550,19552,19557,19558,19563,19565,19570,19572,19578,19580,19585,19586,19591,19592,19597,19598,19603],{"type":48,"value":19551},"Box: ",{"type":42,"tag":65,"props":19553,"children":19555},{"className":19554},[],[19556],{"type":48,"value":2339},{"type":48,"value":456},{"type":42,"tag":65,"props":19559,"children":19561},{"className":19560},[],[19562],{"type":48,"value":3289},{"type":48,"value":19564}," (definite; omitted keeps the intrinsic 160x48 sparkline default), ",{"type":42,"tag":65,"props":19566,"children":19568},{"className":19567},[],[19569],{"type":48,"value":3242},{"type":48,"value":19571}," (flexes the pane like any element — ",{"type":42,"tag":65,"props":19573,"children":19575},{"className":19574},[],[19576],{"type":48,"value":19577},"grow=\"1\"",{"type":48,"value":19579}," is how a chart fills its column), ",{"type":42,"tag":65,"props":19581,"children":19583},{"className":19582},[],[19584],{"type":48,"value":4431},{"type":48,"value":1820},{"type":42,"tag":65,"props":19587,"children":19589},{"className":19588},[],[19590],{"type":48,"value":3595},{"type":48,"value":456},{"type":42,"tag":65,"props":19593,"children":19595},{"className":19594},[],[19596],{"type":48,"value":3602},{"type":48,"value":1820},{"type":42,"tag":65,"props":19599,"children":19601},{"className":19600},[],[19602],{"type":48,"value":2892},{"type":48,"value":123},{"type":42,"tag":61,"props":19605,"children":19606},{},[19607,19609,19614,19615,19620,19621,19626,19627,19632,19633,19638,19640,19645],{"type":48,"value":19608},"Colors are token names (",{"type":42,"tag":65,"props":19610,"children":19612},{"className":19611},[],[19613],{"type":48,"value":8407},{"type":48,"value":1820},{"type":42,"tag":65,"props":19616,"children":19618},{"className":19617},[],[19619],{"type":48,"value":8546},{"type":48,"value":1820},{"type":42,"tag":65,"props":19622,"children":19624},{"className":19623},[],[19625],{"type":48,"value":8518},{"type":48,"value":1820},{"type":42,"tag":65,"props":19628,"children":19630},{"className":19629},[],[19631],{"type":48,"value":8532},{"type":48,"value":1820},{"type":42,"tag":65,"props":19634,"children":19636},{"className":19635},[],[19637],{"type":48,"value":8504},{"type":48,"value":19639},", ...) — never raw colors — so both themes hold up; default ",{"type":42,"tag":65,"props":19641,"children":19643},{"className":19642},[],[19644],{"type":48,"value":8407},{"type":48,"value":123},{"type":42,"tag":61,"props":19647,"children":19648},{},[19649],{"type":48,"value":19650},"Downsampling: past 256 points per series, deterministic index-bucket min\u002Fmax decimation (spikes survive; same series → same pixels, golden-testable). The generated semantics summary still describes the SOURCE series.",{"type":42,"tag":61,"props":19652,"children":19653},{},[19654,19656,19661,19663,19669,19671,19676],{"type":48,"value":19655},"Semantics: role ",{"type":42,"tag":65,"props":19657,"children":19659},{"className":19658},[],[19660],{"type":48,"value":3706},{"type":48,"value":19662},"; label = a generated summary (",{"type":42,"tag":65,"props":19664,"children":19666},{"className":19665},[],[19667],{"type":48,"value":19668},"\"chart: stars 10000 pts last 9999.00\"",{"type":48,"value":19670},") unless ",{"type":42,"tag":65,"props":19672,"children":19674},{"className":19673},[],[19675],{"type":48,"value":2892},{"type":48,"value":19677}," is set; accessibility value = the first series' latest point — assert live data from snapshots without pixels.",{"type":42,"tag":61,"props":19679,"children":19680},{},[19681,19683,19688,19690,19695],{"type":48,"value":19682},"Display-only: no ",{"type":42,"tag":65,"props":19684,"children":19686},{"className":19685},[],[19687],{"type":48,"value":9125},{"type":48,"value":19689}," events and never a press claimer; clicks fall through to the nearest pressable ancestor, so charts inside pressable rows keep the row clickable (",{"type":42,"tag":65,"props":19691,"children":19693},{"className":19692},[],[19694],{"type":48,"value":3831},{"type":48,"value":19696}," makes it a hover target only).",{"type":42,"tag":61,"props":19698,"children":19699},{},[19700,19702,19708,19710,19715,19717,19722,19724,19730,19732,19738],{"type":48,"value":19701},"Zig escape hatch: ",{"type":42,"tag":65,"props":19703,"children":19705},{"className":19704},[],[19706],{"type":48,"value":19707},"ui.chart(ChartOptions, &.{canvas.ChartSeries...})",{"type":48,"value":19709}," is the same code with the same options, plus what markup deliberately excludes — ",{"type":42,"tag":65,"props":19711,"children":19713},{"className":19712},[],[19714],{"type":48,"value":4215},{"type":48,"value":19716}," series (min\u002Fmax envelope: ",{"type":42,"tag":65,"props":19718,"children":19720},{"className":19719},[],[19721],{"type":48,"value":19433},{"type":48,"value":19723}," upper, ",{"type":42,"tag":65,"props":19725,"children":19727},{"className":19726},[],[19728],{"type":48,"value":19729},"low",{"type":48,"value":19731}," lower — a PAIRED second slice per point) and series lists composed at view time (",{"type":42,"tag":65,"props":19733,"children":19735},{"className":19734},[],[19736],{"type":48,"value":19737},"examples\u002Fdeck",{"type":48,"value":19739}," builds its peak-trace series data in the model; a truly dynamic series COUNT is builder territory).",{"type":42,"tag":212,"props":19741,"children":19743},{"id":19742},"rich-text-inline-spans-and-markdown",[19744],{"type":48,"value":19745},"Rich text: inline spans and markdown",{"type":42,"tag":51,"props":19747,"children":19748},{},[19749,19751,19757,19759,19765,19767,19773,19774,19780,19781,19787,19789,19795,19796,19801,19802,19807,19808,19813,19815,19820,19821,19826],{"type":48,"value":19750},"Mixed-style text inside ONE wrapped paragraph: put ",{"type":42,"tag":65,"props":19752,"children":19754},{"className":19753},[],[19755],{"type":48,"value":19756},"\u003Cspan>",{"type":48,"value":19758}," children inside a ",{"type":42,"tag":65,"props":19760,"children":19762},{"className":19761},[],[19763],{"type":48,"value":19764},"\u003Ctext>",{"type":48,"value":19766}," element. Each span styles one run — ",{"type":42,"tag":65,"props":19768,"children":19770},{"className":19769},[],[19771],{"type":48,"value":19772},"weight",{"type":48,"value":2620},{"type":42,"tag":65,"props":19775,"children":19777},{"className":19776},[],[19778],{"type":48,"value":19779},"regular",{"type":48,"value":456},{"type":42,"tag":65,"props":19782,"children":19784},{"className":19783},[],[19785],{"type":48,"value":19786},"medium",{"type":48,"value":19788}," — the semibold rung —\u002F",{"type":42,"tag":65,"props":19790,"children":19792},{"className":19791},[],[19793],{"type":48,"value":19794},"bold",{"type":48,"value":4517},{"type":42,"tag":65,"props":19797,"children":19799},{"className":19798},[],[19800],{"type":48,"value":2744},{"type":48,"value":1820},{"type":42,"tag":65,"props":19803,"children":19805},{"className":19804},[],[19806],{"type":48,"value":2751},{"type":48,"value":1820},{"type":42,"tag":65,"props":19809,"children":19811},{"className":19810},[],[19812],{"type":48,"value":2758},{"type":48,"value":19814}," (a positive multiplier on the paragraph's base size), ",{"type":42,"tag":65,"props":19816,"children":19818},{"className":19817},[],[19819],{"type":48,"value":2766},{"type":48,"value":2871},{"type":42,"tag":65,"props":19822,"children":19824},{"className":19823},[],[19825],{"type":48,"value":2773},{"type":48,"value":19827}," (a color token name) — and the whole thing word-wraps as one paragraph, announcing to assistive tech as ONE text run (spans are visual). Bindings interpolate inside spans like any text:",{"type":42,"tag":219,"props":19829,"children":19831},{"className":1248,"code":19830,"language":1250,"meta":223,"style":223},"\u003Ctext>\n  Disk \u003Cspan weight=\"bold\">{diskUsed}\u003C\u002Fspan> of\n  \u003Cspan foreground=\"text_muted\">{diskTotal}\u003C\u002Fspan> — run\n  \u003Cspan mono=\"true\">native doctor\u003C\u002Fspan> if this looks wrong.\n\u003C\u002Ftext>\n",[19832],{"type":42,"tag":65,"props":19833,"children":19834},{"__ignoreMap":223},[19835,19850,19913,19970,20028],{"type":42,"tag":229,"props":19836,"children":19837},{"class":231,"line":232},[19838,19842,19846],{"type":42,"tag":229,"props":19839,"children":19840},{"style":1260},[19841],{"type":48,"value":1263},{"type":42,"tag":229,"props":19843,"children":19844},{"style":1266},[19845],{"type":48,"value":48},{"type":42,"tag":229,"props":19847,"children":19848},{"style":1260},[19849],{"type":48,"value":1322},{"type":42,"tag":229,"props":19851,"children":19852},{"class":231,"line":241},[19853,19858,19862,19866,19871,19875,19879,19883,19887,19891,19896,19900,19904,19908],{"type":42,"tag":229,"props":19854,"children":19855},{"style":1364},[19856],{"type":48,"value":19857},"  Disk ",{"type":42,"tag":229,"props":19859,"children":19860},{"style":1260},[19861],{"type":48,"value":1263},{"type":42,"tag":229,"props":19863,"children":19864},{"style":1266},[19865],{"type":48,"value":229},{"type":42,"tag":229,"props":19867,"children":19868},{"style":1272},[19869],{"type":48,"value":19870}," weight",{"type":42,"tag":229,"props":19872,"children":19873},{"style":1260},[19874],{"type":48,"value":1280},{"type":42,"tag":229,"props":19876,"children":19877},{"style":1260},[19878],{"type":48,"value":1285},{"type":42,"tag":229,"props":19880,"children":19881},{"style":1288},[19882],{"type":48,"value":19794},{"type":42,"tag":229,"props":19884,"children":19885},{"style":1260},[19886],{"type":48,"value":1285},{"type":42,"tag":229,"props":19888,"children":19889},{"style":1260},[19890],{"type":48,"value":1361},{"type":42,"tag":229,"props":19892,"children":19893},{"style":1364},[19894],{"type":48,"value":19895},"{diskUsed}",{"type":42,"tag":229,"props":19897,"children":19898},{"style":1260},[19899],{"type":48,"value":1371},{"type":42,"tag":229,"props":19901,"children":19902},{"style":1266},[19903],{"type":48,"value":229},{"type":42,"tag":229,"props":19905,"children":19906},{"style":1260},[19907],{"type":48,"value":1361},{"type":42,"tag":229,"props":19909,"children":19910},{"style":1364},[19911],{"type":48,"value":19912}," of\n",{"type":42,"tag":229,"props":19914,"children":19915},{"class":231,"line":251},[19916,19920,19924,19928,19932,19936,19940,19944,19948,19953,19957,19961,19965],{"type":42,"tag":229,"props":19917,"children":19918},{"style":1260},[19919],{"type":48,"value":1330},{"type":42,"tag":229,"props":19921,"children":19922},{"style":1266},[19923],{"type":48,"value":229},{"type":42,"tag":229,"props":19925,"children":19926},{"style":1272},[19927],{"type":48,"value":8742},{"type":42,"tag":229,"props":19929,"children":19930},{"style":1260},[19931],{"type":48,"value":1280},{"type":42,"tag":229,"props":19933,"children":19934},{"style":1260},[19935],{"type":48,"value":1285},{"type":42,"tag":229,"props":19937,"children":19938},{"style":1288},[19939],{"type":48,"value":8477},{"type":42,"tag":229,"props":19941,"children":19942},{"style":1260},[19943],{"type":48,"value":1285},{"type":42,"tag":229,"props":19945,"children":19946},{"style":1260},[19947],{"type":48,"value":1361},{"type":42,"tag":229,"props":19949,"children":19950},{"style":1364},[19951],{"type":48,"value":19952},"{diskTotal}",{"type":42,"tag":229,"props":19954,"children":19955},{"style":1260},[19956],{"type":48,"value":1371},{"type":42,"tag":229,"props":19958,"children":19959},{"style":1266},[19960],{"type":48,"value":229},{"type":42,"tag":229,"props":19962,"children":19963},{"style":1260},[19964],{"type":48,"value":1361},{"type":42,"tag":229,"props":19966,"children":19967},{"style":1364},[19968],{"type":48,"value":19969}," — run\n",{"type":42,"tag":229,"props":19971,"children":19972},{"class":231,"line":260},[19973,19977,19981,19986,19990,19994,19998,20002,20006,20011,20015,20019,20023],{"type":42,"tag":229,"props":19974,"children":19975},{"style":1260},[19976],{"type":48,"value":1330},{"type":42,"tag":229,"props":19978,"children":19979},{"style":1266},[19980],{"type":48,"value":229},{"type":42,"tag":229,"props":19982,"children":19983},{"style":1272},[19984],{"type":48,"value":19985}," mono",{"type":42,"tag":229,"props":19987,"children":19988},{"style":1260},[19989],{"type":48,"value":1280},{"type":42,"tag":229,"props":19991,"children":19992},{"style":1260},[19993],{"type":48,"value":1285},{"type":42,"tag":229,"props":19995,"children":19996},{"style":1288},[19997],{"type":48,"value":5105},{"type":42,"tag":229,"props":19999,"children":20000},{"style":1260},[20001],{"type":48,"value":1285},{"type":42,"tag":229,"props":20003,"children":20004},{"style":1260},[20005],{"type":48,"value":1361},{"type":42,"tag":229,"props":20007,"children":20008},{"style":1364},[20009],{"type":48,"value":20010},"native doctor",{"type":42,"tag":229,"props":20012,"children":20013},{"style":1260},[20014],{"type":48,"value":1371},{"type":42,"tag":229,"props":20016,"children":20017},{"style":1266},[20018],{"type":48,"value":229},{"type":42,"tag":229,"props":20020,"children":20021},{"style":1260},[20022],{"type":48,"value":1361},{"type":42,"tag":229,"props":20024,"children":20025},{"style":1364},[20026],{"type":48,"value":20027}," if this looks wrong.\n",{"type":42,"tag":229,"props":20029,"children":20030},{"class":231,"line":269},[20031,20035,20039],{"type":42,"tag":229,"props":20032,"children":20033},{"style":1260},[20034],{"type":48,"value":1371},{"type":42,"tag":229,"props":20036,"children":20037},{"style":1266},[20038],{"type":48,"value":48},{"type":42,"tag":229,"props":20040,"children":20041},{"style":1260},[20042],{"type":48,"value":1322},{"type":42,"tag":57,"props":20044,"children":20045},{},[20046,20059,20110,20141,20158],{"type":42,"tag":61,"props":20047,"children":20048},{},[20049,20051,20057],{"type":48,"value":20050},"Whitespace between runs collapses to a single space; runs written with NO whitespace between them abut (",{"type":42,"tag":65,"props":20052,"children":20054},{"className":20053},[],[20055],{"type":48,"value":20056},"\u003Cspan mono=\"true\">init.zig\u003C\u002Fspan>.",{"type":48,"value":20058}," puts the period flush against the mono run).",{"type":42,"tag":61,"props":20060,"children":20061},{},[20062,20064,20069,20070,20075,20076,20081,20083,20088,20090,20095,20097,20102,20103,20108],{"type":48,"value":20063},"Spans do not nest and take no events or keys — layout (",{"type":42,"tag":65,"props":20065,"children":20067},{"className":20066},[],[20068],{"type":48,"value":2339},{"type":48,"value":1820},{"type":42,"tag":65,"props":20071,"children":20073},{"className":20072},[],[20074],{"type":48,"value":3242},{"type":48,"value":1820},{"type":42,"tag":65,"props":20077,"children":20079},{"className":20078},[],[20080],{"type":48,"value":2355},{"type":48,"value":20082},"), identity, and ",{"type":42,"tag":65,"props":20084,"children":20086},{"className":20085},[],[20087],{"type":48,"value":2892},{"type":48,"value":20089}," stay on the enclosing ",{"type":42,"tag":65,"props":20091,"children":20093},{"className":20092},[],[20094],{"type":48,"value":19764},{"type":48,"value":20096},". A span paragraph always word-wraps, so ",{"type":42,"tag":65,"props":20098,"children":20100},{"className":20099},[],[20101],{"type":48,"value":2618},{"type":48,"value":456},{"type":42,"tag":65,"props":20104,"children":20106},{"className":20105},[],[20107],{"type":48,"value":4515},{"type":48,"value":20109}," on it are teaching errors.",{"type":42,"tag":61,"props":20111,"children":20112},{},[20113,20118,20120,20125,20126,20132,20134,20139],{"type":42,"tag":65,"props":20114,"children":20116},{"className":20115},[],[20117],{"type":48,"value":2758},{"type":48,"value":20119}," multiplies the base size the paragraph's typography resolves to — ",{"type":42,"tag":65,"props":20121,"children":20123},{"className":20122},[],[20124],{"type":48,"value":2657},{"type":48,"value":157},{"type":42,"tag":65,"props":20127,"children":20129},{"className":20128},[],[20130],{"type":48,"value":20131},"scale=\"1.5\"",{"type":48,"value":20133}," draws heading × 1.5 — and line breaking measures scaled runs at their scaled size, so the wrap is honest. Only positive finite multipliers are accepted (a literal number or one ",{"type":42,"tag":65,"props":20135,"children":20137},{"className":20136},[],[20138],{"type":48,"value":3355},{"type":48,"value":20140}," producing one); zero\u002Fnegative\u002Fnon-numeric scales are teaching errors. One limit to design around: the paragraph reserves ONE uniform line height sized by its largest scale and every run shares that baseline — use mixed scale for inline headings and hero stats beside their captions, not to stack independent text sizes.",{"type":42,"tag":61,"props":20142,"children":20143},{},[20144,20149,20151,20156],{"type":42,"tag":65,"props":20145,"children":20147},{"className":20146},[],[20148],{"type":48,"value":2766},{"type":48,"value":20150}," is a pure decoration (true\u002Ffalse or a ",{"type":42,"tag":65,"props":20152,"children":20154},{"className":20153},[],[20155],{"type":48,"value":3355},{"type":48,"value":20157},") — it does not make the run a link.",{"type":42,"tag":61,"props":20159,"children":20160},{},[20161,20163,20169,20170,20175,20177,20183],{"type":48,"value":20162},"Markup exposes the span model's weight\u002Fmono\u002Fitalic\u002Fscale\u002Funderline\u002Fcolor channels; ",{"type":42,"tag":65,"props":20164,"children":20166},{"className":20165},[],[20167],{"type":48,"value":20168},"strikethrough",{"type":48,"value":1820},{"type":42,"tag":65,"props":20171,"children":20173},{"className":20172},[],[20174],{"type":48,"value":8394},{"type":48,"value":20176}," highlights, and ",{"type":42,"tag":65,"props":20178,"children":20180},{"className":20179},[],[20181],{"type":48,"value":20182},"link",{"type":48,"value":20184}," spans stay Zig-builder territory (below).",{"type":42,"tag":51,"props":20186,"children":20187},{},[20188],{"type":48,"value":20189},"The same paragraph from a Zig view, plus the builder-only channels:",{"type":42,"tag":219,"props":20191,"children":20193},{"className":221,"code":20192,"language":18,"meta":223,"style":223},"const spans = [_]canvas.TextSpan{\n    .{ .text = \"Ship the \" },\n    .{ .text = \"bold\", .weight = .bold },\n    .{ .text = \" parts, run \" },\n    .{ .text = \"zig build test\", .monospace = true },\n    .{ .text = \", then read \" },\n    .{ .text = \"the guide\", .link = \"https:\u002F\u002Fexample.com\u002Fguide\" },\n};\nui.paragraph(.{ .on_link = Ui.linkMsg(.open_url) }, &spans)\n",[20194],{"type":42,"tag":65,"props":20195,"children":20196},{"__ignoreMap":223},[20197,20205,20213,20221,20229,20237,20245,20253,20260],{"type":42,"tag":229,"props":20198,"children":20199},{"class":231,"line":232},[20200],{"type":42,"tag":229,"props":20201,"children":20202},{},[20203],{"type":48,"value":20204},"const spans = [_]canvas.TextSpan{\n",{"type":42,"tag":229,"props":20206,"children":20207},{"class":231,"line":241},[20208],{"type":42,"tag":229,"props":20209,"children":20210},{},[20211],{"type":48,"value":20212},"    .{ .text = \"Ship the \" },\n",{"type":42,"tag":229,"props":20214,"children":20215},{"class":231,"line":251},[20216],{"type":42,"tag":229,"props":20217,"children":20218},{},[20219],{"type":48,"value":20220},"    .{ .text = \"bold\", .weight = .bold },\n",{"type":42,"tag":229,"props":20222,"children":20223},{"class":231,"line":260},[20224],{"type":42,"tag":229,"props":20225,"children":20226},{},[20227],{"type":48,"value":20228},"    .{ .text = \" parts, run \" },\n",{"type":42,"tag":229,"props":20230,"children":20231},{"class":231,"line":269},[20232],{"type":42,"tag":229,"props":20233,"children":20234},{},[20235],{"type":48,"value":20236},"    .{ .text = \"zig build test\", .monospace = true },\n",{"type":42,"tag":229,"props":20238,"children":20239},{"class":231,"line":278},[20240],{"type":42,"tag":229,"props":20241,"children":20242},{},[20243],{"type":48,"value":20244},"    .{ .text = \", then read \" },\n",{"type":42,"tag":229,"props":20246,"children":20247},{"class":231,"line":287},[20248],{"type":42,"tag":229,"props":20249,"children":20250},{},[20251],{"type":48,"value":20252},"    .{ .text = \"the guide\", .link = \"https:\u002F\u002Fexample.com\u002Fguide\" },\n",{"type":42,"tag":229,"props":20254,"children":20255},{"class":231,"line":296},[20256],{"type":42,"tag":229,"props":20257,"children":20258},{},[20259],{"type":48,"value":669},{"type":42,"tag":229,"props":20261,"children":20262},{"class":231,"line":305},[20263],{"type":42,"tag":229,"props":20264,"children":20265},{},[20266],{"type":48,"value":20267},"ui.paragraph(.{ .on_link = Ui.linkMsg(.open_url) }, &spans)\n",{"type":42,"tag":57,"props":20269,"children":20270},{},[20271,20336,20341,20382],{"type":42,"tag":61,"props":20272,"children":20273},{},[20274,20276,20281,20283,20288,20289,20295,20296,20301,20303,20309,20311,20316,20317,20322,20323,20328,20330,20335],{"type":48,"value":20275},"Each span carries ",{"type":42,"tag":65,"props":20277,"children":20279},{"className":20278},[],[20280],{"type":48,"value":19772},{"type":48,"value":20282}," (regular\u002Fmedium\u002Fbold), ",{"type":42,"tag":65,"props":20284,"children":20286},{"className":20285},[],[20287],{"type":48,"value":2751},{"type":48,"value":1820},{"type":42,"tag":65,"props":20290,"children":20292},{"className":20291},[],[20293],{"type":48,"value":20294},"monospace",{"type":48,"value":1820},{"type":42,"tag":65,"props":20297,"children":20299},{"className":20298},[],[20300],{"type":48,"value":3774},{"type":48,"value":20302}," (a ",{"type":42,"tag":65,"props":20304,"children":20306},{"className":20305},[],[20307],{"type":48,"value":20308},"ColorTokens",{"type":48,"value":20310}," field name), ",{"type":42,"tag":65,"props":20312,"children":20314},{"className":20313},[],[20315],{"type":48,"value":2766},{"type":48,"value":1820},{"type":42,"tag":65,"props":20318,"children":20320},{"className":20319},[],[20321],{"type":48,"value":20168},{"type":48,"value":1820},{"type":42,"tag":65,"props":20324,"children":20326},{"className":20325},[],[20327],{"type":48,"value":2758},{"type":48,"value":20329}," (size multiplier vs the body token — how headings work), and ",{"type":42,"tag":65,"props":20331,"children":20333},{"className":20332},[],[20334],{"type":48,"value":20182},{"type":48,"value":123},{"type":42,"tag":61,"props":20337,"children":20338},{},[20339],{"type":48,"value":20340},"Wrapping is span-aware and measured with the same provider the platform draws with; a paragraph reserves its real wrapped height when stacked in a column.",{"type":42,"tag":61,"props":20342,"children":20343},{},[20344,20346,20352,20354,20360,20362,20367,20368,20373,20375,20381],{"type":48,"value":20345},"Link spans are hit-testable: they appear in automation snapshots as ",{"type":42,"tag":65,"props":20347,"children":20349},{"className":20348},[],[20350],{"type":48,"value":20351},"role=link",{"type":48,"value":20353}," named by their visible text, show a pointer cursor, and pressing one dispatches ",{"type":42,"tag":65,"props":20355,"children":20357},{"className":20356},[],[20358],{"type":48,"value":20359},"on_link(span.link)",{"type":48,"value":20361}," — declare ",{"type":42,"tag":65,"props":20363,"children":20365},{"className":20364},[],[20366],{"type":48,"value":18010},{"type":48,"value":18012},{"type":42,"tag":65,"props":20369,"children":20371},{"className":20370},[],[20372],{"type":48,"value":97},{"type":48,"value":20374}," and pair with ",{"type":42,"tag":65,"props":20376,"children":20378},{"className":20377},[],[20379],{"type":48,"value":20380},"Ui.linkMsg(.open_url)",{"type":48,"value":123},{"type":42,"tag":61,"props":20383,"children":20384},{},[20385,20387,20393],{"type":48,"value":20386},"Capacities: ",{"type":42,"tag":65,"props":20388,"children":20390},{"className":20389},[],[20391],{"type":48,"value":20392},"canvas.max_text_spans_per_paragraph",{"type":48,"value":20394}," (32) spans per paragraph; overflow truncates deterministically.",{"type":42,"tag":51,"props":20396,"children":20397},{},[20398,20400,20405],{"type":48,"value":20399},"Markdown (GitHub-flavored subset) maps onto the same widgets. In markup use the ",{"type":42,"tag":65,"props":20401,"children":20403},{"className":20402},[],[20404],{"type":48,"value":17835},{"type":48,"value":20406}," element above; from a Zig view call it directly:",{"type":42,"tag":219,"props":20408,"children":20410},{"className":221,"code":20409,"language":18,"meta":223,"style":223},"const Md = native_sdk.markdown.Markdown(Msg);\n\u002F\u002F inside view():\nMd.view(ui, model.body_markdown, .{\n    .on_link = Ui.linkMsg(.open_url),\n    .on_details = Md.detailsMsg(.toggle_details),   \u002F\u002F Msg{ .toggle_details = usize }\n    .details_expanded = &model.details_expanded,     \u002F\u002F caller-owned [N]bool\n})\n",[20411],{"type":42,"tag":65,"props":20412,"children":20413},{"__ignoreMap":223},[20414,20422,20430,20438,20446,20454,20462],{"type":42,"tag":229,"props":20415,"children":20416},{"class":231,"line":232},[20417],{"type":42,"tag":229,"props":20418,"children":20419},{},[20420],{"type":48,"value":20421},"const Md = native_sdk.markdown.Markdown(Msg);\n",{"type":42,"tag":229,"props":20423,"children":20424},{"class":231,"line":241},[20425],{"type":42,"tag":229,"props":20426,"children":20427},{},[20428],{"type":48,"value":20429},"\u002F\u002F inside view():\n",{"type":42,"tag":229,"props":20431,"children":20432},{"class":231,"line":251},[20433],{"type":42,"tag":229,"props":20434,"children":20435},{},[20436],{"type":48,"value":20437},"Md.view(ui, model.body_markdown, .{\n",{"type":42,"tag":229,"props":20439,"children":20440},{"class":231,"line":260},[20441],{"type":42,"tag":229,"props":20442,"children":20443},{},[20444],{"type":48,"value":20445},"    .on_link = Ui.linkMsg(.open_url),\n",{"type":42,"tag":229,"props":20447,"children":20448},{"class":231,"line":269},[20449],{"type":42,"tag":229,"props":20450,"children":20451},{},[20452],{"type":48,"value":20453},"    .on_details = Md.detailsMsg(.toggle_details),   \u002F\u002F Msg{ .toggle_details = usize }\n",{"type":42,"tag":229,"props":20455,"children":20456},{"class":231,"line":278},[20457],{"type":42,"tag":229,"props":20458,"children":20459},{},[20460],{"type":48,"value":20461},"    .details_expanded = &model.details_expanded,     \u002F\u002F caller-owned [N]bool\n",{"type":42,"tag":229,"props":20463,"children":20464},{"class":231,"line":287},[20465],{"type":42,"tag":229,"props":20466,"children":20467},{},[20468],{"type":48,"value":20469},"})\n",{"type":42,"tag":57,"props":20471,"children":20472},{},[20473,20605,20617],{"type":42,"tag":61,"props":20474,"children":20475},{},[20476,20478,20484,20486,20492,20494,20500,20501,20507,20508,20514,20515,20521,20522,20528,20530,20536,20538,20543,20545,20551,20553,20559,20560,20566,20568,20574,20575,20581,20582,20588,20590,20596,20598,20604],{"type":48,"value":20477},"Supported: ",{"type":42,"tag":65,"props":20479,"children":20481},{"className":20480},[],[20482],{"type":48,"value":20483},"#",{"type":48,"value":20485},"–",{"type":42,"tag":65,"props":20487,"children":20489},{"className":20488},[],[20490],{"type":48,"value":20491},"###",{"type":48,"value":20493}," headings, paragraphs with ",{"type":42,"tag":65,"props":20495,"children":20497},{"className":20496},[],[20498],{"type":48,"value":20499},"**bold**",{"type":48,"value":456},{"type":42,"tag":65,"props":20502,"children":20504},{"className":20503},[],[20505],{"type":48,"value":20506},"*italic*",{"type":48,"value":456},{"type":42,"tag":65,"props":20509,"children":20511},{"className":20510},[],[20512],{"type":48,"value":20513},"`code`",{"type":48,"value":456},{"type":42,"tag":65,"props":20516,"children":20518},{"className":20517},[],[20519],{"type":48,"value":20520},"~~strike~~",{"type":48,"value":456},{"type":42,"tag":65,"props":20523,"children":20525},{"className":20524},[],[20526],{"type":48,"value":20527},"[links](url)",{"type":48,"value":20529},", bare ",{"type":42,"tag":65,"props":20531,"children":20533},{"className":20532},[],[20534],{"type":48,"value":20535},"http(s):\u002F\u002F",{"type":48,"value":20537}," URLs (autolink, trailing punctuation trimmed), ",{"type":42,"tag":65,"props":20539,"children":20541},{"className":20540},[],[20542],{"type":48,"value":18115},{"type":48,"value":20544}," issue refs (opt-in: set ",{"type":42,"tag":65,"props":20546,"children":20548},{"className":20547},[],[20549],{"type":48,"value":20550},"Options.issue_link_base",{"type":48,"value":20552}," and the ref links to base ++ number), bullet + ordered + task lists (task checkboxes are display-only, disabled), fenced code blocks, ",{"type":42,"tag":65,"props":20554,"children":20556},{"className":20555},[],[20557],{"type":48,"value":20558},"> blockquotes",{"type":48,"value":1820},{"type":42,"tag":65,"props":20561,"children":20563},{"className":20562},[],[20564],{"type":48,"value":20565},"---",{"type":48,"value":20567}," rules, GFM pipe tables (header bold, ",{"type":42,"tag":65,"props":20569,"children":20571},{"className":20570},[],[20572],{"type":48,"value":20573},":---",{"type":48,"value":456},{"type":42,"tag":65,"props":20576,"children":20578},{"className":20577},[],[20579],{"type":48,"value":20580},":--:",{"type":48,"value":456},{"type":42,"tag":65,"props":20583,"children":20585},{"className":20584},[],[20586],{"type":48,"value":20587},"---:",{"type":48,"value":20589}," column alignment, inline spans + clickable links inside cells, ",{"type":42,"tag":65,"props":20591,"children":20593},{"className":20592},[],[20594],{"type":48,"value":20595},"\\|",{"type":48,"value":20597}," escapes a pipe in a cell; columns share width equally, and a missing\u002Fmismatched delimiter row degrades the block to paragraphs), ",{"type":42,"tag":65,"props":20599,"children":20601},{"className":20600},[],[20602],{"type":48,"value":20603},"\u003Cdetails>\u003Csummary>",{"type":48,"value":123},{"type":42,"tag":61,"props":20606,"children":20607},{},[20608,20610,20615],{"type":48,"value":20609},"Not in v1 (degrades to plain text, never fails): reference links, raw HTML, footnotes, backslash escapes (except ",{"type":42,"tag":65,"props":20611,"children":20613},{"className":20612},[],[20614],{"type":48,"value":20595},{"type":48,"value":20616}," in table rows).",{"type":42,"tag":61,"props":20618,"children":20619},{},[20620,20625,20627,20632,20634,20639],{"type":42,"tag":65,"props":20621,"children":20623},{"className":20622},[],[20624],{"type":48,"value":18035},{"type":48,"value":20626}," state is elm-style: the CALLER owns the expanded flags. Keep a bounded ",{"type":42,"tag":65,"props":20628,"children":20630},{"className":20629},[],[20631],{"type":48,"value":18083},{"type":48,"value":20633}," in the model, toggle it in ",{"type":42,"tag":65,"props":20635,"children":20637},{"className":20636},[],[20638],{"type":48,"value":483},{"type":48,"value":20640}," on the details message, and pass the slice back in.",{"type":42,"tag":212,"props":20642,"children":20644},{"id":20643},"zig-016-not-015-the-std-idioms-that-changed",[20645],{"type":48,"value":20646},"Zig 0.16, not 0.15: the std idioms that changed",{"type":42,"tag":51,"props":20648,"children":20649},{},[20650],{"type":48,"value":20651},"The toolkit requires Zig 0.16.0, and code written from memory of older Zig fails with \"no member named\" errors on std APIs. The recurring ones, old → current:",{"type":42,"tag":1920,"props":20653,"children":20654},{},[20655,20676],{"type":42,"tag":1924,"props":20656,"children":20657},{},[20658],{"type":42,"tag":1928,"props":20659,"children":20660},{},[20661,20666,20671],{"type":42,"tag":1932,"props":20662,"children":20663},{},[20664],{"type":48,"value":20665},"Compile error says",{"type":42,"tag":1932,"props":20667,"children":20668},{},[20669],{"type":48,"value":20670},"Old idiom",{"type":42,"tag":1932,"props":20672,"children":20673},{},[20674],{"type":48,"value":20675},"Write instead",{"type":42,"tag":1948,"props":20677,"children":20678},{},[20679,20732,20794,20862,20916,20990,21045],{"type":42,"tag":1928,"props":20680,"children":20681},{},[20682,20691,20707],{"type":42,"tag":1955,"props":20683,"children":20684},{},[20685],{"type":42,"tag":65,"props":20686,"children":20688},{"className":20687},[],[20689],{"type":48,"value":20690},"struct 'array_list.Aligned(u8,null)' has no member named 'init'",{"type":42,"tag":1955,"props":20692,"children":20693},{},[20694,20700,20701],{"type":42,"tag":65,"props":20695,"children":20697},{"className":20696},[],[20698],{"type":48,"value":20699},"std.ArrayList(T).init(allocator)",{"type":48,"value":1820},{"type":42,"tag":65,"props":20702,"children":20704},{"className":20703},[],[20705],{"type":48,"value":20706},"list.append(x)",{"type":42,"tag":1955,"props":20708,"children":20709},{},[20710,20716,20717,20723,20724,20730],{"type":42,"tag":65,"props":20711,"children":20713},{"className":20712},[],[20714],{"type":48,"value":20715},"var list: std.ArrayList(T) = .empty;",{"type":48,"value":14463},{"type":42,"tag":65,"props":20718,"children":20720},{"className":20719},[],[20721],{"type":48,"value":20722},"list.append(allocator, x)",{"type":48,"value":1820},{"type":42,"tag":65,"props":20725,"children":20727},{"className":20726},[],[20728],{"type":48,"value":20729},"list.deinit(allocator)",{"type":48,"value":20731}," — the allocator rides every call",{"type":42,"tag":1928,"props":20733,"children":20734},{},[20735,20744,20754],{"type":42,"tag":1955,"props":20736,"children":20737},{},[20738],{"type":42,"tag":65,"props":20739,"children":20741},{"className":20740},[],[20742],{"type":48,"value":20743},"struct 'heap' has no member named 'GeneralPurposeAllocator'",{"type":42,"tag":1955,"props":20745,"children":20746},{},[20747,20749],{"type":48,"value":20748},"hand-built GPA in ",{"type":42,"tag":65,"props":20750,"children":20752},{"className":20751},[],[20753],{"type":48,"value":113},{"type":42,"tag":1955,"props":20755,"children":20756},{},[20757,20763,20765,20771,20772,20778,20779,20785,20787,20792],{"type":42,"tag":65,"props":20758,"children":20760},{"className":20759},[],[20761],{"type":48,"value":20762},"pub fn main(init: std.process.Init) !void",{"type":48,"value":20764}," — use ",{"type":42,"tag":65,"props":20766,"children":20768},{"className":20767},[],[20769],{"type":48,"value":20770},"init.gpa",{"type":48,"value":1820},{"type":42,"tag":65,"props":20773,"children":20775},{"className":20774},[],[20776],{"type":48,"value":20777},"init.arena.allocator()",{"type":48,"value":1820},{"type":42,"tag":65,"props":20780,"children":20782},{"className":20781},[],[20783],{"type":48,"value":20784},"init.io",{"type":48,"value":20786}," (the generated ",{"type":42,"tag":65,"props":20788,"children":20790},{"className":20789},[],[20791],{"type":48,"value":113},{"type":48,"value":20793}," already does)",{"type":42,"tag":1928,"props":20795,"children":20796},{},[20797,20806,20815],{"type":42,"tag":1955,"props":20798,"children":20799},{},[20800],{"type":42,"tag":65,"props":20801,"children":20803},{"className":20802},[],[20804],{"type":48,"value":20805},"struct 'fs' has no member named 'cwd'",{"type":42,"tag":1955,"props":20807,"children":20808},{},[20809],{"type":42,"tag":65,"props":20810,"children":20812},{"className":20811},[],[20813],{"type":48,"value":20814},"std.fs.cwd().openFile(path, .{})",{"type":42,"tag":1955,"props":20816,"children":20817},{},[20818,20824,20826,20832,20834,20840,20842,20847,20849,20854,20855,20860],{"type":42,"tag":65,"props":20819,"children":20821},{"className":20820},[],[20822],{"type":48,"value":20823},"std.Io.Dir.cwd().openFile(io, path, .{})",{"type":48,"value":20825}," — file IO lives on ",{"type":42,"tag":65,"props":20827,"children":20829},{"className":20828},[],[20830],{"type":48,"value":20831},"std.Io.Dir",{"type":48,"value":20833}," and takes an ",{"type":42,"tag":65,"props":20835,"children":20837},{"className":20836},[],[20838],{"type":48,"value":20839},"io",{"type":48,"value":20841},"; in ",{"type":42,"tag":65,"props":20843,"children":20845},{"className":20844},[],[20846],{"type":48,"value":483},{"type":48,"value":20848}," use ",{"type":42,"tag":65,"props":20850,"children":20852},{"className":20851},[],[20853],{"type":48,"value":12370},{"type":48,"value":456},{"type":42,"tag":65,"props":20856,"children":20858},{"className":20857},[],[20859],{"type":48,"value":12363},{"type":48,"value":20861}," instead",{"type":42,"tag":1928,"props":20863,"children":20864},{},[20865,20874,20883],{"type":42,"tag":1955,"props":20866,"children":20867},{},[20868],{"type":42,"tag":65,"props":20869,"children":20871},{"className":20870},[],[20872],{"type":48,"value":20873},"struct 'std' has no member named 'io'",{"type":42,"tag":1955,"props":20875,"children":20876},{},[20877],{"type":42,"tag":65,"props":20878,"children":20880},{"className":20879},[],[20881],{"type":48,"value":20882},"std.io.getStdOut().writer()",{"type":42,"tag":1955,"props":20884,"children":20885},{},[20886,20892,20894,20900,20901,20907,20908,20914],{"type":42,"tag":65,"props":20887,"children":20889},{"className":20888},[],[20890],{"type":48,"value":20891},"std.Io.File.stdout().writer(io, &buffer)",{"type":48,"value":20893}," then print through ",{"type":42,"tag":65,"props":20895,"children":20897},{"className":20896},[],[20898],{"type":48,"value":20899},"&writer.interface",{"type":48,"value":2555},{"type":42,"tag":65,"props":20902,"children":20904},{"className":20903},[],[20905],{"type":48,"value":20906},"flush()",{"type":48,"value":2930},{"type":42,"tag":65,"props":20909,"children":20911},{"className":20910},[],[20912],{"type":48,"value":20913},"std.debug.print",{"type":48,"value":20915}," is unchanged",{"type":42,"tag":1928,"props":20917,"children":20918},{},[20919,20935,20951],{"type":42,"tag":1955,"props":20920,"children":20921},{},[20922,20928,20929],{"type":42,"tag":65,"props":20923,"children":20925},{"className":20924},[],[20926],{"type":48,"value":20927},"struct 'time' has no member named 'sleep'",{"type":48,"value":456},{"type":42,"tag":65,"props":20930,"children":20932},{"className":20931},[],[20933],{"type":48,"value":20934},"'milliTimestamp'",{"type":42,"tag":1955,"props":20936,"children":20937},{},[20938,20944,20945],{"type":42,"tag":65,"props":20939,"children":20941},{"className":20940},[],[20942],{"type":48,"value":20943},"std.time.sleep(ns)",{"type":48,"value":1820},{"type":42,"tag":65,"props":20946,"children":20948},{"className":20947},[],[20949],{"type":48,"value":20950},"std.time.milliTimestamp()",{"type":42,"tag":1955,"props":20952,"children":20953},{},[20954,20956,20961,20963,20969,20970,20976,20977,20982,20984],{"type":48,"value":20955},"clocks live on ",{"type":42,"tag":65,"props":20957,"children":20959},{"className":20958},[],[20960],{"type":48,"value":12378},{"type":48,"value":20962}," — in app code use ",{"type":42,"tag":65,"props":20964,"children":20966},{"className":20965},[],[20967],{"type":48,"value":20968},"fx.wallMs()",{"type":48,"value":7747},{"type":42,"tag":65,"props":20971,"children":20973},{"className":20972},[],[20974],{"type":48,"value":20975},"native_sdk.nowMs()",{"type":48,"value":7747},{"type":42,"tag":65,"props":20978,"children":20980},{"className":20979},[],[20981],{"type":48,"value":12980},{"type":48,"value":20983}," (see Time above), in tests ",{"type":42,"tag":65,"props":20985,"children":20987},{"className":20986},[],[20988],{"type":48,"value":20989},"std.Io.sleep(std.testing.io, ...)",{"type":42,"tag":1928,"props":20991,"children":20992},{},[20993,21002,21013],{"type":42,"tag":1955,"props":20994,"children":20995},{},[20996],{"type":42,"tag":65,"props":20997,"children":20999},{"className":20998},[],[21000],{"type":48,"value":21001},"invalid format string 's' for type '...'",{"type":42,"tag":1955,"props":21003,"children":21004},{},[21005,21011],{"type":42,"tag":65,"props":21006,"children":21008},{"className":21007},[],[21009],{"type":48,"value":21010},"{s}",{"type":48,"value":21012}," on an enum",{"type":42,"tag":1955,"props":21014,"children":21015},{},[21016,21022,21024,21030,21032,21038,21040],{"type":42,"tag":65,"props":21017,"children":21019},{"className":21018},[],[21020],{"type":48,"value":21021},"{t}",{"type":48,"value":21023}," prints the tag name; custom ",{"type":42,"tag":65,"props":21025,"children":21027},{"className":21026},[],[21028],{"type":48,"value":21029},"format",{"type":48,"value":21031}," methods take ",{"type":42,"tag":65,"props":21033,"children":21035},{"className":21034},[],[21036],{"type":48,"value":21037},"(self, writer: *std.Io.Writer)",{"type":48,"value":21039}," and print with ",{"type":42,"tag":65,"props":21041,"children":21043},{"className":21042},[],[21044],{"type":48,"value":9721},{"type":42,"tag":1928,"props":21046,"children":21047},{},[21048,21057,21073],{"type":42,"tag":1955,"props":21049,"children":21050},{},[21051],{"type":42,"tag":65,"props":21052,"children":21054},{"className":21053},[],[21055],{"type":48,"value":21056},"struct 'process.Child' has no member named 'init'",{"type":42,"tag":1955,"props":21058,"children":21059},{},[21060,21066,21067],{"type":42,"tag":65,"props":21061,"children":21063},{"className":21062},[],[21064],{"type":48,"value":21065},"Child.init",{"type":48,"value":3952},{"type":42,"tag":65,"props":21068,"children":21070},{"className":21069},[],[21071],{"type":48,"value":21072},"child.spawn()",{"type":42,"tag":1955,"props":21074,"children":21075},{},[21076,21081,21083,21089,21090,21096],{"type":42,"tag":65,"props":21077,"children":21079},{"className":21078},[],[21080],{"type":48,"value":11360},{"type":48,"value":21082}," in apps; ",{"type":42,"tag":65,"props":21084,"children":21086},{"className":21085},[],[21087],{"type":48,"value":21088},"std.process.spawn(io, .{ .argv = ... })",{"type":48,"value":3952},{"type":42,"tag":65,"props":21091,"children":21093},{"className":21092},[],[21094],{"type":48,"value":21095},"child.wait(io)",{"type":48,"value":21097}," in tools",{"type":42,"tag":51,"props":21099,"children":21100},{},[21101,21103,21108,21110,21116,21118,21124,21126,21132],{"type":48,"value":21102},"Tests that touch files or clocks get their ",{"type":42,"tag":65,"props":21104,"children":21106},{"className":21105},[],[21107],{"type":48,"value":12378},{"type":48,"value":21109}," from ",{"type":42,"tag":65,"props":21111,"children":21113},{"className":21112},[],[21114],{"type":48,"value":21115},"std.testing.io",{"type":48,"value":21117},". The full catalog — env\u002Fargs, sockets, readers, ",{"type":42,"tag":65,"props":21119,"children":21121},{"className":21120},[],[21122],{"type":48,"value":21123},"build.zig",{"type":48,"value":21125}," shapes, each with the SDK's live reference file — is its own skill: ",{"type":42,"tag":65,"props":21127,"children":21129},{"className":21128},[],[21130],{"type":48,"value":21131},"native skills get zig",{"type":48,"value":123},{"type":42,"tag":212,"props":21134,"children":21136},{"id":21135},"validate-without-building",[21137],{"type":48,"value":21138},"Validate without building",{"type":42,"tag":51,"props":21140,"children":21141},{},[21142,21148,21150,21156,21158,21164,21166,21171,21173,21179,21180,21186,21188,21193,21195,21201],{"type":42,"tag":65,"props":21143,"children":21145},{"className":21144},[],[21146],{"type":48,"value":21147},"native markup check src\u002Fview.native",{"type":48,"value":21149}," — instant grammar\u002Fstructure validation with ",{"type":42,"tag":65,"props":21151,"children":21153},{"className":21152},[],[21154],{"type":48,"value":21155},"file:line:column",{"type":48,"value":21157}," errors, including the font-coverage tofu guard: literal text with a codepoint outside the bundled face (⌘, ✓, ⑂, dingbats, CJK) is a teaching error naming the character, because it renders as a tofu box on the reference\u002Fscreenshot and mobile paths — register a font that covers it (",{"type":42,"tag":65,"props":21159,"children":21161},{"className":21160},[],[21162],{"type":48,"value":21163},"UiApp.Options.fonts",{"type":48,"value":21165},") and bind the text from the model (the guard skips ",{"type":42,"tag":65,"props":21167,"children":21169},{"className":21168},[],[21170],{"type":48,"value":2781},{"type":48,"value":21172},"), or use a vector icon (",{"type":42,"tag":65,"props":21174,"children":21176},{"className":21175},[],[21177],{"type":48,"value":21178},"icon=",{"type":48,"value":7747},{"type":42,"tag":65,"props":21181,"children":21183},{"className":21182},[],[21184],{"type":48,"value":21185},"\u003Cicon name>",{"type":48,"value":21187},") or plain words. Dynamic strings get the same lesson as a Debug-build ",{"type":42,"tag":65,"props":21189,"children":21191},{"className":21190},[],[21192],{"type":48,"value":5149},{"type":48,"value":21194}," diagnostic when the view builds. The accessibility lint rides the same pass: unnamed interactive controls and role misuse are errors, unnamed images and redundant labels are warnings (",{"type":42,"tag":65,"props":21196,"children":21198},{"className":21197},[],[21199],{"type":48,"value":21200},"--strict",{"type":48,"value":21202}," promotes).",{"type":42,"tag":51,"props":21204,"children":21205},{},[21206,21208,21213,21215,21221,21223,21229,21230,21235,21236,21241,21243,21248,21250,21256,21258,21263,21265,21271,21273,21278],{"type":48,"value":21207},"The model side checks at check time too: the model-contract step (refreshed by ",{"type":42,"tag":65,"props":21209,"children":21211},{"className":21210},[],[21212],{"type":48,"value":9604},{"type":48,"value":21214},", or run directly as ",{"type":42,"tag":65,"props":21216,"children":21218},{"className":21217},[],[21219],{"type":48,"value":21220},"zig build model-contract",{"type":48,"value":21222}," in an app that owns its build) reflects Model\u002FMsg into ",{"type":42,"tag":65,"props":21224,"children":21226},{"className":21225},[],[21227],{"type":48,"value":21228},"zig-out\u002Fmodel-contract.zon",{"type":48,"value":2871},{"type":42,"tag":65,"props":21231,"children":21233},{"className":21232},[],[21234],{"type":48,"value":3347},{"type":48,"value":6260},{"type":42,"tag":65,"props":21237,"children":21239},{"className":21238},[],[21240],{"type":48,"value":10663},{"type":48,"value":21242}," run in the app directory) then verifies every binding path, iterable, ",{"type":42,"tag":65,"props":21244,"children":21246},{"className":21245},[],[21247],{"type":48,"value":3595},{"type":48,"value":21249}," field, message tag, payload type, and expression type against the app's real surface — did-you-mean over your actual field names, and type errors naming the field's Zig type. It also WARNS on model state and Msg tags no view uses; opt update-only names out with ",{"type":42,"tag":65,"props":21251,"children":21253},{"className":21252},[],[21254],{"type":48,"value":21255},"pub const view_unbound = .{ \"next_id\" };",{"type":48,"value":21257}," on Model or Msg (",{"type":42,"tag":65,"props":21259,"children":21261},{"className":21260},[],[21262],{"type":48,"value":21200},{"type":48,"value":21264}," turns the warnings into failures) — state consumed only by a Zig-BUILT view needs ",{"type":42,"tag":65,"props":21266,"children":21268},{"className":21267},[],[21269],{"type":48,"value":21270},"view_unbound",{"type":48,"value":21272}," too, because the markup checker cannot see Zig view reads. A stale artifact degrades to grammar-only checking with a loud note (\"model contract: not yet built - bindings checked structurally only; run ",{"type":42,"tag":65,"props":21274,"children":21276},{"className":21275},[],[21277],{"type":48,"value":9604},{"type":48,"value":21279}," to enable typed checks\"); binding paths and message tags are always re-enforced when the app builds (and on hot reload).",{"type":42,"tag":212,"props":21281,"children":21283},{"id":21282},"testing-pattern",[21284],{"type":48,"value":21285},"Testing pattern",{"type":42,"tag":51,"props":21287,"children":21288},{},[21289],{"type":48,"value":21290},"Unit tests exercise the real dispatch path — no GUI needed:",{"type":42,"tag":219,"props":21292,"children":21294},{"className":221,"code":21293,"language":18,"meta":223,"style":223},"var view = try canvas.MarkupView(Model, Msg).init(arena, main.habits_markup);\nvar ui = canvas.Ui(Msg).init(arena);\nconst tree = try ui.finalize(try view.build(&ui, &model));\nconst button = findByText(tree.root, .button, \"Done today\").?;   \u002F\u002F walk tree.root\nmain.update(&model, tree.msgForPointer(button.id, .up).?);        \u002F\u002F dispatch exactly like the runtime\n\u002F\u002F rebuild and assert: text updated, widget ids stable\n",[21295],{"type":42,"tag":65,"props":21296,"children":21297},{"__ignoreMap":223},[21298,21306,21314,21322,21330,21338],{"type":42,"tag":229,"props":21299,"children":21300},{"class":231,"line":232},[21301],{"type":42,"tag":229,"props":21302,"children":21303},{},[21304],{"type":48,"value":21305},"var view = try canvas.MarkupView(Model, Msg).init(arena, main.habits_markup);\n",{"type":42,"tag":229,"props":21307,"children":21308},{"class":231,"line":241},[21309],{"type":42,"tag":229,"props":21310,"children":21311},{},[21312],{"type":48,"value":21313},"var ui = canvas.Ui(Msg).init(arena);\n",{"type":42,"tag":229,"props":21315,"children":21316},{"class":231,"line":251},[21317],{"type":42,"tag":229,"props":21318,"children":21319},{},[21320],{"type":48,"value":21321},"const tree = try ui.finalize(try view.build(&ui, &model));\n",{"type":42,"tag":229,"props":21323,"children":21324},{"class":231,"line":260},[21325],{"type":42,"tag":229,"props":21326,"children":21327},{},[21328],{"type":48,"value":21329},"const button = findByText(tree.root, .button, \"Done today\").?;   \u002F\u002F walk tree.root\n",{"type":42,"tag":229,"props":21331,"children":21332},{"class":231,"line":269},[21333],{"type":42,"tag":229,"props":21334,"children":21335},{},[21336],{"type":48,"value":21337},"main.update(&model, tree.msgForPointer(button.id, .up).?);        \u002F\u002F dispatch exactly like the runtime\n",{"type":42,"tag":229,"props":21339,"children":21340},{"class":231,"line":278},[21341],{"type":42,"tag":229,"props":21342,"children":21343},{},[21344],{"type":48,"value":21345},"\u002F\u002F rebuild and assert: text updated, widget ids stable\n",{"type":42,"tag":51,"props":21347,"children":21348},{},[21349,21351,21357,21359,21363,21365,21371,21373,21379],{"type":48,"value":21350},"Two ",{"type":42,"tag":65,"props":21352,"children":21354},{"className":21353},[],[21355],{"type":48,"value":21356},"msgForPointer",{"type":48,"value":21358}," traps: a ",{"type":42,"tag":513,"props":21360,"children":21361},{},[21362],{"type":48,"value":1206},{"type":48,"value":21364}," control yields ",{"type":42,"tag":65,"props":21366,"children":21368},{"className":21367},[],[21369],{"type":48,"value":21370},"null",{"type":48,"value":21372}," (assert ",{"type":42,"tag":65,"props":21374,"children":21376},{"className":21375},[],[21377],{"type":48,"value":21378},"== null",{"type":48,"value":21380}," rather than unwrapping when testing disabled states), and the tree is a snapshot — after each dispatch, rebuild the view before pressing anything again.",{"type":42,"tag":51,"props":21382,"children":21383},{},[21384,21386,21392,21394,21400,21401,21407,21409,21415,21417,21423],{"type":48,"value":21385},"One widget-field trap: there is no ",{"type":42,"tag":65,"props":21387,"children":21389},{"className":21388},[],[21390],{"type":48,"value":21391},"Widget.enabled",{"type":48,"value":21393}," — enabled-ness is spelled ",{"type":42,"tag":65,"props":21395,"children":21397},{"className":21396},[],[21398],{"type":48,"value":21399},"widget.state.disabled",{"type":48,"value":20302},{"type":42,"tag":65,"props":21402,"children":21404},{"className":21403},[],[21405],{"type":48,"value":21406},"bool",{"type":48,"value":21408},", disabled-positive), so a test asserts ",{"type":42,"tag":65,"props":21410,"children":21412},{"className":21411},[],[21413],{"type":48,"value":21414},"try testing.expect(!button.state.disabled);",{"type":48,"value":21416}," for an enabled control and ",{"type":42,"tag":65,"props":21418,"children":21420},{"className":21419},[],[21421],{"type":48,"value":21422},"try testing.expect(save.state.disabled);",{"type":48,"value":21424}," for a disabled one.",{"type":42,"tag":51,"props":21426,"children":21427},{},[21428,21433,21435,21441,21442,21448,21450,21456,21458,21463,21465,21471,21473,21478,21479,21485,21486,21491,21492,21498,21500,21506,21508,21514,21516,21521,21523,21529,21531,21537,21538,21544,21546,21551,21553,21558,21559,21564,21566,21571,21572,21578,21580,21586],{"type":42,"tag":65,"props":21429,"children":21431},{"className":21430},[],[21432],{"type":48,"value":21356},{"type":48,"value":21434}," has a sibling for every handler channel — use the one matching the interaction under test, all on the finalized ",{"type":42,"tag":65,"props":21436,"children":21438},{"className":21437},[],[21439],{"type":48,"value":21440},"Tree",{"type":48,"value":4178},{"type":42,"tag":65,"props":21443,"children":21445},{"className":21444},[],[21446],{"type":48,"value":21447},"msgForKeyboard(id, keyboard_event)",{"type":48,"value":21449}," (activation keys, slider steps, enter-to-submit, text edits), ",{"type":42,"tag":65,"props":21451,"children":21453},{"className":21452},[],[21454],{"type":48,"value":21455},"msgForResize(id, fraction)",{"type":48,"value":21457}," (the split-divider round-trip: dispatch the fraction, assert the model stored it, rebuild, assert the ",{"type":42,"tag":65,"props":21459,"children":21461},{"className":21460},[],[21462],{"type":48,"value":2479},{"type":48,"value":21464}," echo), ",{"type":42,"tag":65,"props":21466,"children":21468},{"className":21467},[],[21469],{"type":48,"value":21470},"msgForDismiss(id)",{"type":48,"value":21472}," (an anchored surface's ",{"type":42,"tag":65,"props":21474,"children":21476},{"className":21475},[],[21477],{"type":48,"value":2235},{"type":48,"value":4517},{"type":42,"tag":65,"props":21480,"children":21482},{"className":21481},[],[21483],{"type":48,"value":21484},"msgForHold(id)",{"type":48,"value":2620},{"type":42,"tag":65,"props":21487,"children":21489},{"className":21488},[],[21490],{"type":48,"value":1183},{"type":48,"value":4517},{"type":42,"tag":65,"props":21493,"children":21495},{"className":21494},[],[21496],{"type":48,"value":21497},"msgForTextEdit(id, edit)",{"type":48,"value":21499}," (text entry), ",{"type":42,"tag":65,"props":21501,"children":21503},{"className":21502},[],[21504],{"type":48,"value":21505},"msgForValue(id, value)",{"type":48,"value":21507}," (BUILDER views only — it fires the ",{"type":42,"tag":65,"props":21509,"children":21511},{"className":21510},[],[21512],{"type":48,"value":21513},"on_value",{"type":48,"value":21515}," constructor, so it returns null for a markup slider; a markup slider binds a plain ",{"type":42,"tag":65,"props":21517,"children":21519},{"className":21518},[],[21520],{"type":48,"value":3043},{"type":48,"value":21522},", so assert its dispatch with ",{"type":42,"tag":65,"props":21524,"children":21526},{"className":21525},[],[21527],{"type":48,"value":21528},"msgFor(id, .change)",{"type":48,"value":21530}," — the accessibility set-value intent falls back to the same handler), ",{"type":42,"tag":65,"props":21532,"children":21534},{"className":21533},[],[21535],{"type":48,"value":21536},"msgForScroll(id, state)",{"type":48,"value":2871},{"type":42,"tag":65,"props":21539,"children":21541},{"className":21540},[],[21542],{"type":48,"value":21543},"msgForContextMenu(id, item_index)",{"type":48,"value":21545},". For tree keyboard NAVIGATION there is nothing app-side to unit test: Up\u002FDown\u002FLeft\u002FRight\u002FHome\u002FEnd run engine-side over ",{"type":42,"tag":65,"props":21547,"children":21549},{"className":21548},[],[21550],{"type":48,"value":2538},{"type":48,"value":21552}," rows and dispatch the landed row's ",{"type":42,"tag":65,"props":21554,"children":21556},{"className":21555},[],[21557],{"type":48,"value":1176},{"type":48,"value":456},{"type":42,"tag":65,"props":21560,"children":21562},{"className":21561},[],[21563],{"type":48,"value":2277},{"type":48,"value":21565}," — assert those Msgs (via ",{"type":42,"tag":65,"props":21567,"children":21569},{"className":21568},[],[21570],{"type":48,"value":21356},{"type":48,"value":456},{"type":42,"tag":65,"props":21573,"children":21575},{"className":21574},[],[21576],{"type":48,"value":21577},"msgFor(id, .toggle)",{"type":48,"value":21579},") and the model transitions; the keymap itself is runtime behavior (drive it live with ",{"type":42,"tag":65,"props":21581,"children":21583},{"className":21582},[],[21584],{"type":48,"value":21585},"native automate widget-key",{"type":48,"value":7386},{"type":42,"tag":51,"props":21588,"children":21589},{},[21590,21592,21598],{"type":48,"value":21591},"Runtime-integration tests use ",{"type":42,"tag":65,"props":21593,"children":21595},{"className":21594},[],[21596],{"type":48,"value":21597},"native_sdk.TestHarness()",{"type":48,"value":21599}," on the null platform; heap-allocate both the harness and the app struct (they are multi-megabyte; stack allocation crashes).",{"type":42,"tag":212,"props":21601,"children":21603},{"id":21602},"verify-live-through-the-automation-harness",[21604],{"type":48,"value":21605},"Verify live through the automation harness",{"type":42,"tag":219,"props":21607,"children":21611},{"className":21608,"code":21609,"language":21610,"meta":223,"style":223},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","native build -Dautomation=true\n.\u002Fzig-out\u002Fbin\u002F\u003Capp> &   # run from the example directory\nnative automate wait                     # blocks until ready=true\ncat .zig-cache\u002Fnative-sdk-automation\u002Fsnapshot.txt   # widgets with ids, roles, names, bounds, state\nnative automate widget-click \u003Ccanvas-label> \u003Cid>   # id is the bare number (snapshot prints #id)\nnative automate widget-hold \u003Ccanvas-label> \u003Cid>    # press-and-hold: drives on_hold via the real timer path\nnative automate widget-context-press \u003Ccanvas-label> \u003Cid>   # right-click: context menu, or on_hold when none\n","bash",[21612],{"type":42,"tag":65,"props":21613,"children":21614},{"__ignoreMap":223},[21615,21633,21656,21678,21696,21753,21806],{"type":42,"tag":229,"props":21616,"children":21617},{"class":231,"line":232},[21618,21623,21628],{"type":42,"tag":229,"props":21619,"children":21621},{"style":21620},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[21622],{"type":48,"value":14},{"type":42,"tag":229,"props":21624,"children":21625},{"style":1288},[21626],{"type":48,"value":21627}," build",{"type":42,"tag":229,"props":21629,"children":21630},{"style":1288},[21631],{"type":48,"value":21632}," -Dautomation=true\n",{"type":42,"tag":229,"props":21634,"children":21635},{"class":231,"line":241},[21636,21641,21646,21651],{"type":42,"tag":229,"props":21637,"children":21638},{"style":21620},[21639],{"type":48,"value":21640},".\u002Fzig-out\u002Fbin\u002F",{"type":42,"tag":229,"props":21642,"children":21643},{"style":1364},[21644],{"type":48,"value":21645},"\u003Capp> ",{"type":42,"tag":229,"props":21647,"children":21648},{"style":1260},[21649],{"type":48,"value":21650},"&",{"type":42,"tag":229,"props":21652,"children":21653},{"style":15826},[21654],{"type":48,"value":21655},"   # run from the example directory\n",{"type":42,"tag":229,"props":21657,"children":21658},{"class":231,"line":251},[21659,21663,21668,21673],{"type":42,"tag":229,"props":21660,"children":21661},{"style":21620},[21662],{"type":48,"value":14},{"type":42,"tag":229,"props":21664,"children":21665},{"style":1288},[21666],{"type":48,"value":21667}," automate",{"type":42,"tag":229,"props":21669,"children":21670},{"style":1288},[21671],{"type":48,"value":21672}," wait",{"type":42,"tag":229,"props":21674,"children":21675},{"style":15826},[21676],{"type":48,"value":21677},"                     # blocks until ready=true\n",{"type":42,"tag":229,"props":21679,"children":21680},{"class":231,"line":260},[21681,21686,21691],{"type":42,"tag":229,"props":21682,"children":21683},{"style":21620},[21684],{"type":48,"value":21685},"cat",{"type":42,"tag":229,"props":21687,"children":21688},{"style":1288},[21689],{"type":48,"value":21690}," .zig-cache\u002Fnative-sdk-automation\u002Fsnapshot.txt",{"type":42,"tag":229,"props":21692,"children":21693},{"style":15826},[21694],{"type":48,"value":21695},"   # widgets with ids, roles, names, bounds, state\n",{"type":42,"tag":229,"props":21697,"children":21698},{"class":231,"line":269},[21699,21703,21707,21712,21716,21721,21726,21730,21734,21739,21744,21748],{"type":42,"tag":229,"props":21700,"children":21701},{"style":21620},[21702],{"type":48,"value":14},{"type":42,"tag":229,"props":21704,"children":21705},{"style":1288},[21706],{"type":48,"value":21667},{"type":42,"tag":229,"props":21708,"children":21709},{"style":1288},[21710],{"type":48,"value":21711}," widget-click",{"type":42,"tag":229,"props":21713,"children":21714},{"style":1260},[21715],{"type":48,"value":16217},{"type":42,"tag":229,"props":21717,"children":21718},{"style":1288},[21719],{"type":48,"value":21720},"canvas-labe",{"type":42,"tag":229,"props":21722,"children":21723},{"style":1364},[21724],{"type":48,"value":21725},"l",{"type":42,"tag":229,"props":21727,"children":21728},{"style":1260},[21729],{"type":48,"value":1361},{"type":42,"tag":229,"props":21731,"children":21732},{"style":1260},[21733],{"type":48,"value":16217},{"type":42,"tag":229,"props":21735,"children":21736},{"style":1288},[21737],{"type":48,"value":21738},"i",{"type":42,"tag":229,"props":21740,"children":21741},{"style":1364},[21742],{"type":48,"value":21743},"d",{"type":42,"tag":229,"props":21745,"children":21746},{"style":1260},[21747],{"type":48,"value":1361},{"type":42,"tag":229,"props":21749,"children":21750},{"style":15826},[21751],{"type":48,"value":21752},"   # id is the bare number (snapshot prints #id)\n",{"type":42,"tag":229,"props":21754,"children":21755},{"class":231,"line":278},[21756,21760,21764,21769,21773,21777,21781,21785,21789,21793,21797,21801],{"type":42,"tag":229,"props":21757,"children":21758},{"style":21620},[21759],{"type":48,"value":14},{"type":42,"tag":229,"props":21761,"children":21762},{"style":1288},[21763],{"type":48,"value":21667},{"type":42,"tag":229,"props":21765,"children":21766},{"style":1288},[21767],{"type":48,"value":21768}," widget-hold",{"type":42,"tag":229,"props":21770,"children":21771},{"style":1260},[21772],{"type":48,"value":16217},{"type":42,"tag":229,"props":21774,"children":21775},{"style":1288},[21776],{"type":48,"value":21720},{"type":42,"tag":229,"props":21778,"children":21779},{"style":1364},[21780],{"type":48,"value":21725},{"type":42,"tag":229,"props":21782,"children":21783},{"style":1260},[21784],{"type":48,"value":1361},{"type":42,"tag":229,"props":21786,"children":21787},{"style":1260},[21788],{"type":48,"value":16217},{"type":42,"tag":229,"props":21790,"children":21791},{"style":1288},[21792],{"type":48,"value":21738},{"type":42,"tag":229,"props":21794,"children":21795},{"style":1364},[21796],{"type":48,"value":21743},{"type":42,"tag":229,"props":21798,"children":21799},{"style":1260},[21800],{"type":48,"value":1361},{"type":42,"tag":229,"props":21802,"children":21803},{"style":15826},[21804],{"type":48,"value":21805},"    # press-and-hold: drives on_hold via the real timer path\n",{"type":42,"tag":229,"props":21807,"children":21808},{"class":231,"line":287},[21809,21813,21817,21822,21826,21830,21834,21838,21842,21846,21850,21854],{"type":42,"tag":229,"props":21810,"children":21811},{"style":21620},[21812],{"type":48,"value":14},{"type":42,"tag":229,"props":21814,"children":21815},{"style":1288},[21816],{"type":48,"value":21667},{"type":42,"tag":229,"props":21818,"children":21819},{"style":1288},[21820],{"type":48,"value":21821}," widget-context-press",{"type":42,"tag":229,"props":21823,"children":21824},{"style":1260},[21825],{"type":48,"value":16217},{"type":42,"tag":229,"props":21827,"children":21828},{"style":1288},[21829],{"type":48,"value":21720},{"type":42,"tag":229,"props":21831,"children":21832},{"style":1364},[21833],{"type":48,"value":21725},{"type":42,"tag":229,"props":21835,"children":21836},{"style":1260},[21837],{"type":48,"value":1361},{"type":42,"tag":229,"props":21839,"children":21840},{"style":1260},[21841],{"type":48,"value":16217},{"type":42,"tag":229,"props":21843,"children":21844},{"style":1288},[21845],{"type":48,"value":21738},{"type":42,"tag":229,"props":21847,"children":21848},{"style":1364},[21849],{"type":48,"value":21743},{"type":42,"tag":229,"props":21851,"children":21852},{"style":1260},[21853],{"type":48,"value":1361},{"type":42,"tag":229,"props":21855,"children":21856},{"style":15826},[21857],{"type":48,"value":21858},"   # right-click: context menu, or on_hold when none\n",{"type":42,"tag":51,"props":21860,"children":21861},{},[21862],{"type":48,"value":21863},"Snapshots expose the same structural widget ids your tests see, so live assertions are greps: click by id, re-read the snapshot, and check names\u002Fvalues\u002Fcounts changed. Widget ids are stable across rebuilds, reorders, and hot reloads — asserting an id stayed constant while its bounds or state changed is the standard way to prove keyed identity.",{"type":42,"tag":51,"props":21865,"children":21866},{},[21867,21869,21874,21876,21882,21884,21890,21892,21898],{"type":48,"value":21868},"For scripted checks (and the CI workflow ",{"type":42,"tag":65,"props":21870,"children":21872},{"className":21871},[],[21873],{"type":48,"value":171},{"type":48,"value":21875}," scaffolds), replace grep-and-sleep with ",{"type":42,"tag":65,"props":21877,"children":21879},{"className":21878},[],[21880],{"type":48,"value":21881},"native automate assert",{"type":48,"value":21883},": each argument is a regex that must match the snapshot, polled up to ",{"type":42,"tag":65,"props":21885,"children":21887},{"className":21886},[],[21888],{"type":48,"value":21889},"--timeout-ms",{"type":48,"value":21891}," (default 30000), with ",{"type":42,"tag":65,"props":21893,"children":21895},{"className":21894},[],[21896],{"type":48,"value":21897},"--absent",{"type":48,"value":21899}," inverting the check. Failure names the missing patterns and prints the snapshot tail.",{"type":42,"tag":219,"props":21901,"children":21903},{"className":21608,"code":21902,"language":21610,"meta":223,"style":223},"native automate assert 'gpu_nonblank=true' 'role=button name=\"Reset\"' 'count: 0'\nnative automate assert --absent 'error event='\n",[21904],{"type":42,"tag":65,"props":21905,"children":21906},{"__ignoreMap":223},[21907,21965],{"type":42,"tag":229,"props":21908,"children":21909},{"class":231,"line":232},[21910,21914,21918,21923,21928,21933,21938,21942,21947,21951,21955,21960],{"type":42,"tag":229,"props":21911,"children":21912},{"style":21620},[21913],{"type":48,"value":14},{"type":42,"tag":229,"props":21915,"children":21916},{"style":1288},[21917],{"type":48,"value":21667},{"type":42,"tag":229,"props":21919,"children":21920},{"style":1288},[21921],{"type":48,"value":21922}," assert",{"type":42,"tag":229,"props":21924,"children":21925},{"style":1260},[21926],{"type":48,"value":21927}," '",{"type":42,"tag":229,"props":21929,"children":21930},{"style":1288},[21931],{"type":48,"value":21932},"gpu_nonblank=true",{"type":42,"tag":229,"props":21934,"children":21935},{"style":1260},[21936],{"type":48,"value":21937},"'",{"type":42,"tag":229,"props":21939,"children":21940},{"style":1260},[21941],{"type":48,"value":21927},{"type":42,"tag":229,"props":21943,"children":21944},{"style":1288},[21945],{"type":48,"value":21946},"role=button name=\"Reset\"",{"type":42,"tag":229,"props":21948,"children":21949},{"style":1260},[21950],{"type":48,"value":21937},{"type":42,"tag":229,"props":21952,"children":21953},{"style":1260},[21954],{"type":48,"value":21927},{"type":42,"tag":229,"props":21956,"children":21957},{"style":1288},[21958],{"type":48,"value":21959},"count: 0",{"type":42,"tag":229,"props":21961,"children":21962},{"style":1260},[21963],{"type":48,"value":21964},"'\n",{"type":42,"tag":229,"props":21966,"children":21967},{"class":231,"line":241},[21968,21972,21976,21980,21985,21989,21994],{"type":42,"tag":229,"props":21969,"children":21970},{"style":21620},[21971],{"type":48,"value":14},{"type":42,"tag":229,"props":21973,"children":21974},{"style":1288},[21975],{"type":48,"value":21667},{"type":42,"tag":229,"props":21977,"children":21978},{"style":1288},[21979],{"type":48,"value":21922},{"type":42,"tag":229,"props":21981,"children":21982},{"style":1288},[21983],{"type":48,"value":21984}," --absent",{"type":42,"tag":229,"props":21986,"children":21987},{"style":1260},[21988],{"type":48,"value":21927},{"type":42,"tag":229,"props":21990,"children":21991},{"style":1288},[21992],{"type":48,"value":21993},"error event=",{"type":42,"tag":229,"props":21995,"children":21996},{"style":1260},[21997],{"type":48,"value":21964},{"type":42,"tag":8871,"props":21999,"children":22000},{},[22001],{"type":48,"value":22002},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":22004,"total":269},[22005,22020,22031,22038,22051],{"slug":22006,"name":22006,"fn":22007,"description":22008,"org":22009,"tags":22010,"stars":25,"repoUrl":26,"updatedAt":22019},"automation","automate and verify Native SDK applications","Automation and verification guide for running Native SDK apps. Use when the user asks to test a running app, inspect runtime state, list windows, wait for readiness, drive widgets, take deterministic screenshots, send bridge commands, debug why automation is not connected, create smoke tests, or verify a Native SDK example in a GUI-capable session.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22011,22013,22016],{"name":22012,"slug":22006,"type":15},"Automation",{"name":22014,"slug":22015,"type":15},"Mobile","mobile",{"name":22017,"slug":22018,"type":15},"Testing","testing","2026-07-20T05:57:16.544049",{"slug":19067,"name":19067,"fn":22021,"description":22022,"org":22023,"tags":22024,"stars":25,"repoUrl":26,"updatedAt":22030},"develop native desktop applications with Zig","Discovery skill for the Native SDK, the complete toolkit for building native desktop applications - views are declarative Native markup (.native), logic is plain Zig, and the toolkit's own engine renders every pixel, with WebView surfaces as the optional web-content path. Use when the user asks what the Native SDK is, how to build a Native SDK app, author native UI, scaffold an app, configure app.zon, add bridge commands, embed web content, package an app, test a running app, or automate a Native SDK app.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22025,22026,22029],{"name":20,"slug":21,"type":15},{"name":22027,"slug":22028,"type":15},"SDK","sdk",{"name":17,"slug":18,"type":15},"2026-07-17T06:08:49.453123",{"slug":4,"name":4,"fn":5,"description":6,"org":22032,"tags":22033,"stars":25,"repoUrl":26,"updatedAt":27},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22034,22035,22036,22037],{"name":20,"slug":21,"type":15},{"name":13,"slug":14,"type":15},{"name":23,"slug":24,"type":15},{"name":17,"slug":18,"type":15},{"slug":22039,"name":22039,"fn":22040,"description":22041,"org":22042,"tags":22043,"stars":25,"repoUrl":26,"updatedAt":22050},"ts-core","author TypeScript application cores","Authoring guide for TypeScript app cores - Model, Msg, update, and the pure functions they call, written in the closed app-core subset and compiled ahead-of-time to arena-backed Zig by the @native-sdk\u002Fcore transpiler. Use when writing or modifying a src\u002Fcore.ts app core, fixing subset checker errors (NS1001-NS1060), or deciding how to express state, messages, text (bytes and the byte-text string methods), text input, continuous controls (sliders, scroll), effects (Cmd), subscriptions (Sub), the host-event wiring channels (frameMsg, keyMsg, appearanceMsg, chromeMsg, envMsgs, app.zon assets), derived values, the view_unbound lint opt-out, local mutation of owned arrays, or how to split a core into modules under src\u002F (relative imports, namespace imports, @native-sdk\u002Fcore\u002Ftext, @native-sdk\u002Fcore\u002Fevents).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22044,22047],{"name":22045,"slug":22046,"type":15},"Engineering","engineering",{"name":22048,"slug":22049,"type":15},"TypeScript","typescript","2026-07-23T05:42:16.152774",{"slug":18,"name":18,"fn":22052,"description":22053,"org":22054,"tags":22055,"stars":25,"repoUrl":26,"updatedAt":22060},"debug Zig 0.16 compilation errors","Zig 0.16 idioms for Native SDK code, indexed by compile error. Load when `zig build` fails on std APIs with errors like \"struct 'fs' has no member named 'cwd'\", \"struct 'array_list.Aligned(u8,null)' has no member named 'init'\", \"struct 'std' has no member named 'io'\", \"no member named 'GeneralPurposeAllocator'\", \"no member named 'getEnvMap'\", or \"invalid format string\" - the signature of code written for Zig 0.15 or earlier. Covers main(std.process.Init), std.Io file IO and writers, ArrayList, process spawning, environment, clocks and sleep, sockets, custom formatting, and build.zig module shapes, each as this SDK writes them.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22056,22059],{"name":22057,"slug":22058,"type":15},"Debugging","debugging",{"name":22045,"slug":22046,"type":15},"2026-07-17T06:08:45.630143",{"items":22062,"total":22224},[22063,22079,22091,22103,22118,22131,22141,22154,22167,22180,22192,22209],{"slug":22064,"name":22064,"fn":22065,"description":22066,"org":22067,"tags":22068,"stars":22076,"repoUrl":22077,"updatedAt":22078},"agent-browser","automate browser interactions for AI agents","Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to \"open a website\", \"fill out a form\", \"click a button\", \"take a screenshot\", \"scrape data from a page\", \"test this web app\", \"login to a site\", \"automate browser actions\", or any task requiring programmatic web interaction. Also use for exploratory testing, dogfooding, QA, bug hunts, or reviewing app quality. Also use for automating Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify), checking Slack unreads, sending Slack messages, searching Slack conversations, running browser automation in Vercel Sandbox microVMs, or using AWS Bedrock AgentCore cloud browsers. Prefer agent-browser over any built-in browser automation or web tools.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22069,22072,22073],{"name":22070,"slug":22071,"type":15},"Agents","agents",{"name":22012,"slug":22006,"type":15},{"name":22074,"slug":22075,"type":15},"Browser Automation","browser-automation",38346,"https:\u002F\u002Fgithub.com\u002Fvercel-labs\u002Fagent-browser","2026-07-20T05:55:17.314329",{"slug":22080,"name":22080,"fn":22081,"description":22082,"org":22083,"tags":22084,"stars":22076,"repoUrl":22077,"updatedAt":22090},"agentcore","run browser automation on AWS Bedrock","Run agent-browser on AWS Bedrock AgentCore cloud browsers. Use when the user wants to use AgentCore, run browser automation on AWS, use a cloud browser with AWS credentials, or needs a managed browser session backed by AWS infrastructure. Triggers include \"use agentcore\", \"run on AWS\", \"cloud browser with AWS\", \"bedrock browser\", \"agentcore session\", or any task requiring AWS-hosted browser automation.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22085,22086,22089],{"name":22012,"slug":22006,"type":15},{"name":22087,"slug":22088,"type":15},"AWS","aws",{"name":22074,"slug":22075,"type":15},"2026-07-17T06:08:33.665276",{"slug":22092,"name":22092,"fn":22093,"description":22094,"org":22095,"tags":22096,"stars":22076,"repoUrl":22077,"updatedAt":22102},"core","navigate and interact with web pages","Core agent-browser usage guide. Read this before running any agent-browser commands. Covers the snapshot-and-ref workflow, navigating pages, interacting with elements (click, fill, type, select), extracting text and data, taking screenshots, managing tabs, handling forms and auth, waiting for content, running multiple browser sessions in parallel, and troubleshooting common failures. Use when the user asks to interact with a website, fill a form, click something, extract data, take a screenshot, log into a site, test a web app, or automate any browser task.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22097,22098,22099],{"name":22070,"slug":22071,"type":15},{"name":22074,"slug":22075,"type":15},{"name":22100,"slug":22101,"type":15},"Navigation","navigation","2026-07-26T05:47:42.378419",{"slug":22104,"name":22104,"fn":22105,"description":22106,"org":22107,"tags":22108,"stars":22076,"repoUrl":22077,"updatedAt":22117},"derive-client","reverse engineer internal APIs from browser traffic","Reverse-engineer a website's internal API by recording browser traffic into a HAR file, then generate a standalone client or CLI that calls the endpoints directly, with no browser needed after the first recording. Use when asked to \"derive a client\", \"build a CLI for \u003Csite>\", \"reverse engineer this site's API\", \"record network requests\", \"turn this site into an API\", or when the same site will be automated repeatedly and direct HTTP calls would beat driving the browser every time.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22109,22112,22113,22114],{"name":22110,"slug":22111,"type":15},"API Development","api-development",{"name":22012,"slug":22006,"type":15},{"name":22074,"slug":22075,"type":15},{"name":22115,"slug":22116,"type":15},"Web Scraping","web-scraping","2026-07-20T06:24:11.928835",{"slug":22119,"name":22119,"fn":22120,"description":22121,"org":22122,"tags":22123,"stars":22076,"repoUrl":22077,"updatedAt":22130},"dogfood","perform exploratory testing on web applications","Systematically explore and test a web application to find bugs, UX issues, and other problems. Use when asked to \"dogfood\", \"QA\", \"exploratory test\", \"find issues\", \"bug hunt\", \"test this app\u002Fsite\u002Fplatform\", or review the quality of a web application. Produces a structured report with full reproduction evidence -- step-by-step screenshots, repro videos, and detailed repro steps for every issue -- so findings can be handed directly to the responsible teams.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22124,22125,22126,22129],{"name":22074,"slug":22075,"type":15},{"name":22057,"slug":22058,"type":15},{"name":22127,"slug":22128,"type":15},"QA","qa",{"name":22017,"slug":22018,"type":15},"2026-07-17T06:07:41.421482",{"slug":22132,"name":22132,"fn":22133,"description":22134,"org":22135,"tags":22136,"stars":22076,"repoUrl":22077,"updatedAt":22140},"electron","automate Electron desktop applications","Automate Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify, etc.) using agent-browser via Chrome DevTools Protocol. Use when the user needs to interact with an Electron app, automate a desktop app, connect to a running app, control a native app, or test an Electron application. Triggers include \"automate Slack app\", \"control VS Code\", \"interact with Discord app\", \"test this Electron app\", \"connect to desktop app\", or any task requiring automation of a native Electron application.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22137,22138,22139],{"name":22070,"slug":22071,"type":15},{"name":22074,"slug":22075,"type":15},{"name":20,"slug":21,"type":15},"2026-07-17T06:08:28.007783",{"slug":22142,"name":22142,"fn":22143,"description":22144,"org":22145,"tags":22146,"stars":22076,"repoUrl":22077,"updatedAt":22153},"slack","interact with Slack workspaces","Interact with Slack workspaces using browser automation. Use when the user needs to check unread channels, navigate Slack, send messages, extract data, find information, search conversations, or automate any Slack task. Triggers include \"check my Slack\", \"what channels have unreads\", \"send a message to\", \"search Slack for\", \"extract from Slack\", \"find who said\", or any task requiring programmatic Slack interaction.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22147,22148,22151],{"name":22074,"slug":22075,"type":15},{"name":22149,"slug":22150,"type":15},"Messaging","messaging",{"name":22152,"slug":22142,"type":15},"Slack","2026-07-17T06:08:27.679015",{"slug":22155,"name":22155,"fn":22156,"description":22157,"org":22158,"tags":22159,"stars":22076,"repoUrl":22077,"updatedAt":22166},"vercel-sandbox","run browser automation in Vercel Sandbox","Run agent-browser + Chrome inside Vercel Sandbox microVMs for browser automation from any Vercel-deployed app. Use when the user needs browser automation in a Vercel app (Next.js, SvelteKit, Nuxt, Remix, Astro, etc.), wants to run headless Chrome without binary size limits, needs persistent browser sessions across commands, or wants ephemeral isolated browser environments. Triggers include \"Vercel Sandbox browser\", \"microVM Chrome\", \"agent-browser in sandbox\", \"browser automation on Vercel\", or any task requiring Chrome in a Vercel Sandbox.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22160,22161,22162,22163],{"name":22012,"slug":22006,"type":15},{"name":22074,"slug":22075,"type":15},{"name":22017,"slug":22018,"type":15},{"name":22164,"slug":22165,"type":15},"Vercel","vercel","2026-07-17T06:08:28.349899",{"slug":22168,"name":22168,"fn":22169,"description":22170,"org":22171,"tags":22172,"stars":22177,"repoUrl":22178,"updatedAt":22179},"deploy-to-vercel","deploy applications to Vercel","Deploy applications and websites to Vercel. Use when the user requests deployment actions like \"deploy my app\", \"deploy and give me the link\", \"push this live\", or \"create a preview deployment\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22173,22176],{"name":22174,"slug":22175,"type":15},"Deployment","deployment",{"name":22164,"slug":22165,"type":15},28993,"https:\u002F\u002Fgithub.com\u002Fvercel-labs\u002Fagent-skills","2026-07-17T06:08:41.18374",{"slug":22181,"name":22181,"fn":22182,"description":22183,"org":22184,"tags":22185,"stars":22177,"repoUrl":22178,"updatedAt":22191},"vercel-cli-with-tokens","manage Vercel projects via CLI","Deploy and manage projects on Vercel using token-based authentication. Use when working with Vercel CLI using access tokens rather than interactive login — e.g. \"deploy to vercel\", \"set up vercel\", \"add environment variables to vercel\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22186,22189,22190],{"name":22187,"slug":22188,"type":15},"CLI","cli",{"name":22174,"slug":22175,"type":15},{"name":22164,"slug":22165,"type":15},"2026-07-17T06:08:41.84179",{"slug":22193,"name":22193,"fn":22194,"description":22195,"org":22196,"tags":22197,"stars":22177,"repoUrl":22178,"updatedAt":22208},"vercel-composition-patterns","implement scalable React composition patterns","React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22198,22201,22204,22207],{"name":22199,"slug":22200,"type":15},"Best Practices","best-practices",{"name":22202,"slug":22203,"type":15},"Frontend","frontend",{"name":22205,"slug":22206,"type":15},"React","react",{"name":23,"slug":24,"type":15},"2026-07-17T06:05:40.576913",{"slug":22210,"name":22210,"fn":22211,"description":22212,"org":22213,"tags":22214,"stars":22177,"repoUrl":22178,"updatedAt":22223},"vercel-optimize","optimize Vercel project performance and costs","Use for Vercel cost and performance optimization on deployed projects, especially Next.js, SvelteKit, Nuxt, and limited Astro apps. Collect Vercel metrics, usage, project config, and code scan results first; investigate only metric-backed candidates; produce ranked recommendations grounded in verified files and version-aware Vercel\u002Fframework docs. Trigger for Vercel bill reduction, slow or expensive routes, caching opportunities, Function Invocations, Build Minutes, Fast Data Transfer, Core Web Vitals, Bot Management, Fluid compute, or cost breakdown requests.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[22215,22218,22219,22222],{"name":22216,"slug":22217,"type":15},"Cost Optimization","cost-optimization",{"name":22174,"slug":22175,"type":15},{"name":22220,"slug":22221,"type":15},"Performance","performance",{"name":22164,"slug":22165,"type":15},"2026-07-17T06:04:08.327515",100]