# MixOS — the whole manual > MixOS is an agent-operable computing system: a Linux userland where every app is a service, every service speaks one message bus, and Mix is the shell. It is tried as a small signed systemd-nspawn image that runs on its own virtual terminal beside the user's existing desktop, at native GPU, input and audio speed. Generated from the Markdown pages of https://mixos.dev/ in sidebar order. It describes the current `main` branch. Index: https://mixos.dev/llms.txt --- # Docs Everything on mixos.dev that is not one component's documentation lives here. A component's page is at `//`, for example [`/mix/`](../mix/README.md) for the Mix language or [`/term/`](../term/README.md) for the terminal. Every page is also published as Markdown at its source path, and [`/llms.txt`](https://mixos.dev/llms.txt) lists them all. The [site URL layout](decisions/2026-10-16-site-url-layout.md) decision fixes these addresses. ## Guides - [Try it beside your desktop](try.md): run MixOS as a small signed image on its own virtual terminal, next to the desktop you already use. - [Driving apps over the Bus](drive.md): how an agent drives a native MixOS app through Bus verbs. ## Indexes - [Components](components.md): every component, with its kind, install profiles, source and page. - [Desktop](desktop.md) and [Server](server.md): the components each install profile includes. ## Decisions Public decision records are the authority for the MixOS tree. The foundational ones: - [Source layout and naming](decisions/2026-10-04-source-layout.md) - [egui applications, engine first](decisions/2026-10-09-egui-applications.md) - [Commands take parameters and return results](decisions/2026-10-20-commands-take-parameters.md) - [One repository, install profiles](decisions/2026-10-12-one-repository-install-profiles.md) - [One system binary](decisions/2026-10-12-one-system-binary.md) - [Site URL layout](decisions/2026-10-16-site-url-layout.md) Every decision record is listed in [`/llms.txt`](https://mixos.dev/llms.txt) and in the [repository](https://github.com/markc/mixos/tree/main/docs/docs/decisions). ## Specifications - [Bus verb registry](https://github.com/markc/mixos/blob/main/docs/docs/spec/bus/verbs.conf.mix) - [Bridge](spec/bridge/README.md) - [Portal](spec/portal/README.md) - [Releases](spec/releases/README.md) - [Settings](spec/settings/README.md) - [Mix programs and documents](spec/mix/2026-10-08-programs-and-documents.md) - [Handler closure capture](spec/handler-closure-capture.md) --- # Mix — manual The reference manual for the **Mix** language, one page per topic. The canonical home of these pages is [`docs/mix/`](https://github.com/markc/mixos/tree/main/docs/mix) in the public [markc/mixos](https://github.com/markc/mixos) monorepo; add a page by dropping `TOPIC.md` there. The same files render everywhere: - **Terminal** — `mix man TOPIC` (`mix man` alone prints this index). Every page is built into the `mix` binary, so it needs no checkout and no network; the lookup order and its overrides are in [the mix command](cli.md). - **Web** — [mixos.dev/mix](https://mixos.dev/mix/overview) serves this directory. - **GitHub** — browse [`docs/mix/`](https://github.com/markc/mixos/tree/main/docs/mix) directly; this file doubles as the directory README. - **Local clone** — plain markdown with relative links; any editor or viewer works. **New to Mix, or coming from bash/Python/JS? Read [gotchas](gotchas.md) first** — it is the errata against every other language's reflexes, and every row of its table is executed by `cargo test -p system --test man_gotchas`, so it cannot rot. `mix man syntax` is the mental model; gotchas is the corrections. ## Start here - **[overview](overview.md)** — what Mix is, why it exists, where it came from. - **[invocation & CLI](invocation.md)** — `mix file`, `-c`, `-`, `-i`, login shell, `--serve`, flags. - **[the mix command](cli.md)** — `mix help`/`man`/`builtins`/`what`/`status`/`trace`/… meta-commands. - **[syntax & the classifier](syntax.md)** — tokens, the newline rule, shell-vs-Mix dispatch. - **[gotchas](gotchas.md)** — the guesses from bash/Python/JS that are silently wrong here. Read it first. ## The language - **[variables, sigils & scope](variables.md)** — `$` sigils, `${...}`, function-local binding. - **[strings](strings.md)** — `'raw'` vs `"interp"`, `..` concat, codepoint/byte/grapheme ops. - **[numbers](numbers.md)** — f64, radix literals, ordering, coercion. - **[operators](operators.md)** — arithmetic, comparison, `and`/`or`/`not`, `..`, `?:`, `??`. - **[control flow](control-flow.md)** — `if`/`while`/`for`/`loop`, if-as-expression, `break`/`continue`. - **[functions, lambdas & modules](functions.md)** — `fn`, closures, the pass-in/return/reassign triad. - **[modules — require/include/source](modules.md)** — `require()` isolated module loading vs the splice loaders. - **[lists & maps](collections.md)** — literals, indexing, `push`/`pop`, the base footguns. - **[higher-order functions](hof.md)** — `map`/`filter`/`reduce`/`sort_by`/`group_by`/… - **[errors & exit handling](errors.md)** — `try`/`catch`/`die`/`panic`, `run_rc().rc`, timeouts. ## Builtins & I/O - **[math](math.md)** — rounding, powers, logs, trig, `min`/`max`/`clamp`, constants. - **[files & I/O](io.md)** — `read_file`/`write_file`/`glob`/`stat`/`chmod`/`walk`/… - **[processes & system](system.md)** — `run`/`run_rc`/`run_stream`/`spawn`/`env`/`exit`/… - **[job control](job-control.md)** — foreground/background jobs, suspension, resume (interactive). - **[data & serialization](data.md)** — JSON, TOML, `jq`, `data_encode`, strict-data `.mix`. - **[byte buffers](buffer.md)** — `buffer`/`buffer_push`/`freeze`, the one reference-semantic type. - **[regular expressions](regex.md)** — `re_match`/`re_find`/`re_replace`/`re_split`/`grep_lines` (subject first). - **[dates & time](datetime.md)** — `time`/`date_format`/`now_iso`/`duration_format`. - **[http](http.md)** — `http_get`/`http_post`/`http_request`, deadlines. - **[datastar](datastar.md)** — `ds_*` SSE event framing. - **[archives](tar.md)** — `tar_list`/`tar_unpack`/`tar_pack`, zstd/gzip/none, safe staged extraction. - **[SQLite](sqlopen.md)** — `sqlopen`/`sqlexec`/`sqlclose`, read-only by default, `"rwc"` creates. ## Mesh & runtime - **[Bus messaging](bus.md)** — `send`/`emit`/`address`/`on`, `$result`/`$rc` bands, the mesh. - **[serving as a citizen](serve.md)** — `mix --serve service.mix`, the supervised runtime. - **[remote execution (ssh)](remote.md)** — `ssh_run`/`ssh_must`/`ssh_mix`, env transports. - **[capabilities & embedding](capabilities.md)** — the capability classes + sandbox model. - **[expression evaluation mode](expr-eval.md)** — `eval_expr_string()`, the single-expression rule, the static deny walk. ## Reference - **[builtin index](builtins.md)** — every builtin by category. - **[lint diagnostics](lint.md)** — every MIX-XXXX code and its story. - **[reserved words](keywords.md)** — the keyword set, keywords-as-names. - **[shell-dispatch mode](shell-mode.md)** — pipes, `&&`, brace expansion, `$(...)`, redirects. - **[owned interactive editor](owned-editor.md)** — `MIX_EDITOR=owned` (preview). - **[usage statistics](stats.md)** — runtime modes, report windows, persistence, kill switch, static coverage. ## Where Mix lives Mix is the MixOS shell and scripting language. The interpreter lives in the `mix` library at `libs/mix`; `cli/mix-shell` owns the interactive shell and native ABP citizen runtime, and `cli/system` builds the `mix` binary. The source versions are declared in [`libs/mix/Cargo.toml`](https://github.com/markc/mixos/blob/main/libs/mix/Cargo.toml) and [`cli/mix-shell/Cargo.toml`](https://github.com/markc/mixos/blob/main/cli/mix-shell/Cargo.toml). Use `mix --version` to identify the installed binary, which can differ from the current source. Use `mix builtins --names` to discover valid builtin names, and `mix builtins ` for their contracts. `mix man overview` and `mix man syntax` explain the language and command classifier. Discovery should inspect process status rather than stop a sequence when a lookup is invalid. Mix scripts use `run_argv` for local processes and native Bus verbs for application control. `run_argv_must` is appropriate when a non-zero exit must stop the script. The public language regression corpus accompanies the interpreter transplant. ## Programs and documents The accepted target for the next major Mix refactor and deployment is two file families: | Extension | Meaning | | --- | --- | | `.mix` | Executable Mix program: macro, behaviour, service, module, installer or test | | `.mx` | Declarative Mix document: configuration, scene, theme, catalogue, manifest or data specification | All program roles use the same language. Documents use strict parsing and an owning schema where applicable. Only declared binding fields may evaluate the existing restricted, side-effect-free expression subset. Data strings otherwise remain literal. A schema is a contract/validator, not necessarily another file. This is an **accepted target, not a completed runtime migration**. Current consumers still use `*.conf.mix`, `*.spec.mix`, `*.data.mix`, `scene.mix` and other legacy data names. Keep their working paths until upgraded readers are deployed. Do not rename executable helper modules such as a scene library's `data.mix`. The refactor must refuse `.mx` through executable file loaders and retain strict document errors without code/shell fallback. Scenes keep their current envelope and pure bindings initially. Readers upgrade before files and writers; rollback includes compatible binaries, scripts and documents. New purposes use existing programs or versioned document schemas. Another file family or evaluation mode requires an architectural decision. See the [decision](https://mixos.dev/docs/decisions/2026-10-08-mix-programs-and-documents/) and [file-family contract](https://mixos.dev/docs/spec/mix/2026-10-08-programs-and-documents/). ## Exit status Uncaught script errors print a diagnostic and exit 1, including messages with `interrupted:false` or the word `interrupted`. Signal outcomes come from actual SIGINT/SIGTERM delivery: non-interactive scripts and `-c` exit 130/143. SIGINT on an interactive terminal retains exit 0 unless `--result-fd` is used. A caught interrupt still takes precedence over the evaluation's result; `--result-fd` emits an error frame rather than a successful value. Script-defined error codes do not impersonate OS signals; `--serve` retains its graceful shutdown behaviour. ## Design docs & specs Public contracts and accepted decisions live under [`docs/docs/spec`](https://github.com/markc/mixos/tree/main/docs/docs/spec) and [`docs/docs/decisions`](https://github.com/markc/mixos/tree/main/docs/docs/decisions). Private deployment records are not part of the public manual. Accepted decisions can describe staged work; check the current implementation and installed binary when a feature's availability matters. ## The library The `mix` component is the language library ([`libs/mix`](https://github.com/markc/mixos/tree/main/libs/mix)): lexer, parser, evaluator, builtin contracts and embedded handler interfaces. Features select the optional data, networking and cryptographic facilities; the interpreter's core stays independent of the Bus and desktop. It was transplanted from `mix` at mixos `e0297242305f3a3c3de09f1ca01e8faa771768da`, retaining its regression corpus and public language version. The shell is [mix-shell](https://mixos.dev/mix-shell/), and the `mix` binary is built by [system](https://mixos.dev/system/). ## Changes - **[changelog](changelog.md)**: the Mix library's change history, generated from `libs/mix/CHANGELOG.md`. ## See also ``` mix help the full categorized builtin reference mix builtins [CAT] list builtins, optionally by category mix what NAME one-line description of a builtin or keyword mix man TOPIC read any of the pages above in the terminal ``` For AI agents: [`AGENTS.md`](https://github.com/markc/mixos/blob/main/AGENTS.md) at the repo root is a short orientation sheet whose canonical reference is this manual. --- # Try it beside your desktop Keep the Linux desktop you already use. MixOS runs next to it on another virtual terminal (VT): your desktop stays on VT1, MixOS takes another VT, and you flip between them with **Ctrl+Alt+F1** and **Ctrl+Alt+F5**. Both keep running. This is how MixOS is meant to be tried, and it shapes the whole design. > **Status:** the mechanism runs daily on the developers' machines. The > download and the one-command installer are being built. This page > describes what they will do. ## Why not a virtual machine? VirtualBox, virt-manager, Incus and Proxmox show a guest desktop in a window, through a virtual GPU and emulated or remote-display input. You can feel that, and a desktop is exactly the kind of software where you notice. Getting native graphics out of a VM means passing through a second GPU, which most people can't do. On its own VT, MixOS drives the **real GPU, keyboard, mouse and speakers**. There is no hypervisor, virtual GPU, remote-display protocol or second kernel in the way. What you try is what you would get. ## How it works Three things already in the Linux kernel and systemd do the work: 1. **Virtual terminals.** Each VT is a separate console. When you switch, the kernel asks the current owner to let go before handing over. 2. **GPU and input handover.** Only one program can drive the display at a time. Your desktop's compositor is managed by `systemd-logind` on VT1; MixOS's compositor is managed by `seatd` on its own VT. Each lets go of the display and input devices when its VT goes to the background, and takes them back when it returns. 3. **Separate sessions.** MixOS keeps its own runtime directory and its own session bus, so nothing it runs collides with your desktop's. The container is what makes it a download: `systemd-nspawn` runs MixOS as a self-contained machine, `importctl` fetches and verifies the signed image, and `machinectl` starts, stops and removes it. ## What you need - A mainstream Linux distribution running systemd: Ubuntu, Fedora, Debian, Arch, openSUSE or Mint, among others. - `systemd-nspawn`. Debian-family systems need the `systemd-container` package; most others already have it. - Intel or AMD graphics. NVIDIA's proprietary driver needs extra work and comes later. - `sudo`, for the one install command. ## What the installer will do 1. Check your system: systemd and kernel versions, GPU vendor, and a free VT. 2. Fetch the signed MixOS image and verify its signature. 3. Describe the machine in one small configuration file: which GPU, which VT, your audio, and a separate home directory with chosen folders shared. 4. Reserve the VT so no login prompt starts there. 5. Start MixOS and switch to its VT. If MixOS doesn't come up in time, it switches you back to VT1 automatically. Removal undoes every step and leaves your system exactly as it was. ## Things to know first - **It is not a sandbox.** MixOS gets your real input devices and GPU, so trust it as you would any package you install as root. - **Your desktop keeps running** in the background, and so does everything in it. While MixOS is in front, the installer stops your desktop from deciding you have gone idle and suspending the machine. - **Apps can't be in both places at once.** Firefox and Thunderbird already open on one desktop refuse to open the same profile on the other. That is their profile lock protecting your data, not a fault. - **If a screen ever goes black,** SSH in from another machine, or use Alt+SysRq+R followed by Ctrl+Alt+F1 where your kernel allows it. ## For agents Every page on this site is static HTML, and also published as Markdown: this page is [`/try.md`](/docs/try.md). [`/llms.txt`](/llms.txt) indexes the whole manual and [`/llms-full.txt`](/llms-full.txt) is all of it in one file. --- # Driving apps over the Bus MixOS is agentic first: anything a person can do in a native app, an agent can do over the Bus. Every egui app built on the shared toolkit offers two sets of verbs: - **Its own verbs**, which read and change its state directly, such as BusViewer's `busviewer.filter` or `busviewer.call`. These are the fastest and most precise way to work with an app. - **The drive verbs**, which are the same in every app and work the window the way a hand does: read the widget tree, click, type, press keys, scroll, open menus, move the window and take a screenshot. The drive verbs are named under the app's own prefix: `.ui.tree`, `.ui.click`, `.window` and so on. `HELP` and `app.describe` list all of an app's verbs with their arguments, both sets included. ## Replies and refusals A verb answers with return code 0 and a JSON body, or with return code 10 and a body naming the reason: ```json {"error_code": "ARGUMENT", "message": "give exactly one of label and id"} ``` | `error_code` | Meaning | |---|---| | `ARGUMENT` | The arguments are wrong, or name something that is not there. | | `UNKNOWN_VERB` | The app has no such verb. | | `BUSY` | Not now: work is in flight, a dialog is open, the window is hidden, no frame has been drawn yet, or the app is closing. | | `CANCELLED` | Input that was accepted but never delivered: the window was hidden or minimised first, or the app closed. | | `CAPTURE` | A screenshot could not be taken or written. | | `UNAVAILABLE` | The app does not offer the drive verbs. | An app's own verbs may add codes of their own; BusViewer's are listed [below](#busviewer-the-worked-example). ## The drive verbs Coordinates and sizes are in points, egui's logical pixels: the same space for every verb, at any display scale. ### `.ui.tree` The widget tree, from egui's AccessKit output for the last frame, depth first from the root. - **Arguments:** `label` (optional, a case-insensitive substring of the label), `exact` (optional; `true` makes `label` match the whole label, case and all, so "Close" does not also list "Close All") and `role` (optional, an AccessKit role name such as `Button`, `TextInput` or `MultilineTextInput`). Only matching nodes are listed. - **Answer:** `{root, focus, nodes}`. Each node carries: - `id`: the node id, as **decimal text**; - `role`, `label`, `value` and `placeholder`; - `rect`: `[x0, y0, x1, y1]`; - `enabled`, `focused` and `selected`; - `checked`: `"true"`, `"false"`, `"mixed"` or null; - `children`: the child ids; - `depth`. - **Refused** with `BUSY` until the first frame has been drawn. Node ids are 64-bit numbers, and most JSON readers keep only 53 bits of a number. Every id in a drive answer is therefore text, and should be passed back as text. For a plain text label, egui puts the text in the node's value; the drive verbs report it as the `label` too. ### `.ui.click` **Each step is one frame.** Input verbs run one pass per step and answer a pass after their last, so their time is the window's frame interval. A session on a background VT, or a window without focus, may be drawn only once a second: there a `ui.click` (move, click, redraw) takes about two to three seconds, and a `ui.type` a pass per line and per newline. Expect that wait; it is not a hang. Moves the pointer to a node's centre in one pass, then presses and releases there in the next, as a quick hand does within one frame. Passes can be far apart (a compositor may draw an unfocused window only once a second), and egui takes a release more than 0.8 s after its press as a long press, not a click, so the two never wait for separate passes. A separate `ui.pointer` press and release are two passes, and may be a long press for that reason. - **Arguments:** - exactly one of `label` (exact text) or `id` (decimal text, or an exact integer); - `button`: `primary` (the default), `secondary` or `middle`; - `double`: true for a double-click. - A `label` matches nodes whose label is exactly that text. If none has it, nodes whose placeholder (hint text) is that text match instead. - A control (a button, link, checkbox, field, slider, menu item and so on) wins over text that repeats its name, such as its own tooltip or a label beside it. - When several nodes still match, the click is refused with `ARGUMENT`, listing their ids; click one of them by id. - **Answer:** `{target, at, menu, focused, pointer}`. `target` is the node that was clicked and `at` the point. The rest is the state the click left: the open menu (as `ui.menu` reports it), the focused node and the pointer position. - **Tracing:** every input verb (`ui.click`, `ui.pointer`, `ui.scroll`, `ui.key`, `ui.type`) takes `trace: true`, and its answer then carries `passes`: one entry per pass the job ran, with every input event egui read in it (the platform's own and the injected ones, in order), egui's input `time` for the pass in seconds, and what egui made of them: `clicked`, `drag_started`, `dragged`, `drag_stopped`, `hovered` and `contains_pointer` (widget ids, the same decimal text as node ids), the `pointer` position, whether a button is `down`, and `no_longer_a_click` (the pointer has moved too far since the press for its release to click). Use it when a click answers but does nothing. ### `.ui.pointer` One pointer event at a point. - **Arguments:** `x`, `y`, `action` (`move`, `press` or `release`) and `button` (optional, as for `ui.click`). - **Answer:** `{menu, focused, pointer}`. Press and release are separate requests, so an agent can drag, or open a menu on press the way a person does. ### `.ui.scroll` Moves the pointer to `x`, `y` and scrolls there by `dx`, `dy` points, with egui's signs: a positive `dy` moves the content down, revealing what is above. egui animates a scroll over a few frames, so the answer may arrive before the content has finished moving. ### `.ui.key` Presses and releases one key. - **Arguments:** `key`, an egui key name (`Enter`, `Escape`, `Tab`, `ArrowDown`, `F1`, `A`, …), and `modifiers` (optional), a list of `ctrl`, `shift`, `alt` or `command`. Off macOS, `command` is Ctrl. - **Answer:** `{menu, focused, pointer}`. A key with modifiers triggers the app's shortcuts exactly as the keyboard does. ### `.ui.type` Types `text` into the focused widget. A newline in the text presses Enter. Focus a field first, for example with a `ui.click` on it. ### `.ui.menu` The open menu: `{menu: {menu, path, depth}}`, or `{menu: null}` when no menu is open. - `menu`: the open menu's title. - `path`: the highlighted row's label at each open level, null where no row is highlighted. - `depth`: the level the keyboard acts on. ### `.ui.menu.list` Every menu, as the menu bar draws it: `{menus: [{title, rows}]}`. A row is `{label, id, shortcut, enabled, checked}`, with `submenu` holding the rows of a submenu row (whose `id` is null), or `{separator: true}`. ### `.ui.menu.open` Opens a menu, and submenus within it, by label. - **Arguments:** `path`, a list: a menu title, then the label of a row at each level below it. Every row but the last must open a submenu. Or `menu`, a title alone. Case and a trailing "…" don't matter. - **Answer:** the open menu, as `ui.menu` reports it. The last row named is highlighted, and the keyboard acts on the deepest open level, so a `ui.key` Enter then runs that row. - **Refused** (`ARGUMENT`): no path, a title or label the menus don't have, a disabled row, or a row before the last that opens no submenu. ### `.ui.menu.close` Closes every open menu. Answers `{closed}`: whether one was open. ### `.ui.capture` A screenshot of the window, written to a new PNG file. - **Arguments:** `name`, optional (see the rules below). - **Answer:** `{path, width, height}`, with the size in physical pixels. Captures are confined to the app's own directory: - Every capture goes to `$XDG_RUNTIME_DIR//captures/`. The app creates that directory, mode 0700. Each of its two levels must be a real directory; a symbolic link there refuses the capture. - A caller gives at most a file name, never a path. The name must: - end in `.png` and be at most 128 bytes; - contain no `/`, `\`, NUL or `..`; - not start with a dot. - Without a name, the app picks a fresh `capture--.png`. - A name that already exists, as a file or as a link, is refused with `ARGUMENT`. The file is created exclusively, mode 0600, so a link planted while the capture waits fails the capture (`CAPTURE`) instead of being followed. - Without `XDG_RUNTIME_DIR` there is nowhere to capture to, and every capture is refused. - The screenshot is waited for for 10 seconds at most, then the capture is refused with `CAPTURE`. ### `.window` A window command, sent the way the title bar's caption buttons send it. - **Arguments:** `action`: `minimize`, `maximize`, `restore`, `close` or `focus`. - **Answer:** the window state (as `window.state` reports it) plus `requested`, the action. - `maximize`, `restore` and `focus` answer two frames later, with the state the window then reports. - A minimised window draws no frames, so `minimize` first settles any input still queued (see [Ordering](#ordering)), then answers at once. - `close` answers at once. The app then closes as if its close button had been pressed, finishing work it has accepted first. ### `.window.state` `{size, outer_size, maximized, minimized, fullscreen, focused, pixels_per_point}`, where `size` and `outer_size` are `[width, height]`. ## Ordering The drive verbs are built so that a caller can step an app deterministically. - **Input runs one request at a time, in order.** Each step of a click, key or text is its own frame: for a click, the move, the press and the release. The answer comes one frame after the last step, so it reports what the input changed. - **Later requests wait for earlier input.** The app holds each Bus command in arrival order. Further input (`ui.click`, `ui.pointer`, `ui.scroll`, `ui.key`, `ui.type`, `ui.capture`) joins the input queue directly. Every other verb waits until the input before it has been answered: reads such as `ui.tree`, `window` commands, and the app's own verbs. So `ui.type` into a field followed by an app verb that reads that field sees the typed text. - **A hidden window draws no frames.** While the window is minimised or covered, new input is refused with `BUSY`. Input already accepted is settled: - a request that only had its redraw frame left is answered normally; - anything with input still to send is answered `CANCELLED`. A click cancelled after its press, but before its release, has its button released safely when the window returns. The pointer first moves away, so the release can never complete the click. - **Closing answers everything.** Before an app closes its Bus connection, every request it has accepted gets an answer: - input in progress: return code 0, with `closing: true`, and `interrupted: true` when part of it was never sent; - queued input and captures: `CANCELLED`; - every other request, including any still arriving: `BUSY`. ## BusViewer: the worked example Native `.commands` responses report `shortcut` and `more` using stored keymap chords, with modifiers in Ctrl, Alt, Shift order. Key names follow the keymaps grammar (`ArrowLeft` or `Left`, `=`, `-`, `/`), so reported chords can be used in keymap overrides without translating display labels. Command execution returns `DENIED` for an authorisation refusal and `DISABLED` when the command cannot run in the current state. Authorisation is checked first. BusViewer browses the services and verbs on a node and calls one verb with a JSON body. Everything in its window is also a Bus verb. ### Reading | Verb | Arguments | Answer | |---|---|---| | `busviewer.ping` | | `{schema, version}` | | `busviewer.info` | | the whole state: connection, discovery snapshot, selection, body, last reply, status, and the arranged UI (`ui`) | | `busviewer.tree` | | `{rows}`: the rows the tree shows, depth first, each with `key`, `kind`, `label`, `depth`, `expanded`, `selected` and its child count; verb rows add `selection`, error rows `error` | | `busviewer.reply` | | `{text, value}`: the reply panel and the last reply | | `busviewer.commands` | | every menu command with its label, shortcut, further chords (`more`, when it has any) and whether it is enabled now. The shortcuts are those in force: the desktop profile can rebind them (`settings.apply` `keymaps.busviewer.`) | | `HELP`, `app.describe` | | every verb, with arguments | A row's `kind` is `service`, `verb`, `error`, `peers`, `peer` or `no_peers`. Its `key` names it in the verbs below, for example `service:noded` or `verb:noded:noded.list`. ### Changing state | Verb | Arguments | Notes | |---|---|---| | `busviewer.filter` | `text` | Filters the tree, as typing in the search field does. Answers with the rows. | | `busviewer.expand` | `key`, `open` (bool) | Opens or closes a row that has children. | | `busviewer.select_row` | `key` | Selects any row. A verb row also selects its verb. | | `busviewer.select` | `service`, `verb` | Selects an advertised verb. | | `busviewer.body` | `text` | Sets the JSON body (at most 65,536 bytes). Refused while a call is using the body. | | `busviewer.split` | `value`, 0 to 1 | The services pane's share of the window, kept between 0.2 and 0.65. The answer reports the value in effect, to 4 places. | | `busviewer.dialog` | `open`: `"about"`, `"shortcuts"` or null | Opens a dialog, or closes the open one. | | `busviewer.call` | `service`, `verb` and `body`, each optional | Calls the selected verb once, or the given one; `service` and `verb` go together. The answer is the reply: `{service, verb, request_body, rc, body}`. A transport failure is never retried: it answers return code 10 with `transport_error` and `outcome_unknown`. | | `busviewer.theme` | `scheme`, `style`, `mode`, `decorations` and `caption_side`, each optional: a name, or null | Chooses the window's theme, in this window only. A present key sets that axis (null follows the session's), an absent one leaves it, and an unknown name is refused with `ARGUMENT`; nothing changes unless every axis is valid. Schemes: `ocean`, `crimson`, `stone`, `forest`, `sunset`, `mono`, `pro`, `studio`, `classic`; styles: `plain`, `pro`, `studio`, `classic`, or `own` for the scheme's own; modes: `light`, `dark`; decorations: `csd` (the app's title bar), `ssd` (the compositor's, with the menus in a menu-bar row); caption sides: `right`, `left`. The answer is `{scheme, style, mode, decorations, caption_side, effective: {…}}`: the choice, then what the window shows. Taken while a dialog is open. | | `busviewer.execute` | `id` | Runs one command, from the menus or the title bar: `file.refresh`, `file.quit`, `edit.format`, `edit.clear`, `edit.copy`, `bus.call`, `help.shortcuts`, `help.about`, `view.search`, `view.mode` (the light/dark switch), and View › Theme's choices `view.theme.`, `view.theme.session`, `view.mode.light`, `view.mode.dark`, `view.mode.session`, `view.style.own`, `view.style.