attui is a keyboard-first terminal browser for navigating the accessible web. It does not rasterize HTML or implement another browser engine. It drives Chromium with Playwright, reads Chromium's accessibility tree, and maps that semantic tree into Ink primitives.
Rendering model:
Chromium accessibility tree -> roles, labels, values, state, actions
DOM flow hints -> compact row/column presentation only
semantic renderer -> terminal primitives
terminal geometry -> Norigin spatial navigation
Ink -> terminal view and keyboard interaction
The accessibility tree is the only source of content, order, roles, names, and
state. DOM/CSS access can only affect layout through flow hints; it cannot add,
remove, rename, or reorder semantic nodes. A small capability channel preserves
native input type where Chromium's accessibility tree does not expose it.
Spatial navigation uses the rendered terminal rectangles, never browser pixels.
Pages are
rendered as soon as Chromium reaches domcontentloaded. A debounced live watcher
then refreshes the semantic view when an SPA changes history, accessible DOM
content, focus, online state, or service-worker control, without waiting for
network-idle.
Install from the public repository:
git clone https://github.com/srsatt/attui.git
cd attui
npm ci
npm run build
npm link
attui browser installOn Linux, attui browser install --with-deps can also install Chromium's
operating-system dependencies. npm link is optional; without it, use
npm start -- https://example.com after building.
Run:
npm run dev
npm run dev -- https://example.com
npm run dev -- --profile ~/.local/share/attui/profiles/main https://app.example.com
npm start -- https://example.comSessions are ephemeral by default. Pass --profile <path> to preserve cookies,
local storage, and other Chromium profile data for authenticated PWAs. Use
--headful to show Chromium while debugging, and --width / --height to
change the browser viewport. Run npm run dev -- --help for the complete CLI
help. Web notifications are denied by default; --allow-notifications requests
Chromium permission and surfaces page/service-worker notification events in the
terminal status line.
Keys:
Enter load the address while the address bar is focused
Enter activate the selected primitive while the page is focused
Enter enter edit mode for a selected text/search input
Enter open a selected native select as a full-screen picker
l focus the address bar
i enter focused-control mode for the selected primitive
r reload
u refresh the semantic document
[ / ] back / forward through Chromium history
Arrow keys spatial navigation between links, buttons, and controls
Arrow keys adjust the selected slider or numeric spinbutton
j/k scan selection by rendered primitive row
Tab jump to the next link, button, or control
Shift+Tab jump to the previous link, button, or control
Space/b move selection one page down/up
g/G top/bottom
q quit
Ctrl+C quit
t open a new tab
x / X close the current tab / reopen the last closed tab
{ / } previous / next tab
T open the tab switcher
D inspect site data and optionally clear it
/ find text in the semantic document
n / N next / previous find match
f open link hints
y / Y copy selected text / current URL via OSC 52
m add or remove a bookmark for the current page
B / H open bookmarks / browsing history
? / : open command help / searchable command palette
I inspect the raw accessibility tree (semantic data only)
E review and export a diagnostic bug bundle
Focused text/search inputs:
Typing update the local input buffer
Enter apply a single-line value / add a multiline newline
Ctrl+S apply a multiline value without submitting
Ctrl+Enter apply the value and submit its form
Esc cancel editing without applying the buffer
Backspace delete the last character
Ctrl+U clear the input buffer
Focused-control mode sends a bounded set of trusted Playwright key presses to the focused web control. This supports custom ARIA comboboxes, listboxes, menus, trees, and grids while keeping ordinary terminal navigation separate.
Arrows/Home/End/PageUp/PageDown send navigation keys to the web control
Enter/Space/Tab/Shift+Tab send activation or focus keys
Esc send Escape and leave focused-control mode
Ctrl+] leave focused-control mode without sending a key
File inputs and downloads open a terminal path prompt. Nothing is uploaded or
saved until Enter confirms the displayed path; Esc cancels, and Ctrl+U
clears the path. Paths are resolved directly by Node without shell evaluation.
Chromium pages are real tabs with independent history and terminal cursor/scroll
positions. The header shows the active tab index. --popup switch follows new
windows, --popup background keeps them as background tabs, and --popup block
closes them immediately.
Sessions without --profile are ephemeral. --private makes that intent
explicit and cannot be combined with a persistent profile. The site-data view
shows counts without exposing cookie values; clearing requires pressing C
twice and is scoped to the active HTTP(S) origin. If Chromium has locked a
persistent profile, attui reports how to close the competing session or choose a
different profile path.
Camera, microphone, geolocation, clipboard, and notification API requests pause
at a terminal allow/deny prompt and are granted only for the requesting origin.
HTTP Basic credentials can be supplied with --http-user and
--http-password-env so the password stays out of the command line. Invalid
certificates remain errors unless --ignore-https-errors is explicit. Non-web
schemes such as mailto: and custom application protocols are blocked and shown
as errors; attui never launches an external handler automatically.
Bookmarks and deduplicated browsing history are stored atomically in
$XDG_DATA_HOME/attui (or ~/.local/share/attui) and can be relocated with
--data-dir. Explicit private sessions never write browsing history. Copy
commands use OSC 52 and only emit a clipboard sequence when stdout is a TTY.
Terminal presentation can be tuned with --theme default|high-contrast|mono,
--density comfortable|compact, and --verbosity quiet|normal|verbose.
--zoom 50..300 changes the effective Chromium CSS viewport so responsive page
layout follows browser-style zoom.
--reduced-motion disables any optional motion (the core document renderer is
already static). Accessibility text, titles, options, and saved library labels
are sanitized before display; control, C1, and bidi-override characters cannot
be emitted as terminal instructions. CJK and emoji geometry uses terminal cell
width for navigation and mouse targeting.
Configuration is read from $XDG_CONFIG_HOME/attui/config.json (or
~/.config/attui/config.json). The JSON object accepts the long-option names in
camel case plus keybindings, whose keys are terminal key names and values are
stable command IDs shown by ?. Explicit CLI flags take precedence. Use
--config <path> for another file or --no-config to ignore configuration.
Diagnostics are opt-in. attui doctor checks Node, Chromium, writable data and
profile paths, profile locks, and terminal capabilities. --log-file <path>
writes JSONL lifecycle events with credentials, secret-shaped fields, URL
queries, and fragments redacted. --trace-dir <path> captures a Playwright
trace; traces can contain page content and should be shared only after manual
review. E shows the exact privacy boundary before writing a mode-0600 JSON bug
bundle: it excludes cookies, storage, credentials, profile paths, URL paths,
queries, and fragments, but includes visible accessibility text, page titles,
origins, and runtime metadata. Shell definitions are emitted by
attui completions bash|fish|zsh.
Select picker mode:
Enter choose the selected option
Esc/q close the picker
Arrow/j/k move between options
Space/b move ten options down/up
g/G first/last option
Current primitive coverage includes pages, header/nav/sidebar/footer/main landmarks, headings, wrapped text, horizontal link/control rows, buttons, text/search inputs, checkboxes, radios, switches, select-like comboboxes, options, menus, tabs, sliders, progress/status nodes, images, lists, and tables. Links and clickable controls are activated through the backing DOM node exposed by Chromium's accessibility tree.
Editable native inputs retain their browser type. Date, time, month, week,
datetime, color, number, email, URL, telephone, password, and search values are
applied through Chromium and emit the page's normal input and change events.
Required, read-only, disabled, and invalid states are shown alongside controls;
validation descriptions, alerts, status messages, and ARIA live-region updates
remain visible in the semantic document.
Development checks:
npm test # unit tests plus deterministic Chromium fixtures
npm run typecheck
npm run buildSupported targets are Linux and macOS on Node.js 22 or 24. Dependency
versions and the Playwright-compatible Chromium revision are pinned; see
docs/RELEASING.md for upgrade and version policy and
docs/SECURITY.md for the threat/privacy review.
The current version is pre-release software; compatibility evidence and known
limitations are tracked in docs/COMPATIBILITY.md.