Repository navigation
feat(lab): Embedded views — Frame surface live-bound to the lab, an A2UI release demo - #261
Merged
Merged
Conversation
…ace, A2UI The lab's Embedded views screenset (⌘3, WORKSPACES_LOAD "Embedded views"): one pane per kind, each naming it — a Dioxus component, a standalone Luau ImGui script, Frame's frame.surface (frame-surface when found, a stand-in otherwise; LAB_FRAME_SET / LAB_FRAME_STATE) and the A2UI agent surface (LAB_A2UI_DATA reads a surface's data). Guide scenario embedded-views uses each pane once. STA-653. Co-Authored-By: Claude Code <noreply@anthropic.com>
The Embedded views screenset's ImGui pane is now architect's ShowDemoWindow: a lab extension's Luau script (apps/lab/extensions/imgui-demo) with the sections of Dear ImGui's imgui_demo.cpp (MIT; code and words our own), each drawing what architect's ImGui can and naming in a disabled "not implemented" line the API it lacks, looked up in the module as it runs. With no scrolling, sections drill in. CANVAS_IMGUI_DEMO brings it up from anywhere (Vault, screenset, pane focused) for the CLI and MCP. The demo takes the left column's full height; Dioxus, Frame and A2UI stack on the right. A script canvas now draws one more frame after a release, so what a click changed shows without waiting for input. Co-Authored-By: Claude Code <noreply@anthropic.com>
The ImGui pane goes back to the Luau script's window; the effort is in the Frame and A2UI panes. - Frame: the pane loads apps/lab/surfaces/drive.frame into the real frame-surface extension, reloads it on save, and binds its knob to the lab's drive with the meter following (LAB_FRAME_SET, LAB_FRAME_STATE, over the extension's FRAME_SURFACE_* actions). - A2UI: LAB_A2UI_DEMO renders a release surface as an agent: a form, a task list, and a Ship it button whose LAB_RELEASE_SHIP waits for the person's yes and writes its answer back. A2UI_PATCH patches it live. - architect-a2ui: a button's wantResponse/responsePath, as the catalog spells them, are honoured (a2ui-base reads only the snake_case names). - The embedded-views guide covers all of it. Co-Authored-By: Claude Code <noreply@anthropic.com>
Use immutable integration checkpoints until the upstream dependencies ship as tags.
…el position Work in progress, saved so it is not lost. Lab: Keyflow editor and chart panes, the Chart screenset, New Chart command, Keyflow actions (state, focus, set source, set cursor, chart view, chart theme), Embedded rail entry. Shell: full-width title and status bars on the desk, page tabs in the title row. Workspace: ViewInput::Wheel carries position and modifiers; View::headless hides a pane header. Cargo.lock and local path overrides are not committed; the lab builds against local checkouts through an uncommitted --config file.
…s only its colour The lab's override was written for the caret-as-a-mark experiment (editor f11406a, not taken): it forced an inline-block 2px box that fought the editor's zero-width caret. The lab now sets --editor-caret only.
…bf4bc) The IME commit (ba4ac02) sat on a base without v0.7.1's dioxus-vdom suspended-boundary fix; 25bf4bc is the same change on the tag, so dioxus-native, -dom and -vdom all come from v0.7.1's line.
…s branch The Keyflow lab feature built only through local overrides: Keyflow 4605563 predates the wheel position in ViewInput::Wheel. keyflow#48 pins this branch, editor#22 and the Blitz/vendor IME commits, so `cargo check -p architect-desktop --features keyflow-lab` builds as is.
…es, THEME_* actions A theme is a small palette plus overrides, resolved in OKLab into every semantic token (architect-ui's, the shell's, the signals); a ThemeStore composes the layers (builtin, app, org, person, device, workspace, pick, preview) and broadcasts once per real change. Theme files are strict JSON (comments allowed) with last-good reload, a notify watcher and the shipped-upgrade rule. architect-ui's presets are now the built-in theme files; ThemeStoreProvider follows a store.
…s follow a theme store ShellTheme's colours are owned (Arc<str>) so they can change at run time; ShellTheme::from_resolved builds them from a resolved theme, and AppShell follows the app's store when given no theme. WorkspaceView's theme defaults to the shell's. PaintPane gets the whole resolved theme as ThemeTokens (From<&Resolved>); without a store, shell_tokens names the accent trio brand/brand-ink/brand-soft. Icon.color is an Arc<str>. Extensions may declare [[themes]]. The lab keeps one store: its shipped themes in <app dir>/themes (watched), THEME_* on its Action List, a ⌘K picker, the OS's light/dark from the window.
…daries rows, change file
…t, process views Seen in the lab on a switch to a light theme: - the workspace host kept its colours in a CopyValue, so its panes, tab bars and docks stayed on the theme they were first drawn with; it is a Signal now (set only when it changes); - architect-ui's ThemeStoreProvider also writes its variables on :root (an app-level sheet), and hands them down as architect_theme::RootCss; - a process extension's view is a Blitz sub-document that never sees the host's sheets: RemoteDocument::set_theme_css puts the host's :root sheet in its head, set when the view mounts and on every change, so its var(--…) follow the app.
…s, provenance, THEME_INSPECT
A domain app (Studio) can now put namespaced tokens on top of architect's
semantic ones and have them follow every palette:
- Token names are dot-separated `[a-z0-9-]` segments (`arrange.item.edge`)
in theme files, layers, `Theme::from_tokens` and `THEME_TOKEN_SET`.
- Values may be expressions (`$name`, `mix(a, b, t)`, `alpha(a, t)`, nested;
`features/theme/src/expr.rs`), evaluated after derivation and every
layer, so a later literal wins and a reference sees the final value.
An unknown name, a cycle, a non-colour or a t outside 0..1 is magenta
plus a `Problem` naming the token (`ThemeStore::problems`,
`THEME_PROBLEMS`).
- `Resolved::number`, `source` (`TokenSource`, `Origin`), `misses`,
`problems`, `css_tokens`, `css_namespaced`; `Projected::revision`.
- `ThemeStore::revision` is an atomic; `insert_theme` / `remove_theme`
with `Source::Memory` themes kept across a reload.
- `THEME_INSPECT {prefix?}`; `THEME_TOKEN_SET` / `_RESET` take `layer`.
- architect-ui's root CSS writes only undotted tokens;
`ThemeStoreProvider { css_namespaces }` opts an app's in.
Breaking (pre-1.0): `Resolved::with_overrides` removed, `Composed` gains
`chosen_by` / `set_by`, `Source::Memory` added. Cargo.lock gains the
architect-extensions -> architect-theme edge that HEAD already had in its
manifest.
…pins it A typed slice of the theme (ChartColours: paper, ink, staff from card, card-foreground and muted-foreground) drives the chart's ChartTheme, so the paint view repaints only when those colours change. KEYFLOW_SET_CHART_THEME still chooses light or dark, and from then on the chart keeps that choice.
…s, in and out as a diff ThemeStore::contribute / withdraw / contribution keep each owner's themes and token defaults beside the app's registry: a reload is a diff, a broken file keeps the owner's last good theme and is a problem, an id or token someone else has is left out and said. The tokens are a new LayerKind::Extension, under App. THEME_LIST says extension:<id> (Source::label); Rgba::packed gives ImGui's 0xRRGGBBAA.
…s theme store Host::with_themes(store): an extension's [[themes]] files and its new [tokens] table (its own namespace, Manifest::token_namespace, checked like a theme's overrides) are contributed as it loads, diffed on a reload (a dev copy's save shows at once), withdrawn as it unloads. A file that does not read is a THEME_PROBLEMS entry, never a failed load (host::theme_contribution).
…revision architect_scripting::theme::ThemeModule over a ThemeSource (the app's ThemeStore, or a ThemeCell a host sets): theme.color (0xRRGGBBAA), value, number, scheme, id, revision, snapshot() (the same read-only table until the revision moves) and subscribe(fn) for live scripts. Module gains frame() (called as each live frame starts) and live_fields() (a mutable global, so Luau never folds theme.scheme at load).
… restyled on every switch imgui::Style::from_theme maps each style colour (imgui::Col, Dear ImGui's ImGuiCol_ values) to a theme token (imgui.button, …) or, when the theme has none, a derivation from the shell's ($sheet, $pill, mix($pill, $brand, 0.18), …). luau::Session::start_themed / set_theme restyle the windows and move the script's theme (the scripting ThemeModule over a ThemeCell). Scripts override one colour with ImGui.PushStyleColor / PopStyleColor and ImGui.Col_*. use_script_canvas follows the theme store above it: a theme change restyles and draws a frame, so a Luau ImGui pane no longer keeps its first colours.
… own thread; busy cleared on unmount; an overdue event noticed as it is asked Live::last_frame_time is the script's time on its thread, what Session::frame_metered records (the host's own wait no longer counts as the view's). A pane unmounted mid-frame clears its meter's busy state. RemoteView looks again at the oldest unanswered event whenever one is asked, so a view blocked by its newest event goes busy.
… a naughty view marked busy, a process view using use_work heavy.luau counts primes below three million in its [[workers]] primes worker with a live progress bar and a spinner that keeps turning; naughty.luau spins 300 ms a frame on its own thread and is marked busy; the process view counts primes with use_work and can block its own view thread for two seconds. The lab provides one Meters (EXT_VIEWS, forwarded) and logs slow or busy views through its ViewReport.
… runner, budgets and the busy mark; WASI's mapping; change files
…used mut (no behaviour change)
Doc paragraphs split (every word kept), a similar binding renamed, a write! for a push of format!, asserts and indexing in the APP_SHOT test helpers replaced by checked and get-based code, redundant clones removed. No behaviour change, no new allow attributes.
…ited over a channel
…w edits, page files, generated architect.d.ts/.d.luau, TSX by oxc
…thread, frames as edits, reload on save keeping the last good page, a per-call interrupt, workers, the host in process
…cument, timed and marked busy; ViewPages so an override file or a view's page draws an extension's view live; [[views]] page
…read-only built-in under the lab's folder; overrides in <lab dir>/pages; PAGES_STATUS/CHECK/CLONE; the page types written at start
… clone-to-edit brings the page's files; a page in a shell slot has the bar's height
…he same edit path, the theme read through; the lab runs them
…ted types, the runners, safety and performance; boundaries rows; change file
The guide test names PAGES_STATUS, PAGES_CHECK and PAGES_CLONE, the pages actions on the lab's list.
…one generated view API, live reload with last-good (#275) Stacked on #274. Implements #273 (first phase): a page is an editable file, re-rendered live on save, as in herdr's desktop app. - **Fix (from #274):** a script's action calls run on the app's Tokio runtime and the script awaits the answer over a channel (\`features/scripting/src/vm.rs\` \`Dispatch\`); \`tests/runtime.rs\` failed with "no reactor running" before. - **One API, generated types:** \`features/pages/src/api.rs\` \`PageHost\` (run, actions, settings, theme, shared state, notify, subscriptions) and \`PageView\` (render, overlays, events) as \`#[architect::rpc]\` services; \`architect.d.ts\` from vox's TS codegen and \`architect.d.luau\` (\`features/pages/src/codegen.rs\`), checked in under \`features/pages/types/\` with a drift test, written into the app's folder at start. - **QuickJS** (\`rquickjs\`): a runtime per page on its own thread; the window only applies \`wire::Edit\` frames to the same \`RemoteDocument\` process views use. Limits: 64 MB / 1 MB stack, 500 ms per render/handler/timer (interrupt: "hand heavy work to a worker"), busy mark after 16 ms. \`worker.spawn("job.js")\` in its own runtime. Imports limited to the page's folder, no Node APIs, \`console.log\` to the page's log, theme snapshot keyed by revision. - **TSX by oxc:** TS strip and JSX → \`h()\` in about 1 ms with file:line:col errors; save-to-screen about 150–300 ms (the 200 ms folder watch dominates). - **Luau element pages** on the same API. - **File-backed pages:** \`[[views]] page = "pages/x.tsx"\`; a compiled view is overridden live by \`<app dir>/pages/<view-id>.tsx|.luau\` and restored when the file goes; built-in page files read-only with clone-to-edit (the page's other files copied too). A broken save keeps the last good page under a banner and writes \`<file>.error\`. \`PAGES_STATUS\` / \`PAGES_CHECK\` / \`PAGES_CLONE\`. - **Lab:** the Board's tab is \`apps/lab/board/pages/board.tsx\` (data via \`useData("LAB_BOARD_STATE")\`, its own actions, theme tokens, a primes worker). - **Fix found live:** frames with no input behind them weren't drawn until the next click — in \`PageView\` and the same gap in \`RemoteView\`. Tests: \`architect-pages\` diff (3), js (8: handlers, live reload with broken-save fallback, runaway stopped, Tokio action, worker, imports/no Node, theme, check), luau (1), pane (1: override then restore), types (3), 6 unit; board page (1); scripting, extensions, board \`both_ways\` pass. clippy clean on \`architect-pages\`; \`xtask docs\` 0; \`xtask boundaries\` 16; \`just ci-changes\`; \`cargo check -p architect-desktop\`. Live in a Loft test session: the TSX Board, pin, worker result, a live edit appearing within 1 s with no input, the broken-save banner and \`.error\`, a theme switch, a \`status-bar.tsx\` overriding the compiled bar and the bar returning when deleted. Not yet: A2UI JSON pages (kind recognised, not run); Luau pages without workers/timers; runtime errors point at the transpiled JS (no source maps); \`APP_STATUS\`/\`EXT_CHECK\` seams (\`Pages::problems\`, \`Pages::check\`) not wired; the stream half of \`PageHost\`; the web Web Worker runner; phase two (worker-side DOM); a first click with no prior pointer move was missed once. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
#278) ## What Makes `cargo clippy --workspace --all-targets` clean (default features, exit 0, no warnings) and `cargo clippy --workspace --all-targets --all-features` clean on the extension stack. No behaviour change. - Cherry-picked the `main` fix (`crdt`: `use_here` doc paragraph split), from #276. - `features/workspace`: doc paragraphs split (`ctx`, `home`, `kinds`, `paint/mod`); `const fn` on `HostData::shape`, `PaintView::len`/`is_empty`/`invalidate`, and the private `with_wake`; identical `Ime` commit and `Paste` arms merged; `mul_add` and `Cell::get` path forms in tests (`W` and `H` are 8.0 and 6.0, so the products are exact and the results do not change); `chunks_exact(4)` to `as_chunks::<4>()`; `Default::default()` to `Insets::default()`; `;` added to block-tail statements; test-only arithmetic (`drawn.saturating_add(1)`, `checked_add(..).unwrap()`) for `arithmetic_side_effects`. - `features/extensions`: ten doc paragraphs split (`contrib`, `contrib_actions`, `guest`, `interface`, `linked`, `manifest`, `wire`). Words are kept; a few sentence breaks became paragraph breaks. - `apps/lab/ui` (feature `keyflow-lab`, all-features only): `args["x"]` indexing replaced by a `get` helper that returns `null` for a missing field, as serde_json's index does; `apply` takes `&ChartCommand`; `ok()` returns the bytes and the closures wrap it in `Ok`; two redundant clones removed; a doc backtick. `paint.rs`: `stats` renamed to `timing` (similar names). `vault.rs`: the `mut` is now only where the `keyflow-lab` push needs it (the unused `mut` was a warning without the feature). ## Counts Each pass only shows what the one before let through: `architect-workspace` failing stops its dependents from being checked. | Build | Before | After | | --- | --- | --- | | default, `--all-targets`, pass 1 | 11 (`architect-workspace` lib) | | | default, pass 2 | 27 (workspace tests, extensions docs) | | | default, pass 3 | 12 (workspace tests, `architect-lab-ui` `paint.rs`/`vault.rs`) | 0 | | `--all-features`, `--all-targets` | 13 (`architect-lab-ui` `keyflow.rs`, after default was clean) | 0 | Totals across the passes: `too_long_first_doc_paragraph` 14 (4 workspace, 10 extensions), `missing_const_for_fn` 5, `match_same_arms` 1, `redundant closure` 1, `arithmetic_side_effects` 2, `semicolon_if_nothing_returned` 3, `default_trait_access` 2, `chunks_exact` to `as_chunks` 2, `suboptimal_flops` 7, `similar_names` 2, `unused_mut` 1, plus the `keyflow.rs` set: `redundant_clone` 2, `doc_markdown` 1, `needless_pass_by_value` 1, `unnecessary_wraps` 1, `indexing_slicing` 8. No `#[allow]` added. ## Checks - `cargo clippy --workspace --all-targets --keep-going` (default): exit 0, 0 diagnostics. - `cargo clippy --workspace --all-targets --all-features --keep-going`: exit 0, 0 diagnostics. - `cargo fmt --all -- --check`: exit 0. - `cargo xtask docs`: exit 0 (114 pages, 0 names that do not exist). - `just ci-changes`: exit 0 (`.changes/fixed-clippy-lints-in-70b7.md`, bump `none`). - `cargo test -p architect-workspace -p architect-extensions -p architect-lab-ui`: exit 0. - `cargo test -p architect-lab-ui --features keyflow-lab`: exit 0. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…ers, frame budgets with a busy state, guest spawn_work (#274) Stacked on #271. The rule: **an extension's GUI never waits on its own heavy work, and the app's UI never waits on any extension.** - **Luau off the UI thread** (\`features/scripting\`): one-off runs (actions, hooks) on a growing pool, dropping the future stops the script; each live script (ImGui window, overlay) its own thread and Luau state; the UI only awaits channels. The audit's one UI-path wait — Luau futures polled inline on the UI executor — is gone. Theme snapshots work across threads. - **Workers:** \`[[workers]]\` in the manifest (\`script\` or \`entry\`); Luau \`worker.spawn/post/cancel/progress/on_message/on_done\` (\`features/scripting/src/worker.rs\` \`Workers\`, \`WorkerGroup\`), each worker its own Luau state with its own limits, no UI access; cancelled with its owner or extension (under 1 s on unload). Process guests: \`Guest::spawn_work\` / \`Work\` / \`WorkHandle\` (drop cancels) / \`use_work\` (\`features/extensions/src/work.rs\`). - **Budgets and degradation:** \`ViewMeter\`/\`Meters\` (\`features/workspace/src/meter.rs\`) time every extension view on its own thread; past budget the pane keeps its last frame with a theme-coloured \`BusyBadge\` and the rest of the app keeps answering; \`EXT_VIEWS\` reports p50/p99/slow counts; \`Meters::with_report\` is the seam for \`APP_STATUS\` (#270). Luau \`Limits\` gain \`frame\` (500 ms) and \`time\`. - **Lab:** the clicker's primes job in a worker with a live progress bar; a deliberately naughty view. - **WASI mapping** (documented): a worker is another instance of the component on a pool thread; on the web, a Web Worker. Breaking (pre-1.0): \`Limits\` fields; process wire 5 (\`ANSWERED_WIRE\`): every event is answered with a frame. Measured: with a 0.66 s job in a worker, 313 view frames at p50 20 µs / p99 182 µs (the same job a quarter as long inline: one 97 ms frame); while one view spent 250 ms per frame another drew 6,898 frames within budget. Live in a Loft test session: busy mark on a naughty view and on a blocked process pane while other panes update; the progress bar and frame counter advancing during a job. Tests: scripting \`tests/workers.rs\` (7) + \`vm.rs\` (3); canvas \`a_naughty_view_goes_busy_and_the_other_panes_keep_updating\`, \`a_window_with_a_worker_animates_within_budget_while_the_job_runs\`; extensions \`tests/workers.rs\` (4, incl. \`unloading_an_extension_cancels_its_workers\`), guest work (2), manifest; clicker. fmt, \`xtask docs\`, \`xtask boundaries\` (16), \`cargo check -p architect-desktop\`, \`just ci-changes\`. Known: actions a script calls now run on its VM thread, so an action needing a Tokio runtime context there fails (fix next); \`entry\` workers not checked at hello; a self-sent process frame is attributed to the oldest pending event; linked extensions not metered; per-view budgets not in the manifest; Luau not yet on wasm. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…ns manager (#277) Stacked on #270. #178, #181 (widened into the Extensions manager), #239 X5. - **Host service:** an extension sends one request (run an app action, org, selection, open/read a document, read/write a file, its own setting or data, log) and gets \`{ok}\`/\`{error}\`, the same from a process (\`guest::host\`), Luau (\`host\`) and in-process (\`Handle::host\`); a WASI runner maps one import onto it. Each call is checked against the extension's manifest capabilities narrowed to what a person granted (new \`capabilities.reads\`: org, selection, documents); a refusal names the missing capability and is logged. Actions run as \`Principal::Extension { id, on_behalf_of }\` (**breaking** in \`architect-permissions\`); \`perm.*\` on the span (wide-events behind an opt-in feature to keep the UI build off tower-http). - **Consent:** install asks first; grants per device or per org in \`<app dir>/extension-grants.json\`, revocable (next call refused); an agent asking to install or grant gets \`confirmation_required\` with a ticket, never a hang; built-in and dev copies run with what they ask. - **Actions:** \`EXT_PREVIEW\`, \`EXT_INFO\`, \`EXT_UPDATE\` (skips unchanged), \`EXT_PIN\`, \`EXT_GRANT\`, \`EXT_REVOKE\`, \`EXT_GRANTS\`, \`EXT_LOGS\`, \`EXT_HOST_CALL\`; \`EXT_INSTALL\` takes \`grant\`/\`pin\`; \`MANAGER_KINDS\`/\`_LIST\`/\`_GET\`/\`_RUN\`. - **Extensions manager** (\`features/extensions/src/manager/\`): one place for everything that customizes an app, a registry of \`ItemKind\`s (list, get, operations) shipping extensions, Luau scripts and hooks, workers, themes, layouts and \`keys.json\`; an app adds kinds with \`Manager::register\` (e.g. Studio's audio plugins). Itself a linked-in extension (\`manager\`), overridable by a dev copy (#272). UI from architect-ui and theme variables only: a settings section, a workspace view, ⌘O \`ext:\`; a project's missing extensions use the same consent prompt. Tests (all pass): \`the_metronome_runs_an_app_action_and_reads_its_setting_through_the_host\`, \`a_call_outside_its_capabilities_is_refused_and_logged\`, \`installed_it_runs_with_what_a_person_granted\`, \`the_page_finds_extensions_by_name_kind_and_what_they_add\`, \`the_manager_lists_and_acts_on_every_kind\`, the clicker asking over its connection, a linked extension asking through its handle, permissions' extension principal tests; 232 across touched crates + 29 action-list. No new clippy lints; \`xtask docs\` 0; \`xtask boundaries\` 16; \`architect-desktop\` builds. Live in a Loft test session: the metronome's consent prompt and install (grant written), the Board's page "linked into the app", Revoke and the next call refused at \`reads.selection\`, \`EXT_HOST_CALL\` refused at \`capability: actions\`, \`EXT_LOGS\`, \`APP_STATUS\` and \`MANAGER_LIST {kind: layout}\` listing a broken layout file. Three bugs found live and fixed (app actions looked up on the wrong list; clicks in a nested scroller; a clipped overlay prompt). Left: frame health (#274 \`EXT_VIEWS\`) is a placeholder; QuickJS (#275) and WASI kinds not registered yet; the confirmation sheet for an agent's ticket doesn't show the capabilities; per-org grants are a local file, not synced; the manager view is cramped in a narrow pane (first 12 rows). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…_SHOT, EXT_CHECK, a generated guide, refused unknown args, clone-to-edit, an escape hatch (#270) Stacked on #267 (alongside #269). #268 items H2, H3, H4, H5 and H7: the tools herdr's desktop app gives a coding agent to change the running UI and check its own work. - **H2 Guide:** \`<app dir>/AGENTS.md\` and \`actions.schema.json\`, written from the live Action List when the app settles and whenever an action comes or goes (\`architect_control::guide::Guide\`: the app's own opening plus architect's rules). A test fails if it misses an action. - **H3 \`APP_SHOT {path?, pane?}\`:** the main window's Blitz document painted again on the CPU (\`anyrender_vello_cpu\`) at its pixel size; a pane crops to its measured rectangle. \`actions shot [file] [--pane id]\`. Not pop-outs; GPU-only custom widget paint may be missing. - **H4 \`APP_STATUS\`:** theme, workspace (revision, focus, screenset, windows), pending confirmations and every problem with source, file, field and message, from a \`Problems\` registry features report into (\`crates/architect/src/action_list/app.rs\`); theme files and extension loads report today. - **H5 \`EXT_CHECK {folder}\`:** load an extension and render each view once, or the exact error (\`features/extensions/src/check.rs\` \`Checker\`); \`architect-desktop extensions check <folder>\` exits nonzero. - **H7 Safety:** an argument name an action doesn't take is refused (\`bad_args\`) listing the valid names (**breaking**: it was ignored); agents are told to ask, never left waiting; \`WORKSPACES_CLONE\` / \`WORKSPACES_RESTORE_BUILTINS\`, and a save over a built-in from outside the app answers \`builtin\` (**breaking**); \`EXT_CLONE\` (refused for process extensions, whose program answers its old ids); Ctrl+Alt+Shift+Escape restores the built-in layouts and can't be rebound. - Lab: \`LAB_GO {tab}\` so an agent can open a page. Tests: \`unknown_argument_names_are_refused_with_the_valid_ones\`, \`status_is_every_section_and_every_problem\`, \`the_guide_names_every_action(_of_the_lab)\`, \`the_escape_hatch_cannot_be_rebound\`, \`a_built_in_set_is_cloned_to_change_it\`, \`the_escape_hatch_restores_the_built_in_layouts\`, \`a_shot_is_the_window_at_its_pixel_size\`, \`the_metronome_checks_clean_and_a_broken_copy_says_exactly_why\`, \`app_status_lists_a_broken_theme_and_a_broken_extension\`, and more; existing MCP, theme, workspace, desktop and lab scenario suites pass. \`xtask docs\`, \`xtask boundaries\` (16, unchanged), \`just ci-changes\`, \`cargo check -p architect-desktop\`. Clippy clean on new code (base lints in \`features/workspace\` and \`extensions/manifest.rs\` remain). Live check in a Loft test session: \`APP_SHOT\` matched the compositor's screenshot; a broken theme file showed in \`APP_STATUS\` with file and field; \`EXT_CHECK\` passed on the clicker and named \`effect\`, line 20, on a broken copy; a CLI save over a built-in was refused and \`WORKSPACES_CLONE\` worked; the escape hatch put the layout back. Left of #268: H1 (layouts and keymap as live files, \`.error\`), H6 (the chrome as views), H8 (view parity) — the next workstream; Luau API type files in the guide; pop-out shots. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…, in-process extensions, the chrome as slots, the interface as files (#271) Stacked on #269. Workstream 3 of the extension plan: #179, #183, #239 (X1–X4, X6) and #268 H1 + H6 — extensions (and agents) change the app's GUI at run time, and nothing in the default interface is special. - **Contributions (#179):** \`[[openers]]\` by file extension, \`DocKind\` and URL pattern (Ctrl/Cmd-click routes to a URL opener), \`[[doc_kinds]]\`, \`[[tabs]]\`, view \`home\`, \`[[status]]\`, \`[[menus]]\`, \`[[context]]\` (a target's context menu), \`[[bar]]\` (phone action bar); also at run time from Luau (\`ext\`) and process guests (wire 4). All withdrawn as a diff on unload (\`Contributions\` registry). - **Lifecycle (#239):** \`[[hooks]]\` on startup and app events (an action or a script function); \`Invocation\` on \`ActionCaller\` (palette, menu, context with its target, key, hook, agent); view \`placement\` (tab, split, overlay, popup, zoomed, float) and \`on_unload\`; status items and badges owned by their source, time-limited, sequenced, withdrawn on unload. - **In-process (#183):** \`features/extensions/src/linked.rs\` \`Extension\` (manifest, files, \`build\`), registered with \`Extensions::with_linked\` as a built-in, on/off at run time, overridable by an installed or dev copy; the same value runs as a process via \`Guest::extension(&ext).serve()\` (\`apps/lab/board\`, test \`both_ways\`). - **The chrome as slots (#268 H6):** \`Chrome\` above \`AppShell\`: rail/ribbon, sidebars, tab bar, status bar and title row are slots a built-in or extension view fills or replaces; the shell's own components are the default fillers. A golden recording of every Dioxus mutation of \`AppShell\` (desk and phone) from the base matches byte for byte. - **The interface as files (#268 H1):** \`layouts/<name>.json\` (look through the theme store's workspace layer, per-slot \`view\`/\`hidden\`/\`tokens\`, optional arrangement) and \`keys.json\`, watched and live; a broken file keeps the last good one and writes \`<name>.error\`; hand changes go to \`.state/\`; built-in \`main\` is read-only. \`HOME_PROBLEMS\` reports them (to be joined to #270's \`APP_STATUS\`). - **Lab:** \`apps/lab/board\`, an example contributing a tab, a status item, a context-menu entry, \`.kf\` and GitHub-issue openers, a startup hook and the status-bar slot. Breaking (pre-1.0, in the change file): \`Manifest::openers\` → \`Manifest::contributes.openers\`; runners get \`contributions\`; process wire 4 (2–3 still run; an older host refuses wire 4). Tests: architect lib 57; extensions lib 21 + contributions 2 + linked 2 + existing 19; shell 22 incl. the golden; workspace 46+32+15 incl. \`home::\`; commands 21+6; theme; \`architect-lab-board\` \`both_ways\`; desktop 2. fmt, \`xtask docs\`, \`xtask boundaries\` (16), \`cargo check -p architect-desktop\`. Live in a Loft test session: the Board's tab, status slot, context-menu pin, \`.kf\` opener with the \`doc.opened\` hook, the URL opener, a layout file applied and edited live, a broken file kept with \`.error\`, \`EXT_DISABLE\` withdrawing everything, and the Board as a process overriding the built-in. Left: a unit test for the pane context menu; the left slot's look doesn't recolour the sidebar ground; sizes/folds not yet in \`.state/\`; the phone bar and contributed menu rows not seen on screen; a process view isn't given its pane state. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…ributed themes, Luau theme, ImGui styles, the theme as data for process guests (#269) Stacked on #267. Workstream 1 of the extension plan: an extension and every GUI it draws follow the app's theme live, and an extension can bring themes and tokens of its own. - **Contributed themes and tokens:** an extension's \`[[themes]]\` files and \`[tokens]\` table (its own namespace, \`clicker.*\`, with \`$ref\` defaults) go into the app's \`ThemeStore\` on load and leave on unload, diffed (\`ThemeStore::contribute\` / \`withdraw\`, \`LayerKind::Extension\`); \`THEME_LIST\` names the source \`extension:<id>\`; a broken file is a \`THEME_PROBLEMS\` entry, not a failed load; dev copies hot-reload. A theme file can restyle an extension's tokens. - **Luau:** \`theme.color/number/scheme/revision\`, a frozen snapshot keyed by revision (herdr's \`__theme_revision\`), live fields so a script sees a switch. - **ImGui:** host style colours from tokens (\`imgui.<name>\` overrides, else defaults over \`ink\`, \`sheet\`, \`pill\`, \`brand\`, \`rule\`, \`chart-1\`), re-applied each revision; scripts can \`ImGui.PushStyleColor\`. - **Process guests get the theme as data:** wire 3 adds \`ThemeData\` (id, scheme, revision, tokens), sent after hello and on every real change; \`Guest::theme\`, \`on_theme\`, \`use_theme\`. Wire-2 processes still load (no theme sent). A host older than this refuses a wire-3 process (exact version check). - **Lab:** the clicker is a themed example — a process view styled by \`var(--clicker-*)\` and a ramp computed from theme data, a Luau ImGui window, its own token layer and a contributed "Clicker Neon" theme; the Luau ImGui pane now follows \`THEME_SET\` (it stayed dark before). Tests: theme contributions (6), extension themes (4), process theme (on open and on switch), Luau snapshot (3), ImGui light/dark mapping and a headless pixel check (3), clicker themed (2); existing suites of touched crates pass. fmt, \`xtask docs\`, \`xtask boundaries\` (16, unchanged), \`cargo check -p architect-desktop\`. Clippy: theme and scripting clean; canvas, extensions, clicker clean but for the known \`kinds.rs\` \`too_long_first_doc_paragraph\` in the base. Checked in a real lab window (dark, Catppuccin light, Clicker Neon). Left: a process extension can't yet ship a Luau window through its manifest (lab glue runs it); extension tokens reach a process view's CSS only where the app opts in (\`css_namespaces\`); no \`cargo hakari\` run for the new dev-dependencies. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…ew follows, app tokens, THEME_* actions (#267) Stacked on #266. A runtime theming system modelled on herdr's desktop app: a theme is a small palette plus overrides; everything else derives; one revisioned store; every view follows a switch live. **\`architect-theme\`** (\`features/theme\`, model wasm-clean): - \`resolve\`: palette → every semantic token architect-ui and the shell use (OKLab mixes, light/dark branches, ink contrast ≥ 4.5:1); magenta for an unknown name. - \`ThemeStore\`: layers builtin → app → org → person → device → workspace → pick → preview; \`set\` is a no-op when nothing changes; \`Send + Sync\`, lock-free \`revision()\`; \`insert_theme\` for in-memory themes. - App tokens: dotted names (\`arrange.item.edge\`), \`$name\` / \`mix(a,b,t)\` / \`alpha(a,t)\` values resolved after the cascade (cycles and unknowns: magenta + a \`Problem\`), \`number()\`, provenance (\`source\`), \`misses\`. - \`fs\`: strict JSON theme files, a watcher with last-good reload, shipped-theme upgrades only over an unedited earlier version. The 20 old presets are 40 theme files. - \`actions\`: \`THEME_LIST/GET/SET/PREVIEW/PREVIEW_END/TOKEN_SET/TOKEN_RESET/RELOAD/PROBLEMS/INSPECT\`. - \`dioxus\`: \`use_theme_store\`, \`use_resolved\`, \`use_themed::<T>()\` (a typed slice that changes only with its part). **Wiring (breaking, see the change files):** \`ShellTheme\` is runtime (\`Arc<str>\` fields); \`AppShell\`, \`WorkspaceView\` (its host theme is a signal now) and architect-ui's \`ThemeStoreProvider\` (also on \`:root\`) follow the store; paint panes get the resolved theme; a process extension's view (a Blitz sub-document) gets the host's \`:root\` sheet (\`RemoteDocument::set_theme_css\`). The lab: one store, \`THEME_*\` on its Action List, a ⌘K picker, the OS scheme; its Keyflow chart follows via a typed slice. Checks: tests of theme, shell, ui, workspace (ui, paint-ui), extensions (remote) — 217 passed, 0 failed; \`cargo xtask docs\` 0; \`cargo xtask boundaries\` 16 violations (unchanged); \`cargo check --workspace --all-targets\` and \`-p architect-desktop --features keyflow-lab\` without overrides. In a real window: \`THEME_SET\` across dark and light themes restyles the shell, panes, A2UI, the Keyflow chart and Frame's process-drawn pane live. Not yet: layers are not persisted; no Appearance settings page (#185); pop-out windows take the theme when opened; extension \`[[themes]]\` are declared but not loaded into the store. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…osition (#266) Stacked on the paint view PR. Supersedes the WIP draft #264 (its work, finished and pinned so it builds without local overrides). - The lab's opt-in \`keyflow-lab\` feature: Keyflow's source editor and chart (a paint view) in Embedded views and a Chart screenset; \`KEYFLOW_*\` actions on the Action List (state, set source/cursor, focus, chart view and theme). - Shell bars, and \`ViewInput::Wheel\` carries the pointer position and modifiers. - The lab's Keyflow caret: the editor's own zero-width caret; the lab sets only \`--editor-caret\`. - Pins: Keyflow keyflow#48, vendor's IME commit on v0.7.1 (\`25bf4bc\`), Blitz IME (\`db5b7ef\`). Checks (no local overrides): \`cargo check --workspace --all-targets\`, \`cargo check -p architect-desktop --features keyflow-lab\`. Run in a real window: typing into the Keyflow editor, the chart via \`KEYFLOW_*\`, the Frame surface pane driven by \`LAB_FRAME_SET\`. Known: in the lab's real window the plain caret is not painted (it is in every headless render, \`native_caret_probe\` in keyflow#48). Merge after editor#21/#22 and keyflow#48; re-pin to their merges. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…demand, live theme, WebGL2 and web panes (#265) Stacked on #261. The \`paint\` view kind (#221): an in-process view drawn on the GPU in a pane. - \`features/workspace/src/paint/mod.rs\`: \`PaintView\` (input / paint / hit), \`Painter::set_theme\`, \`Marks\`, \`FrameStats\`; \`KindSpec::paint\`. - Blitz host (\`paint/widget.rs\`, feature \`paint-ui\`): \`PaintPane\`, \`PaintMount::wake_handle\` / \`PaintWake::wake\` for external changes, live shell theme tokens. - Web: \`paint/web.rs\` WebGL2 canvas (\`paint-web\`), \`paint/web_ui.rs\` Dioxus web panes (\`paint-web-ui\`). Tests: 37 paint-ui tests, Blitz CPU-raster pixel checks at 1× and 2×, a browser smoke (hide/show, theme, coalesced wake, pointer, 2×). Used by Keyflow's chart in the lab (next PR). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
STA-653: the lab's Embedded views gallery. Following the Captain's change of focus, the work is in Frame and A2UI; ImGui is parked.
WORKSPACES_LOAD {name: "Embedded views"}over MCP/CLI. The layout is three columns: Dioxus over Luau ImGui, then Frame, then A2UI.imgui-demoextension, noCANVAS_IMGUI_DEMO). The metronome extension has no ImGui window, so there was nothing to open there; a metronome ImGui window can be a follow-up.frame.surface, drawn by Frame'sframe-surfaceprocess (feat(frame-view): the frame.surface view kind — a minimal provider (architect#224) frame#31).apps/lab/surfaces/drive.frame, a knob onsurface/driveand a meter on the feedlevel.ARCHITECT_LAB_FRAME_DOCUMENToverrides it.extensions::frame_binding, 150 ms):FRAME_SURFACE_LOAD) and pushes the drive (FRAME_SURFACE_PARAM_SET).FRAME_SURFACE_STATEand feeds the meterlevel = drive·66 − 60 dB(FRAME_SURFACE_FEED).LAB_FRAME_SET {drive}moves the drive andLAB_FRAME_STATEreads drive, level, who set it, the document, its load count and any error. TheFRAME_SURFACE_*actions are on the list too.LAB_A2UI_DEMOrenders thereleasesurface (apps/lab/ui/src/release.a2ui.json) as the agentdemo-agent. The pane shows it on first open.LAB_RELEASE_SHIP(Mutating), so the press waits for the person's yes in the confirmation sheet. The answer is then written back at/shipped.A2UI_PATCH {messages: [{updateDataModel: {surfaceId: "release", path: "/tasks/2", value: {title: "Notarize the build", done: false}}}]}.wantResponseandresponsePath, as the catalog spells them, were silently ignored, because a2ui-base 0.3.1 reads onlywant_responseandresponse_path.events::resolvenow accepts the catalog's spelling.apps/lab/scenarios/embedded-views.styx, 9 steps:LAB_FRAME_SET {drive 0.9}, checking the level is −0.6 dBv0.11.0 shipped to beta; 2 tasks still open.Stack: merge FastTrackStudios/frame#31 first, then this PR. There is no build dependency between them.
Checks (local, once, at 41610ca):
cargo fmt --all: 0, no diff.cargo clippy --workspace --all-targets: 0, no warnings.cargo test -p architect-a2ui -p architect-lab-ui: all passed. The lab's 13 include the newembedded::tests::the_ship_button_ships_what_the_form_says, which renders the demo twice, resolves the button and runsLAB_RELEASE_SHIPthrough the list.cargo run -p architect-lab-media -- embedded-views --test, withARCHITECT_LAB_FRAME_SURFACEset to frame#31'sframe-surface:ok embedded-views (9 steps), exit 0.mediabranch GIF updates whenjust media-publishis run.GIF: https://raw.githubusercontent.com/FastTrackStudios/architect/media/embedded-views.gif
🤖 Generated with Claude Code