feat(mobile): unify the terminal and home composers
Why
The terminal composer was a stripped copy of the home glass pill — no voice, no attachments, and a send button that hard-popped in and out of a zero-width container instead of morphing. Two divergent copies of a fiddly SwiftUI component.
What
Both surfaces now build on one GlassComposer (screens/(authenticated)/components/GlassComposer), with slots for the surface-specific chrome (header, toolbarLeading, above). NewChatWidget and TerminalComposer are thin wrappers.
The terminal inherits the mic (dictation → recording pill → finalizing chip), the animated mic→send swap, focus-expand, and attachments. Project/branch/agent pickers stay home-only.
Also:
- Attachments sheet moved from
(home)/attachmentsto/(authenticated)/attachmentsso both surfaces can open it. - Quick keys are now SwiftUI, inside the composer's
Host. As RN siblings their gap to the pill was a two-value guess (pb-[26px]/pb-2.5) at a height that varies continuously, animating on a third curve — it visibly drifted whenever the pill grew. SwiftUI now owns that spacing. - Terminal attachments upload into the worktree's
.superset/attachments/and ride along as worktree-relative paths appended to the prompt, mirroring the desktop terminal adapter. Agent sessions only — a plain shell tries to execute the path (zsh: permission denied: …IMG_0006.HEIC), so+is gated onagentId != null.
Net −340 lines.
Notable gotchas encountered
Documented inline, since each cost real debugging time:
- The SwiftUI
TextFieldsits outside RN's responder chain. Programmaticfocus()doesn't fireonFocusChange; iOS restores first responder after a sheet dismisses without firing it either; andKeyboard.dismiss()can't lower the keyboard for it (tap-outside must call the field's ownblur()). transitionEndonly fires on the navigator owning the animating screen — moving the attachments route to a parent stack silently killed the refocus callback.- The liquid-glass material paints past its layout frame.
onGeometryChangereports the frame accurately; the bleed still needs explicit padding inside theHost.
Verification
Driven on an iOS 26 simulator against production: expand/collapse, voice control, send insertion, tap-outside dismiss, attachment thumbnails, and an image round-tripping to Claude Code — which read the HEIC and described it correctly.
Summary by cubic
Unifies the home and terminal input into a shared SwiftUI GlassComposer, so the terminal now supports voice, attachments, animated mic→send, and consistent expand/collapse. Previously the terminal had no voice/attachments and a hard-swapping send button; now both surfaces share one component to prevent drift.
- Adds
screens/(authenticated)/components/GlassComposerwithheader,toolbarLeading, andaboveslots;NewChatWidgetandTerminalComposerwrap it. Exposesfocus(),blur(), andclear()on a ref, pluskeepExpanded,onActiveChange,onSubmit,isSending, andshowAttachments. - Moves the attachments sheet to
/(authenticated)/attachments; both composers open the same sheet. Quick keys render in SwiftUI inside the composer'sHostfor exact spacing and native animation. - Terminal attachments write to
.superset/attachments/via the host service and are sent as worktree‑relative paths appended to the prompt. The+button shows only for agent sessions. AddsuseWriteTerminalAttachments. - Measures composer height via
onGeometryChangeand pads for glass bleed to avoid keyboard clipping.onActiveChangeenables backdrops/tap‑to‑dismiss in callers.
Migration
- Update any links or
router.pushcalls to the attachments screens from/(authenticated)/(home)/attachments[...]to/(authenticated)/attachments[...].
Summary by CodeRabbit
-
New Features
- Added photo and screenshot attachment selection with permission guidance, previews, ordering, and multi-select support.
- Attachments can be added to chats and terminal sessions.
- Added voice dictation with recording controls, live levels, and transcript insertion.
- Introduced a unified composer with attachment previews, quick actions, focus controls, and send-state handling.
- Improved attachment presentation in authenticated mobile flows.
-
Refactor
- Standardized chat and terminal input experiences around the shared composer.