Decisions and open questions
The choices behind the terminal specification, and the questions it left open. Numbering continues from the Android page's D1–D5 (android.md). A decision's state is proposed, accepted or superseded; every accepted row was chosen by the repository owner from the alternatives it lists.
| ID | Question | Alternatives | Choice | Revisit when | State |
|---|---|---|---|---|---|
| D6 | How is the selection menu drawn without Java? | native D overlay; enlarge the hidden EditText and use the system floating toolbar | native: the system toolbar acts on the EditText, not the terminal, and cannot be extended without an ActionMode.Callback (Java, D2) | — | accepted |
| D7 | Where does the configuration live? | config.json over the Termux files; the Termux files only | config.json (wired) with the Termux files as a compatibility layer below it (TCF2) | — | accepted |
| D8 | Delivery shape | a spec PR then a stack; specs only; one branch | one branch, one PR | — | accepted |
| D9 | Tabs and splits on a phone? | both on both platforms; splits desktop-only | both, on both — a landscape phone or a tablet has room | — | accepted |
| D10 | How is the UI's look decided? | specify it in prose; HTML mockups first | 2–3 HTML mockups per surface in Claude Design; layout rows stay open until one is chosen (design) | — | accepted |
| D11 | Order of work | mockups then UI, non-visual work meanwhile; mockups only; one prototype | specs + mockups first; non-visual work in parallel; visual UI after the choice | — | accepted |
| D12 | What does a tap on a link do? | confirm bar; open at once; long-press menu | configurable (links.tap, links.longPress), default confirm | — | accepted |
| D13 | Which schemes may Open launch? | http(s)/mailto + config; anything after confirming | allow-list http, https, mailto + links.schemes | — | accepted |
| D14 | When does OSC 9/99/777 become a system notification, and where? | only if unseen; always; per pane. Android only; Android and desktop | only if unseen, otherwise a toast; always logged. Android and desktop (freedesktop D-Bus; macOS later) | — | accepted |
| D15 | How does a notification tap find its pane on Android? | spike then fall back to the log; always the log; a Java onNewIntent shim | spike first; fallback: one notification while away → its pane, else the notification log. A Java shim would break D2 | the spike's result | accepted |
| D16 | Light and dark terminal colours | a config pair, Termux file pins; built-in themes by name; follow without a light default | appearance.colors.dark/.light, built-in pair by default; colors.properties pins one scheme | — | accepted |
| D17 | What colours does the chrome use? | derived from the terminal's fg/bg; a fixed built-in theme | derived (Theme.effectivePalette, THM7); appearance.chromeTheme overrides; the Sparkles brand becomes the default if SPK3 lands | SPK3 | accepted |
| D18 | Which keys does the terminal claim on the desktop? | Ctrl+Shift chords + leader; zellij-style modes; tmux-style prefix only | Ctrl+Shift chords for the common actions + a leader (Ctrl+Shift+Space) opening the guide (TKM) | — | accepted |
| D19 | What does a pane do when its program exits? | prompt on failure, close on 0; always prompt; close shells, prompt commands | promptOnFailure by default; explicit commands always prompt (TSS1) | — | accepted |
| D20 | Do tabs and splits survive a restart? | layout + directories; nothing; layout + re-run commands | layout, titles and directories, fresh shells; command panes at the prompt | — | accepted |
| D21 | What is a tab's icon? | OSC 1 as a glyph string; process-derived only; text only | OSC 1 string (≤ 2 cells), else from the foreground process name | — | accepted |
| D22 | Paste safety without bracketed paste | confirm multi-line; never confirm | confirm multi-line (paste.confirm) | — | accepted |
| D23 | Kitty keyboard and the soft keyboard | text as synthetic key events; legacy for soft keyboard | synthetic press+release where a key exists, associated text otherwise | — | accepted |
| D24 | What does the log hold, and for how long? | ring + file; ring only; include logcat | the app's log in a ring and a rotating file; never typed or pasted text | — | accepted |
| D25 | Tabs and splits by touch | strip + swipe + menu; a Termux-style drawer; strip only | strip, two-finger swipe, long-press menu; splits from the guide | the mockups (D10) may refine the shape | superseded by D42 |
| D26 | Showing and hiding the extra keys | follow the keyboard + swipe; Termux's Volume-Up+Q; setting + command only | auto follows the soft keyboard; swipe to hide/show; a command | — | accepted |
| D27 | The settings pane: lift or copy? | lift hue's into sparkles:ui; copy for the terminal | lift; hue moves onto it | — | accepted |
| D28 | Where is Autofill offered? | menu + a chip at password prompts; menu only | menu, and a chip while the pty reads with echo off | the anchor-view question in selection | accepted |
| D29 | OSC 52 policy | write yes, read asks; write yes, read never; both allowed | write with a toast; read asks (once / always for this pane / deny); configurable | — | accepted |
| D30 | How are third-party components credited? | a generated licence dump; a NOTICE file; prose per component | a credits document in markdown, one part per component crediting how the project uses it, composed and given its licence texts by VitePress @include, published on the docs site and shown in-app through the document viewer (TPG12) | — | accepted |
| D31 | What does an open request for a local file do? | hand it to the platform; open it in the app when the viewer supports it | open it in the app as a tab or split (TDV1); the platform keeps the rest | — | accepted |
| D32 | How does the terminal get a document viewer? | depend on apps/hue; write a viewer for the terminal; extract hue's into a library | extract hue's GPU document viewer into a library both apps mount (UIA14) | — | accepted |
| D33 | Which selection menu? | floating bar with overflow (S1); bottom sheet (S2); icon pill (S3) | the bottom sheet (S2), with a swipeable action row whose last item is cut off at the edge, an edge fade and a More item — extensible without a page indicator | — | accepted |
| D34 | Anchored cards or bottom sheets for confirmations? | anchored everywhere; sheets everywhere; mixed per surface | one setting, ui.overlayStyle, anchored by default — never mixed within the app (TCF10) | — | accepted |
| D35 | Icons or text on buttons? | icon + text; text; icon | a setting, ui.buttonLabels, icon + text by default; icon-only keeps the label as accessible name and tooltip (TCF9) | — | accepted |
| D36 | How does the settings page save, order and present itself? | explicit Save (hue); autosave; autosave with undo. Section order by a table or by the struct. Bindings out of the page or a capture editor | autosave with a "Saved" toast and the property tree's undo; "overrides colors.properties" on edited Termux values; all sections expanded; order = struct field order; a capture editor for bindings, shared with hue (TSP3–TSP8) | — | accepted |
| D37 | Settings layout | property tree (G1); grouped list (G2); a tree with sections and inline controls (G3) | G3: the property tree, extended with section headers, descriptions, inline editors by type, changed markers and per-row reset (PRT36–PRT41) | — | accepted |
| D38 | How is the notification log organised? | by tab only; chronological only; a grouping control | a grouping control: none, tab/split, app, agent, project, rules; the app and directory are captured when the notification arrives (TPG18) | — | accepted |
| D39 | The exit prompt | a banner (E1); a centred card (E2) | the banner (E1); the status line expands to the full command, directory and times (TSS2) | — | accepted |
| D40 | Approved as drawn | — | the autofill chip (A1), the OSC 52 read sheet (P2), the about page with rendered credits (C1), the dense log page with search on its own row (LG1, matching by sparkles:fuzzy), the viewer as a tab and as a split (V1, V2), the key guide with navigation on top and search at the bottom (K3), the desktop right-click menu and link bar (D1) | — | accepted |
| D41 | Which overlay variants ship? | pick one per surface; offer them as settings | all approved variants ship, chosen in Settings: the selection menu as a sheet, card or compact pill (ui.selectionMenu), confirmations as anchored cards or sheets (ui.overlayStyle), button labels as icon + text, text or icon (ui.buttonLabels); the desktop uses the right-click menu (X1). The defaults are the sheet, anchored cards and icon + text | — | accepted |
| D42 | How are tabs and panes laid out and reached? | A top tab strip; B framed panes and a bottom bar; C a zoomed pane with a pill, rail or sidebar; D C's pane tree with search; E D's tree opened by a tap | E, with its two choices as settings: the opener (ui.tabsOpener: pill or rail) and the pane chrome (ui.paneChrome: reveal, header or framed). No edge swipe opens anything and no swipe switches tabs — the screen edges belong to the system Back gesture. Supersedes the strip, two-finger swipe and long-press tab menu of D25 | a user asks for swipe navigation and Android lets an app reserve an edge from Back | accepted |
| D43 | How does the desktop terminal reach the session bus? | shell out to gdbus/notify-send; libdbus or sd-bus through ImportC; a native D client | a native D client in apps/terminal (dbus_wire, dbus_conn, desktop_bus): ActionInvoked and SettingChanged need a long-lived connection that receives signals, which a helper per call is not; the subset is small and polled per frame, where libdbus or sd-bus would add a Linux-only link dependency to every build | a second consumer (hue, sparkles:ui-app): make it a library | proposed |
| D44 | How does an open request reach the app, and which pane asked? | a shell-script xdg-open speaking through socat; per-pane environment variables naming the pane; the socket peer's process | the terminal executable itself, linked as xdg-open in a per-app directory first on every session's PATH, talking over a per-app Unix socket; the requesting pane is the one whose child the socket peer (SO_PEERCRED) descends from — the same rule for the desktop shim and Android's am server, and no per-pane environment to keep in sync (open_request) | a platform without SO_PEERCRED or /proc | accepted |
| D45 | Which leader on Android? | keep Ctrl+Shift+Space; Alt+Space; Ctrl+Shift+A | Ctrl+Alt+Space on Android, the desktop keeps Ctrl+Shift+Space: Android takes Ctrl+Shift+Space (and Ctrl+Space) from a hardware keyboard to switch layouts, so the app never receives it | Android frees the chord, or a user rebinds it | accepted |
| D46 | How does a touch user reach the guide when the extra-keys row is hidden? | long-press ☰; nothing | a ⋯ button in the opener — under ☰ in the rail, at the end of the pill band — on touch screens, opening the touch guide; a keyboard cover hides the row (TCF7) and with it MENU | — | accepted |
| D47 | Do large screens get their own defaults? | only a max width | yes: under auto, a screen whose smallest width is at least 600 dp keeps the rail in portrait and gets the anchored-card selection menu; pages, settings and the guide stop at 100 columns, centred — the phone layouts stretched across a 3200 px tablet | a tablet layout mockup supersedes it | accepted |
| D48 | Fullscreen or the status bar on Android? | stay fullscreen (accept the overlay); the status bar on tablets only | the status bar, everywhere: raylib opens the window fullscreen, and HyperOS draws its multitasking handle over a fullscreen window's top centre — it is not an inset the app can read; with the status bar the handle has a band of its own, as in Termux and Chrome, at the cost of about 30 dp of rows | a system that lets a fullscreen app learn what it draws over it | accepted |
Open questions
| ID | Question | Affects | Resolver / decision point |
|---|---|---|---|
| OQ1 | Parse OSC 52/99/777/1 payloads in the host scanner or extend libghostty-vt's C API? | TPR2, TPR8, TPR20 | resolved: in the host scanner; the engine stays pinned (Protocols) |
| OQ2 | Does the engine send a mode-2031 report when the mode is set? | TPR14 | resolved: no report when the mode is set, as in Ghostty and libghostty-vt (Protocols) |
| OQ3 | Do autofill services show their dropdown anchored to a 1×1 transparent view? | TSE8 | answered in M5: Google's service (the device default) offers its passwords as inline suggestions in Gboard's strip, so the 1×1 field's size does not matter there; a service drawing a dropdown is untested |
| OQ4 | Can a NativeActivity read the intent of a notification tap without Java? | TPR12 | answered in M5: no — after a tap getIntent() and the task's baseIntent both lack the extra; the tap is inferred from the auto-cancelled notification (TPR12) |
| OQ5 | Where does the documentation build stage the licence files the credits include, and how does a local yarn docs:build get them? | TPG13, TPG16 | resolved in M10: the .#credits derivation stages them from each component's source; docs:build copies them, git-ignored, through docs/scripts/build-credits.sh, and the APKs bundle the same derivation |
| OQ6 | Which tree-sitter grammars does the terminal APK carry? | TDV1 | resolved in M11: a common subset — markdown and its inline grammar, D, C, Bash, Nix, JSON, YAML, TOML, Python — parsers as native libraries, queries extracted with the fonts (terminal.nix viewerGrammars); any other text file opens unhighlighted |