ADR-504: One TUI engine on ratatui and one theme for every interactive surface¶
Summary¶
- Decided: agent-ways has three applications with separate purposes, sharing one theme.
waysis one application for settings,introspectand the plain ways output.attend-chatis an independent application and binary for people. Both run onagent-tui, an app shell on ratatui, soattend-chatis ported off iocraft for the code reuse.attendis being reduced toward an MCP server and has no TUI. Every surface that prints colour, interactive or not, draws it fromagent-theme, a theme engine with no ratatui dependency and outputs for ratatui and plain ANSI. A theme file has the structure of the operator's dotfiles palettes, which is valid TOML, so a palette file loads unchanged; agent-ways adds keys in that style only where the structure is insufficient. Themes are bundled and user-owned, previewed live in a theme tab and saved apart from settings. Shared code has one copy each: aclaude-sessionscrate locates Claude directories, projects and transcripts, and the Pythonclaude-projectsis retired for a Rustways projects;agent-themealone detects terminal colour; agent-identity's colours are the one categorical palette; a raw colour escape outsideagent-themefails a test. New tooling on these paths is Rust. Hook scripts are thin adapters over decisions the binaries make; the commands they run never touch the engine or read a theme file, within a stated latency budget.ways rethinkand its flags are removed. This amends ADR-154 §2 and §4. - Trades away: The zero-dependency compositor ADR-154 §2 chose, with its binary-size margin, and the hand-rolled
rethinkcode, which is rewritten. A theme schema shaped to agent-ways alone: its theme keys follow the dotfiles palettes.attend-chat's iocraft UI, which is rewritten. Scripts and muscle memory that still callways rethinkstop working. Theclaude-projectscommand name: its commands move toways projects. The names of attend's signal directories for project paths holding characters other than letters, digits,/,_and.. - One-way? Not strictly, but undoing it after
rethink, the settings screens andattend-chatmove means rewriting them again. The theme file format, the dotfiles palette structure plus agent-ways' added keys, becomes a contract with every user theme. Removingways rethinkcan be undone by restoring the alias, but a caller that has broken has already broken. - Probes: Confident (one look): you want the
introspectscreens, the settings app,attend-chatand the plainwaysandattendtables to read as one product, and changing the theme to change all of them. Not confident (own themes): you are content that agent-ways' themes live only in its own directory, so changing your dotfiles theme changes nothing here until you copy or edit a theme in the theme tab. - Inversion: Between each surface choosing its own library and colours, and one framework every application must render through. The theme is shared by all three applications, and the engine by the two that draw screens; each application keeps its own binary and purpose.
Context¶
The workspace has two TUIs on two foundations. rethink is about 2,900 lines of hand-rolled crossterm plus about 1,000 lines of compositor and render code; attend-chat uses iocraft. ADR-154 §2 kept crossterm and a small compositor for introspect, rejecting ratatui on binary size and on its break with the ANSI-String contract cmd/render shares with ways list, and named its own escape hatch: adopt ratatui once a surface needs multiple panes, mouse, resizing and text selection.
Colour has no shared source. Ok, warn and err exist as four separate copies using three different codes; selection is drawn three ways; only agent-identity honours NO_COLOR. Raw SGR escapes appear on 236 lines in 24 files across six crates, 138 of them in ways-cli and 56 in ways-audit. Terminal detection exists twice, agent-identity's TermCaps and the spike's ColorDepth, and the two disagree on TERM=dumb and on 256-colour terminals. Categorical colour exists twice: ways show pins clusters with ten truecolor values of its own, three of them near agent-identity's coral, teal and sky.
Locating Claude Code's data has no shared source either. tools/claude-projects is a 1,422-line Python script that lists, searches, cleans and relocates ~/.claude/projects. Transcript paths are built separately in ways-core, six ways-cli modules, attend, sensor-peers and the spike's project picker. The project-path encoder is copied into ten files under three rules: / and . become -; /, _ and . do; every character outside [A-Za-z0-9] does. attend, attend-chat and sensor-peers do not depend on ways-core, so its transcripts_root was out of their reach. introspect's session picker lists sessions from the ways event log and opens their transcripts through this path code.
Hooks run ways on every prompt, tool call and stop. On those paths ways loads its config files and, for the relevance gate, agent.yaml (ADR-503). The hook scripts are bash; check-post.sh finds and runs each way's postcheck.sh and decides what to inject itself. Claude Code mods are TypeScript functions in plugins that run in-process before, after, instead of or around tool calls, permission requests and prompts.
ways rethink, rethink --list and rethink --json have been deprecated aliases for ways introspect replay, list and dump since ADR-154 §4 and ADR-189 increment 4. Docs still cite ways rethink --json.
attend is expected to get thinner as the ways MCP server (ADR-501) takes on more of what it does. The ways agent daemon (ADR-502) runs the judge, and embedding search is to move into it next (#668).
The operator asked for a maintained library over hand-rolled code, for themes agent-ways owns as its own TOML resource without linking to the status line, for a theme tab that previews in place and saves independently of settings, and for the same themes across ways, attend and tools such as rethink. The operator answered "one app" for the settings of ways and attend, and drew the application boundaries by purpose: introspect, settings and the plain ways output are one application; attend-chat is another, for people, respects the themes and moves to the same UX framework for more code reuse; attend is being stripped toward an MCP server. Themes are agent-ways' own, in the structure of the operator's dotfiles palettes, deviating where that structure is insufficient. The operator also asked that the legacy paths into the interactive modes be retired, and that the duplicated foundations and the hook paths be settled in this decision. The operator settled on Rust for these tools, and named lightweight hooks as the start of compatibility with Claude Code mods.
A dry-run spike (tools/spikes/settings-tui on spike/settings-tui) built the shell and theme engine on ratatui: tabs, typed values, queued actions, masked entry, per-tab review and apply, an exit guard, guided flows, mouse, and a theme tab with live preview and its own save. It runs 107 tests on ratatui's TestBackend, and its frames were reviewed as PNGs. Its frames were turned into PNGs by a Python script kept outside the repository. roguemap's TUI loop (headless snapshot, PNG review, golden frames) was the model for testing.
Decision¶
- Three applications, one theme. Each keeps a separate purpose.
ways: the settings screens (§8),introspect replayandlivefirst among the interactive modes, then list, query, document and flow screens as they are built, and the plain ways output. One application onagent-tui.attend-chat: an independent application and binary for people. It is ported from iocraft toagent-tui, sharing the tabs, lists, input, mouse, theme and test kit withways; only the engine and theme are shared, and its screens and purpose stay its own. A JSON object mode for programmatic use may follow in a later decision.attend: being reduced toward an MCP server, or an MCP server mode (#538). It has no TUI, and its settings are edited fromways(ADR-503).
The ways agent daemon takes on embedding search beside the judge (ADR-502, #668), which keeps ways thin as a client.
-
ratatui.
waysandattend-chatuse ratatui with the crossterm backend. This invokes ADR-154 §2's escape hatch: the settings screens need multiple panes, mouse, resizing and guided flows, andintrospect's drill-down is the inspector §2 described. ratatui'sTestBackendrenders frames incargo test. The ANSI-Stringpanels incmd/renderstay for non-interactive output and take their colours fromagent-theme. -
agent-tui, the app shell. It provides a tab bar with pending badges, a bottom bar, the modes browse, review and apply, an exit guard across tabs, mouse with a capture toggle, confirm and masked entry, and guided flows (pick, preview, confirm). Screen shapes are tree and detail, list and detail, query and ranked results, timeline, and document. Review is a read-only mode of the browsing layout. Apply and discard act on one tab: value writes first, then queued commands in order, stopping at the first failure. The crate knows nothing aboutwaysorattend; an adapter supplies content, actions and previews. -
agent-theme, the theme engine. It has no ratatui dependency. - A theme file has the structure of the operator's dotfiles palettes (
~/.dotfiles/theme/palettes/*.theme):THEME_NAME,THEME_LABEL,THEME_KIND(lightordark) and ten slots,THEME_BG,THEME_FG,THEME_DIM,THEME_SUBTLE,THEME_ACCENT,THEME_INFO,THEME_OK,THEME_WARN,THEME_ERRandTHEME_ALT. Bare uppercase keys, quoted values and#comments make such a file valid TOML as written, so agent-ways parses theme files as TOML with these keys, and a dotfiles palette file loads unchanged with no import step. The tool bindingsTHEME_VIVID,THEME_BATandTHEME_DELTAare accepted and ignored. - Where that structure is insufficient, agent-ways adds keys in the same style:
THEME_BACKGROUND = "terminal"keeps the terminal's ground and"fill"paints bg behind every cell, defaulting toterminal;THEME_HOT,THEME_RULE,THEME_FADEDandTHEME_SELECTIONoptionally pin the derived roles. - Derived roles (hot, rule, faded text, selection) are computed unless pinned.
- Text roles are lifted to 4.5:1 contrast, and muted roles to 3:1, against bg and against the selection, by moving lightness in OKLCH and keeping hue and chroma as far as sRGB allows.
- The status roles (accent, err, ok, warn, info, hot) stay at least 0.08 apart in ΔE OK, each moved the least that clears the ones before it.
- It detects colour depth and honours
NO_COLOR, under which nothing is coloured and emphasis is reverse video. - Outputs map roles onto ratatui styles and ANSI SGR strings.
-
Identity colours and the banner gradient are not theme roles.
-
Where themes live. Bundled themes ship in the binary in the same format: a default on the agent-ways identity palette, and a few taken from the operator's dotfiles palettes. User themes live in
$XDG_CONFIG_HOME/agent-ways/themes/, with the active choice in a file beside them; a dotfiles palette copied there is a user theme as it stands, and a user file with a bundled theme's name overrides it. Nothing reads another tool's theme at runtime. -
Shared foundations. Code that every application needs has one copy.
- Claude directories, projects and sessions. A leaf crate,
claude-sessions, locates Claude Code's data: the config directory (~/.claudeor one given, such as an ADR-184 target), its projects with their sessions, a project path's directory name and the path a name resolves to on disk, and a session's transcript by id. It depends on the standard library andserde_jsononly. It is a new crate becauseattend,attend-chat,attend-groups,attend-instancesandsensor-peersneed it and none of them depends onways-core;ways-core'stranscripts_rootand transcript lookup delegate to it. Its one encoder follows Claude Code's rule, every character outside[A-Za-z0-9]becoming-, pinned by a test against directory names Claude Code wrote, and replaces the ten copies.attendnames its signal directories with the encoder too; for one release it also reads directories named under its old rule.introspect, the settings screens' project and target pickers, thewayscommands that open transcripts (context,corpus,memory-seed,list) andattenduse it. ways projects. The commands oftools/claude-projectsare reimplemented in Rust overclaude-sessionsas theways projectssubcommand group:list(with--active,--memory,--stale),search,show,stats,cleanup(with--dry-run) andrelocate(a preview by default,--executeto move). A group inwaysover a binary of its own becausewaysalready linksclaude-sessionsandagent-theme, and a separate binary would need its own release, download script and install link. The change that lands the port deletes the Python file and its link inscripts/install.sh, and updatesdocs/migration-1.0.md. The command regrouping (ADR-503 §14) may move the group underways session.cleanupandrelocatekeep their confirmation; they are the only writers into~/.claude/projects, whichclaude-sessionsitself only reads.- Terminal capability.
agent-themedetects colour depth (truecolor, 256, 16, none) andNO_COLOR.TERM=dumb, an emptyTERMand a non-emptyNO_COLORmean no colour. agent-identity'sTermCapsis removed; its callers inagent-identity,attend,attend-chatandattend-identity-viewtake the depth fromagent-theme, and agent-identity chooses between its rich and basic palettes from it. - Categorical colour. agent-identity's identity colours are the one categorical palette.
ways showpins its clusters from it, and its tenPIN_COLORSare removed. Categorical colours stay outside themes (§4). - Every coloured output goes through
agent-theme. Status colours are theme roles; categorical colours and the banner gradient are written by the same outputs. Plain CLIs use the ANSI output and screens the ratatui output.ways-cli,agent-fmt(tables, banner and permissions),ways-audit, agent-identity's ANSI rendering,attend-identity-viewandattendmove to it. A raw SGR literal (\x1b[,\033[,\u{1b}[) in a Rust source undertools/outsideagent-themeandtools/spikesis a finding: a test inagent-theme, run bycargo testandmake test, scans the workspace sources and fails naming each one with its file and line. A second test plants a literal in a fixture file and requires the scan to report it. Until the plain outputs move (§9), the scan reads an allowlist of the files that still carry literals; each step of §9 removes its files, and a file not on the list fails from the first change. - Rust. New tooling on these paths is Rust: the crates above, the
ways projectsport, the SGR check and the test kit's PNG renderer (§12). Once the port lands no Python remains on them, and no shim keeps theclaude-projectsname. Existing non-Rust tools on these paths are follow-ups outside this decision: the bash hook adapters inhooks/ways/, whose remaining logic moves into the binaries under §11. -
Note, 2026-10-02 (PR #714 review). Attend's signal trays and instance registries are not named by the bare encoder.
claude-sessions::attend_keynames them: the project slug,-, then base 36 of Claude Code's 32-bit path hash over the full path.- Why. The slug is lossy.
/srv/my projand/srv/my-projshare one, and so do two all-CJK names of one length. A shared tray delivers a directed message to the wrong project's sessions. - Limit. The hash is Java's
hashCodeform, which keeps accidental collisions unlikely but not deliberate ones: anyone who can create directories as the user can build two paths with one key. That is the same user, so it is not a privacy boundary. - Liveness. Cleanup reads the slug part, before the last
-, againstprojects/. - Transition. Trays and registries under attend's old names are moved into the new names once, at
attend runstart and inattend cleanup, and are read until #701 removes that code (ADR-506).
- Why. The slug is lossy.
-
The theme tab. It follows the settings tabs. Moving the cursor previews a theme across the whole UI; leaving the tab returns to the active theme; Enter makes the one under the cursor active. New, copy, rename, delete and edit are actions; editing a bundled theme starts a named copy. The editor shows each slot as a swatch with its hex, sliders and hex entry for the selected slot, and the contrast and distinctness results, naming any failing role. A failing check does not block saving. Theme saves are written at once or on Ctrl-S, outside the settings review; the exit guard lists unsaved theme edits beside the settings tabs.
-
One set of settings screens.
ways settingsopens them withattend's tabs besideways' (ADR-503), andways settings <tab>opens on that tab.attendhas no command that opens a settings UI. -
Adoption order. One step per change:
claude-sessions, with the path code inways-core,ways-cli,attendandsensor-peersmoved onto it, andways projectsreplacingtools/claude-projects.agent-theme's detection and ANSI output, withTermCapsremoved and its callers moved, and the SGR test with its allowlist.- The settings screens.
introspectreplay and live.- The
attend-chatport. - The plain outputs:
ways-cli's, withways showon the identity palette;agent-fmt's tables, banner and permissions;ways-audit; then what remains ofattend's. The allowlist is empty when this step lands.
The foundations go first because the settings screens' pickers and every later step use them. introspect goes before attend-chat so the shell's list, timeline and document shapes are settled on screens inside ways before a second binary depends on them. iocraft leaves the workspace when the port lands, and attend-chat follows the theme from then on.
-
Every mode keeps its CLI form. Nothing is reachable only through a TUI. Hook and script plumbing stays CLI-only and gets no screen.
-
Hooks are thin and stay off the engine. A hook script is a transport adapter: it passes the event to a binary and returns what the binary says. Every decision a hook needs (which ways fire, what to inject, the gate's verdict) is reached through one interface the binaries own, the
wayscommand's JSON output and the ways agent daemon's protocol (ADR-502). The same logic can then be called from a Claude Code mod in place of a per-event shell hook without being written again; no mod work is decided here. Logic still held in a hook script, such ascheck-post.sh's postcheck loop, moves intowaysas a follow-up. The commands hooks run areways scan prompt,scan state,scan command,scan file,scan task,scan messages,show wayandresponse-topics-pathon prompts, tool calls and stops;ways disable --listandways status --jsonon each subagent start; andattend inbox --drainon stop. These code paths never initialise a terminal, call intoagent-tuior read a theme file. Colour on them, if any, isagent-theme's ANSI output at built-in defaults. Each change in §9 that touches a crate these commands use is measured against the commit before it:- Budget: the median of each command rises by no more than 5% and by no more than 2 ms.
- Method:
hyperfinewith 20 warm-up runs and 200 measured runs per command, on one machine, each command fed a fixed fixture input (a prompt, a Bash command, a file edit, a stop), withHOMEand everyXDG_*directory pointing at a temporary fixture install so the operator's event log and settings are untouched, and with no agent daemon running so the relevance gate takes its local path. - A change over budget is fixed before it merges or carries the measurement and the reason in its PR.
-
A testing method ships with the engine.
- The crate's test half: a headless
render(state, w, h)entry point and a--snapflag; one shot list that drives both snapshots and golden tests; a frame format of glyph, colours and modifiers; a comparator that reports what drifted, exact by default, with record and dump switches; a PNG renderer for review, written in Rust inagent-tui's test support, in place of the spike's Python script. - A way,
softwaredev/code/testing/tui: render headless from the start, judge the look from an image, iterate before pinning, treat golden frames as a reviewed baseline whose re-record names the frames and the reason, cover 80x25 and a reference size, drive interaction through the real key handler. -
Skills shipped to every project:
tui-snaprenders shots to PNGs and reviews them;tui-goldenruns the golden check and, on an intended change, re-records and drafts the commit text. -
Legacy entry points are retired.
ways rethink, with its--listand--jsonflags, is removed whenintrospect replayandlivemove to the engine, with no hidden alias. Its deprecation window, opened by ADR-154 §4, has run. The one-release hidden aliases of a command regrouping cover names that are current; this one is not. The same change moves the docs that still name it:docs/explanation/how-ways-works/reading-the-session-data.md,docs/explanation/how-ways-works/how-ways-works-the-model.mdanddocs/attend-and-monitor/salience.md.
Consequences¶
Positive¶
- One theme change reaches every surface that prints colour, and
NO_COLORis honoured everywhere. - Status colours stay legible and distinct on any theme, including user themes that would fail the checks as written.
- TUI changes are reviewed as frames and pinned by golden tests.
- New screens reuse the shell's tabs, review, apply and flows.
- Project paths encode one way everywhere, so
attend's liveness check and the transcript lookups find the same directory for every path. - A new colour escape fails a test instead of landing.
- The TUI test loop and the project commands need no Python.
- Moving hook logic behind the binaries' interface lets a mod call it later.
- Hook latency has a budget each change is measured against.
Negative¶
- ratatui adds crates and binary size to
ways; ADR-154 estimated 300 to 600 KB and 15 to 30 crates. rethinkis rewritten on the new engine.attend-chat's UI is rewritten on the new engine, and its tests move to theTestBackendand golden-frame method of §12.- Until the port lands,
attend-chatkeeps its own colours and two TUI libraries remain. - A script or habit that still calls
ways rethinkfails once the alias is removed. - The theme format is a contract with user files and follows the dotfiles palettes; a slot added there needs adding here, and an added agent-ways key must not collide with a later dotfiles key.
claude-sessionstracks a layout Claude Code owns and can change; its encoder test fails when the rule moves.attendreads signal directories under two names for one release.- The latency measurement adds a step to every change that touches a hook path's crates.
Neutral¶
agent-fmttakes its colours fromagent-themeand keeps its own output form.agent-identitydepends onagent-themefor terminal depth;agent-themedoes not depend onagent-identity.claude-projectsusers runways projectswith the same verbs and flags; the old name stops working.- ADR-154 §1 and §3 are unchanged. §4's migration ends: the
rethinkalias is removed. agent-tuiandagent-themetake no dependency onattendand assume nothing about its shape; its tabs in the settings screens are an adapter that can shrink or move with its settings.
Alternatives Considered¶
- Keep
attend-chaton iocraft with a theme adapter. It avoids rewritingattend-chat, but leaves two TUI stacks whose look, input handling and tests must be kept consistent by hand, andattend-chatcould not reuse the shell's widgets or test kit. - Keep
ways rethinkas a hidden alias. It has been deprecated since ADR-154, and an alias kept through the port is one more entry point to maintain on the new engine. - Keep the hand-rolled compositor (ADR-154 §2). It meets the cases §2 scoped it to. The settings screens and the drill-down exceed them, and §2 itself named ratatui as the step at that point.
- iocraft everywhere.
attend-chatalready uses it. It brings the async runtime ADR-154 kept out of the synchronouswaysCLI, and the spike's test method rests on ratatui'sTestBackend. - A third-party colour picker or theme crate.
tui_color_picker(about 80 downloads) andtui-rgb-picker(0.1.0, about 170) are early and single-purpose;ratatui-themesships fixed palettes with no editing and no contrast checks. The slot editor and the checks are small and needed regardless. - Read the operator's dotfiles theme tool at runtime. Declined by the operator. It would tie agent-ways to one machine's setup; sharing the file structure lets a palette be copied in instead.
- A theme schema of agent-ways' own. Declined by the operator. It would need an import step for every dotfiles palette and diverge from it over time.
- A theme per tool. The present state, with four copies of the status colours.
- Put the Claude locator in
ways-core.ways-corealready hastranscripts_root, butattendand its crates would take on its YAML, markdown and corpus dependencies to reach it. - Keep
claude-projectsin Python, or wrap it. Declined by the operator in favour of Rust. It keeps a second implementation of the locator and a Python dependency. - A Rust
claude-projectsbinary. It keeps the old name but adds a release artifact, a download script and an install link for what awayssubcommand group serves. - A Clippy lint, a per-crate test or a grep script for SGR literals. Clippy has no such lint and a custom one needs a driver; a test per crate has to be added to each; a shell script is new non-Rust tooling. One workspace-scanning test covers every crate, including new ones.
Note (2026-10-01): the default theme and the 16-colour fallback¶
The operator, on the review of PR #711: "the default theme should be something that works on a conventional 16 color terminal, then any other theme can be chosen (with the 16 color theme as a fallback if the terminal isn't 256 or true color)".
The default theme is the 16-colour terminal palette: the status roles are the terminal's own ANSI colours, muted text is dim and selection is reverse video. Any other theme can be chosen. A chosen theme is used only where the detected depth is 256 or truecolor; on a 16-colour terminal the whole default theme is used in its place, not the chosen theme reduced to 16 colours. Without colour, behaviour is as §4 and §6 state. agent_theme::Painter::select holds the rule.
Note (2026-10-02): the hooks call ways hook <event>¶
§11's list of the commands hooks run is out of date. Since #702 (PR #730) every script under hooks/ways/ is an adapter that runs ways hook <event>, which reads the hook's JSON payload on stdin. The events are prompt, state, command, file, task, post-tool, queued, stop, subagent-start, session-start and tasks-active; they reach the same scan and show code §11 names. The postcheck loop §11 left in check-post.sh as a follow-up now lives in the binary as the post-tool scan. ways show way --budget-used and --subagent, and ways response-topics-path, which only hooks called, are removed. attend inbox --drain on Stop is unchanged.
Note (2026-10-01): the active theme is a settings key¶
The settings screens (#697) settle §5's active choice. It is the key theme.active in the user config.yaml, in ways' schema (ADR-503), beside theme.shape, which names the lozenge caps. The theme tab writes it through the settings writer, as ways settings set theme.active <name> does. A reader takes the name from the registry and passes it to agent_theme::Painter::named; agent-theme reads no settings file. terminal names the 16-colour default. The provisional themes/active file of #711 is not read, and there is no transition read (ADR-506). The theme section is not among the sections the hook commands load (§11).
Note (2026-10-02): views, and the adapter may refuse one¶
§3 lists what an adapter supplies. Since #743 (PR #757) it may also supply views. An action with Arg::View(name) queues nothing. The shell calls Adapter::view(name, pending), where pending holds the store of each pending value edit, and reloads the tree when the adapter switches. The adapter refuses, with a reason the bottom bar shows, a view in which a pending edit would no longer show, since the reload would drop it; nothing changes then. Adapter::title(tab) names the view in the tree pane's title. The settings screens use this for the ways tab's switch between this project's ways and every known project's.
Note (2026-10-02): screens that are not a settings tree stay on Screen¶
The settings shell (App) holds a tree of settings, a queue of edits and commands, and the apply that runs it. The session screens show a timeline, and attend-chat shows a message feed; neither has values to edit or an apply to run. Both stay on the Screen trait of §3 (#738, #749). The settings screens share their theme, panic hook, terminal guard and test kit with these screens already. The other conventions those screens lack, a footer built from declarations and mouse capture (#739), come as helpers a Screen calls. ways session is the first Screen with tabs (#738): a digit picks a tab, and each tab is named with its digit, as the settings screens pick theirs.
Note (2026-10-02): a screen that is not a tree is a pane inside App¶
This revises the note above for attend-chat (#749). The operator accepted the reversal on 2026-10-03.
The shell's conventions are the tab bar, the bottom bar with a footer read from declarations, the mouse with its capture toggle, the exit guard, the key help, the response modal and the picker. None of them needs the settings tree, so App hosts a screen that has none. App::with_pane takes a Pane, which does the following:
- lists its tabs;
- draws the area between the bars;
- handles the keys the shell leaves it;
- declares its keys as
Bindings, which the footer and the key help both read, with the help key first; - opens the response modal or a picker through
Pane::take_open, the picker's choice coming back throughPane::picked; - chooses whether the mouse starts captured.
The tree, the queue and the apply stay as they were, and a pane app has no theme tab. The pane's tick, the shell's tick during an apply or a reading, and the file watch each run on their own schedule.
A pane that owns text, such as a compose box, types every plain key, so the shell's keys take their Alt form beside it: Alt+1-9 for a tab and Alt+m for the mouse. F1 opens the keys. Esc and Ctrl-C quit, asking first over unsaved work. The exit guard there takes no letter as an answer, since letters are typing. D arms the quit, and Enter, or Ctrl-C again, confirms it. Unarmed, Esc and Enter go back to the work, and a character or an editing key (Backspace, Delete, the arrows, Home, End) closes the guard and acts on the work. Armed, Esc disarms it, and a character or an editing key first types the arming D and then acts. So keys typed after an accidental Esc reach the draft as typed. The settings guard keeps D then y.
A chat starts with the mouse off, so the terminal selects text and middle-click pastes. The shell captures clicks, the wheel and drags, but not bare moves.
End state: screens that are not a tree move onto panes as they are touched. attend-chat is the first; ways session is next (#739), which gives it the mouse from the shell. Until the tree itself is a pane, App has two paths, one for the tree and one for a pane (#805). Bracketed paste for a pane's text entry is #806.