* test: remove 98 low-value mobile tests
* test: drop stale typecheck allowance for removed browser budget test
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* revert(mobile): drop the details card on session tab taps
Reverts 852391da0f (#36395). The card popped up on every tab switch.
Tapping a tab switches tabs again, and the session title card goes back
to its own owner from #30389. Keeps later changes to these files: tab
press feedback, the compact top bar's + button, and #36866's keyboard
keep-focus for title taps.
* fix(mobile): hide branch and folder when the setting is off
The phone's workspace row always printed the branch or folder path, so the
Branch name / Branch / folder path toggle under Card display did nothing.
The shared card details now carry that rule, and the row reads it.
* fix(mobile): centre the chevron on Home list rows
* fix(mobile): scroll sheets taller than the screen
Content-sized sheets ride the keyboard by translation, but their height cap ignored that lift, so a tall sheet pushed its top off screen with no way to scroll back to it. The cap now gives back the keyboard lift. Sheets that scroll their own list can now shrink to the cap instead of clipping it, and the GitHub Pages picker no longer nests its list inside the drawer's ScrollView.
* feat(mobile): show each desktop's machine kind and model
The desktop now reports whether it is a laptop, desktop or server, and its
model, on status.get. The phone stores it with the other last-known host
fields and turns it into a line like "MacBook Pro · macOS".
* feat(mobile): edit a host from its screen and align the Automations bar
The host screen's menu gains Edit host and Network diagnostics. Edit host moves
onto the standard bar and gains Remove host. Automations uses the standard bar
with its switcher pinned under it, as Source Control does.
* feat(mobile): arrange Home and bring back stats and account usage
Home drops the Jump back in times, names the repo on every Jump back in row
(#37642), shows each desktop's machine kind, and adds stats and account usage
at the bottom. A new Arrange button reorders and hides sections, saved on the
phone.
* fix(mobile): keep the page bundle within budget and show the machine on the host screen
The edit route gets a web stub so its native-only removal flow stays out of
the page's chunks, and Automations keeps its own segment style. The host
screen header shows the machine line like Home does.
* fix(mobile): give the edit route's page stub its own module
A bare re-export of the catch-all route compiled to the same asset as
[...page], so the page manifest listed it twice and the desktop template's
inventory check refused the build.
* feat(mobile): turn Home into Feed, Usage, Automations and Settings tabs
Feed keeps agents (needs-you first, five then Show all), desktops with a
needs-you count, and Tasks; New workspace moves to a floating button.
Usage shows stats and per-desktop account usage, Automations a per-desktop
summary with on/off switches that opens the page for editing (new=1 opens
its editor). Settings becomes a tab. Arrange Home is removed: tabs replace
the long scrolling Home it was for.
* style(mobile): darken the accent blue slightly
* style(mobile): give the Home Tasks row the desktop tile icon
* style(mobile): condense the Home Feed
Smaller tiles, type and gaps; a desktop is two lines with its needs-you
count as an amber pill; the Tasks row drops its duplicate heading.
* style(mobile): drop the Last known prefix on Home desktop rows
* chore(mobile): bump to 0.0.63 and Android versionCode 27
* test(mobile): re-record Linear task recordings for the darker accent blue
* test(mobile): check the page Back stack on Tasks now that Edit host is native
* fix(mobile): pass the Usage tab's limit window to the shared usage helpers
* fix(mobile): type the Usage tab test to the account-slot limits and keep the tab under 400 lines
* feat(mobile): bring back the terminal key row and one live input
Every terminal tab, shell or agent, gets the dock from before #36166:
the scrolling key row (desktop width, Paste, the built-in keys, custom
keys, add) above one live field that types straight into the terminal,
with the attach and mic buttons beside it. The field keeps today's 3:1
outline and focus ring, and the keys use today's chips and press styles.
The Keys/Message toggle, the Message composer, the arrow pad, the ⋯
sheet on the row and the per-terminal mode preference go. A stored mode
from an older build is never read. Sticky ctrl/alt/shift and the More
keys sheet stay only behind the iPhone Duo rail, which shipped shells
still draw; the page reports no composer, so shipped shells grey their
input-mode key and newer shells hide it.
* refactor(mobile): delete the buffered terminal line
With one live input there is no way to write a terminal line first and
send it, so the per-terminal drafts and their storage, the Send path,
command-field dictation, draft restoration and the terminal setting for
autocomplete and autocorrect (which only applied to that line) go.
Dictation in a terminal always types into it live. Drafts and the setting
saved by older builds are never read.
The PR tab now leads with the PR's identity, one row per merge gate
(checks, unresolved threads, approvals, conflicts) that opens the screen or
sheet that resolves it, a changes card and a collapsed description. Close,
Unlink, Auto-merge, Edit title and Copy link move into the ⋯ menu.
Checks and Comments become pushed screens: failures open with their
annotation and Fix / Rerun / Open / Log on the failure, running checks
next, passed folded; comments filter by Unresolved / All / Hide bots with
Resolve and Reply on each thread and a comment box at the bottom.
Merge opens a sheet that lists only the methods the repo allows, starting
on its default. Reviewers move into a sheet with search to add and remove.
Every Create PR entry point opens one sheet: title and description seeded
from the branch's commits (shared seed rule in src/shared, GitHub's own
default), Write with AI, base picker and draft. It pushes first when
needed, works for all five providers and lands on the PR tab for GitHub.
* feat(mobile): one next-step bar under both Source Control tabs
Changes and Pull request are the only segments. A bar at the foot of both
shows the branch's one next action: the commit box, push or pull, create
the review, resolve or fix with the workspace's agent, merge when ready,
merge, or why it can't. Commits opens as a pushed screen.
* fix(mobile): keep Merge reachable and show bar failures on both tabs
* test(mobile): expect the two Source Control segments in the page render test
The review screen becomes the one diff screen from the approved design.
It walks the Changes section the file came from (uncommitted, staged or on
the branch) or one commit's files, so 'N of M' and next/previous stay in
that list. The title opens a jump-to-file sheet built on the shared
source-control tree, with search and viewed boxes. Unchanged runs fold the
way desktop's diff does. The bar's main button is Stage · next file for
uncommitted files and Viewed · next file otherwise; Discard and Unstage move
to the menu. Filter chips, hunk jumps and Stage Reviewed are gone.
openDiffForFile also takes { kind: 'commit', commitOid, filePath }, read
through git.commitCompare and git.commitDiff.
The queue card's invisible full-text line-count probe overflowed its two-line
row and stretched the queue's scroll range far past the real rows. Clip it to
the card text.
The queued-message editor drew Save and Cancel labels inside 38pt round icon
controls, so translated labels spilled over each other. They are now pills that
hug their label and truncate only when the row runs out of room.
* fix(mobile): show workspace images in session Markdown tabs
* fix(mobile): show workspace images restored by undo or paste
* fix(mobile): keep session image loader current and inside the workspace
* fix(mobile): classify entity-encoded image URLs by their decoded scheme
* refactor(mobile): classify editor images with the shared Markdown href router
* test(mobile): expect workspace image markup in the code-block selection check
* fix(mobile): stop page bounce and input zoom; URL keyboard for new browser tabs
* feat(mobile): smoother motion, press feedback everywhere, reduced motion for sheets
Working rings and the tool-run pulse run as compositor animations on the
page. Sheets, the right panel, the page stack and the pager cross-fade under
Reduce Motion. The right panel shares the sheet's drag physics. The composer
cross-fades Send/Stop/Save and the mic, jump-to-latest and prompt cards ease
in, banners ease their height, and every Pressable shows a pressed state.
Pairing gets a success beat, and the clean state fades in after a commit.
* feat(mobile): polish press feedback, sheet motion, states, spacing and layout jumps
Every control presses and disables the same way, with one haptic rule.
Sheets follow the finger and settle without bounce, pickers stack instead of
swapping, and screens slide over a dimmed, shifted parent. Loading, empty,
error and offline states share one view with plain-language errors, rows and
labels line up on one grid, and screens stop jumping as data arrives.
* fix(mobile): settle voice, tasks and accounts states; keep host snapshot current
Voice settings with no paired desktop shows the connect state instead of
loading forever. A failed task settings read after a successful probe retries
hydration. The shared host-list snapshot drops on invalidation and ignores
stale passes. Accounts shows a re-pair state on auth failure, and busy
action-sheet rows keep their disabled state for screen readers.
* feat(mobile): expand Mermaid diagrams into a pinch-zoom viewer; image lightbox zooms the image, not the page
* fix(mobile): expand Mermaid labels with nonbreaking spaces instead of a blank image
* feat(mobile): draw SVG files with a Preview/Source toggle; pinch, drag and double-tap to zoom images
* test(mobile): render an SVG preview under the shipped shell policy and pinch it
* fix(mobile): double-tap after a drag at fit zooms in instead of re-fitting
* fix(mobile): pinch follows the fingers, Back resets zoom, and writable SVG artifacts get a preview
* fix(mobile): keep a writable SVG's save failure visible in Preview
* feat(mobile): compact top bar, bottom search and filter chips
Every list screen gets one compact bar (back, centred title, at most two
buttons, the rest in a ⋯ pull-down), with search moved to a wide bar at
the bottom and filters as chips. The workspaces page shows the host name
at the top of the list and folds it into the bar as you scroll.
* test(mobile): pass the Tasks bar's accessory in the host subtitle test
* fix(mobile): stretch filter chips to fill the row; follow moved header controls in render checks
* fix(mobile): draw Claude Code's ⏵⏵ mode marker on Android terminals
Android ships no font with U+23F4-23F7, so the terminal drew boxes.
Bundle a 664-byte Noto Sans Symbols 2 subset, inline it in both
terminal hosts, append it to the Android font stack only, and load it
with the Nerd font before the WebGL atlas first paints.
* fix(mobile): spell the terminal font probe glyphs as escapes
* refactor(mobile): tidy terminal symbols font wiring
* feat(mobile): give every text input the chat composer's look
Adds shared MessageBox, TextField and ComposerSendButton built from the
composer's own styles, and moves chat, form, search, Tasks, PR, review and
commit inputs onto them. The commit message becomes multi-line, every input
is 16pt (no page zoom on focus), and focus rings are gone. The terminal and
browser typing docks are unchanged.
* feat(mobile): rebuild the terminal dock around Keys and Message modes
Agent tabs open in Message mode, where you write in the chat composer and
Send types it into the terminal. Shell tabs open in Keys mode, where each
letter goes straight to the terminal through the live field. A pick made on
an agent tab lasts until the agent quits. The shell/agent rule is the one
keyboard dismissal already used, now shared.
The key row carries sticky ctrl, alt and shift (tap for the next key,
double tap to lock) and one arrow pad (tap a side, or hold and drag to
repeat through the existing held-key repeat). Paste, attachments, the
display width, the remaining keys and custom keys move to a ⋯ sheet. Return
in the composer adds a line; Send sends.
* fix(mobile): read arrow-pad taps from the pad and keep every key reachable from ⋯
A touch on a chevron made the icon the touch target, so the side was
measured against the icon's box and most taps read as left. The ⋯ sheet
now leaves out only the keys the current row shows, so agent tabs get Tab
and shell tabs get Shift+Tab and Enter back.
* fix(mobile): keep the last changed files clear of a growing commit box
The file list's bottom inset was a fixed 196 pt, so a commit message several lines long hid the last rows behind the commit bar. The inset now follows the bar's measured height.
* test(mobile): give the file list test its bottom inset
* fix(mobile): retain editor styling when switching color schemes
* test(mobile): fit theme-switch harness hooks under the file line cap
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(mobile): keep table-cell and list-item line breaks on their own source line
Table cells and list items save line breaks as <br /> (and sibling P/DIV
blocks inside an item as breaks), and reopen attribute-free <br> tokens
as breaks, so Shift+Enter or Enter no longer splits a row or ends a list.
Typed '<br>' text gets only its '<' entity-escaped, raw text newlines
become spaces, and browser caret placeholder breaks are dropped (a cell
that holds only a placeholder saves empty; a cleared item keeps a <br />
so its marker still parses).
* fix(mobile): keep fenced code blocks inside Markdown list items
Indented fences under a list item (README steps such as
'1. Install:' followed by an indented bash fence) now read as a code
block inside the item and save back under the item's continuation
indent. Only items whose continuation holds a fence change; headings,
quotes, rules and tables after the fence end the item and read as
before. Top-level and list code blocks share one PRE writer, blank
separator lines are not indented, and a fence needs no blank line
beside it so tight items stay tight.
* fix(mobile): keep caret code blocks out of the paragraph they follow
Code block with a collapsed caret after text in its block ran native
insertHTML, which merged the PRE into that paragraph as prose. Prepend
a <br> only when content precedes the caret, so block-start, empty-line
and empty-document insertions stay as they were.
* fix(mobile): keep authored runs of trailing table-cell breaks
* fix(mobile): use template literal for list code-fence probe
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(mobile): preserve literal code through rich Markdown edits
* fix(mobile): preserve inline code containing backticks
* Preserve adjacent inline-code text when saving mobile Markdown
* Preserve quote paragraphs and blocks in mobile Markdown
* fix(mobile): preserve selected text when creating code blocks
* test(mobile): choose native history shortcut from browser host
* Preserve paragraph-owned mobile code blocks when saving
* Preserve code selected inside mobile headings after saving
* fix(mobile): keep code serialization space-normalized and trim review tests
- Inline code and code blocks keep turning browser-created NBSPs into spaces;
only the reader-side entity fix is needed for literal code.
- Inline code line breaks save as spaces so table rows and headings stay intact.
- Code blocks keep trimming trailing newlines left by placeholder <br>s.
- Heading- and paragraph-owned code blocks join adjacent inline code runs.
- Empty nested quotes reopen as a plain empty paragraph.
- Trim synthetic and behavior-locking tests.
* fix(mobile): keep quoted text literal when block reading would rewrite it
Quoted list continuations and lazy lines were restructured on save because the list reader drops them; reopen quote blocks only when they save the source or the same bytes as the literal paragraph.
* test(mobile): use template literal for saved code-block markdown
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(mobile): show live input caret and preserve selection edits
Display the existing input directly so its native caret shows. Accessory
Backspace/Delete edit at the field's caret, read from the input when pressed,
so edits that keep the caret (IME composition) still delete, and the caret is
restored for subsequent typing.
Port and adapt community PR #28294 to the current command dock.
Co-authored-by: Kyino <kyino1025@gmail.com>
* fix(mobile): keep live input caret keys local and the dock height fixed
Hardware Left/Right/Home/End moved both the visible field caret and the
PTY cursor, so the mirror erased at the wrong place. With field text they
now move only the field caret, like Backspace and Delete; Right/End at the
field end still reach the PTY so shells can accept an autosuggestion. On
Android the key binding lets plain caret keys through to that decision
instead of sending them to the PTY. The dictation and upload status covers
the field instead of adding a row, so starting or stopping either no longer
resizes the terminal.
* test(mobile): annotate the DOM field cast in the accessory input test
* chore(mobile): allowlist the live input field strings for localization coverage
---------
Co-authored-by: Kyino <kyino1025@gmail.com>
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
* fix(mobile): restore lifecycle-safe terminal accessory repeat
The selection hook replaced the established repeat controller with unbounded timers, allowing held keys to cross terminal/client changes. Reintegrate the existing pinned sender, lifecycle cancellation and backpressure.
* test(mobile): stop held terminal input after refusal or disconnect
* test(mobile): give accessory-repeat harnesses the PTY modes and focus hook main now reads
* fix(mobile): stop a held key when its strip unmounts and repeat through the current live-input mode
* fix(mobile): type the accessory repeat sender's input
* refactor(mobile): read app state live in the accessory repeat gate
* test(mobile): pin the key bar's held press to the repeat controller's first send
---------
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
mobile/ is a separate pnpm project, so the root pnpm install leaves
mobile/node_modules empty and pnpm build failed in build:mobile-web with
hundreds of unexplained esbuild "Could not resolve" errors.
build-mobile-web-app-bundle now checks for mobile/node_modules first and
exits with the one command that fixes it. The probe path is shared with
the bundle tests' dependency check. CONTRIBUTING documents the install in
Local Setup and points "Before Opening a PR" at it.
Fixes#24324
Co-authored-by: Neil <neil@stably.ai>
* feat(mobile): light and dark themes with desktop's palettes
Settings → Appearance offers System, Light and Dark. The shell passes the
resolved scheme to over-the-air pages in its init frame.
Fixes#22184
* fix(mobile): draw your chat messages in desktop's user bubble
Desktop uses a faint surface with a hairline border in both themes; the inverted fill went black in light.
* test(mobile): re-measure the page script sweep after the shared theme hooks
Every route now imports the theme hooks while the entry does not, so they emit one shared script (96 for the tree). Re-swept every route prefix; the worst route step is now 15.
* test(mobile): read themed stylesheets in the text-input font-size census
The census only unwrapped StyleSheet.create({...}); inputs styled through themedStyleSheet(() => ({...})) came back unread.
* test: remove low-value inventories, passthroughs and redundant cases
* style: format test suites after pruning redundant cases
* test: remove obsolete workflow and flaky-test references
* test: prune deleted builder test from Windows shim allowlist
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* test(mobile-web): act on the HTML preview frame only once the browser routes input into it
Chromium runs the sandboxed preview frame in its own process. The rig decided an arm was
ready once the artifact had parsed, but the embedder can still be showing the frame's white
before its surface is composited, and still keep pointer input over the frame for itself
until it holds the frame's hit-test region. A click in that window landed on the
embedder's <iframe>, Playwright reported success, and the arm waited for a navigation
that never came (or read white as pixelBefore).
The rig now waits for the artifact's paint before reading pixelBefore, and before acting
moves the pointer over the frame until the embedder stops receiving it.
* test(mobile-web): name the arm in the input-routing timeout and trim history comments
* test(mobile-web): click the drawer chip once it is actionable, and honour the wait timeouts
* test(mobile-web): say why the drawer trial click stays, and report a failed open with page errors
* fix(mobile): preserve agent icons across stacked routes and tab updates
Closes#31939
* fix(renderer): address review feedback on mobile agent icon stability
* fix(renderer): fall back to customTitle when saved leafTitle is blank
* Keep mobile agent icons painted across retained routes
Use instance-specific OMP gradients and preserve bundled icon fallback during web image reload failures. Add a real mobile-web route-retention pixel regression.
Adapted from zhangxaochen’s scoped rendering fixes in #31948; unproven session equality and title changes are excluded.
Co-authored-by: zhangxaochen <zhangxaochen@163.com>
---------
Co-authored-by: Neil <neil@stably.ai>