Skip to content

Improve Mirror interaction, remote scrolling, and device pairing - #846

Merged
onevcat merged 13 commits into
onevcat:mainfrom
Awhisper:fix/mirror-interactive-input
Oct 2, 2026
Merged

onevcat merged 13 commits into
onevcat:mainfrom
Awhisper:fix/mirror-interactive-input

Conversation

@Awhisper

@Awhisper Awhisper commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Why

This change addresses problems found while using Mirror for everyday interaction on Mac, iOS, and Android: ordinary phone messages can become blocked by an automation receipt, terminal History cannot navigate history owned by a full-screen TUI, and pairing/re-pairing requires too many separate steps.

Interactive input should not require task completion

Mobile messages previously used agents.dispatch. If the Agent failed to call dispatch-complete, a pending dispatch could block subsequent human input even after the Agent became idle.

Add a capability-negotiated agentsInput command that reuses readiness checks and guarded delivery without creating, completing, or abandoning an automation dispatch. Delivery acknowledgement means the input was delivered, not that the Agent finished its work. Existing automation completion semantics remain unchanged. Mobile clients retain text-v1, authenticated pane leases, request deduplication, and delivery-receipt recovery; unsupported Hosts require an upgrade rather than silently falling back to dispatch.

Navigate the Host viewport as well as retained History

History reads Ghostty's retained text buffer. Some full-screen TUIs, including the tested Codex version, manage and redraw their own history instead of placing it in terminal scrollback. Increasing History pagination cannot expose that content.

Keep History and add explicit Scroll Up / Scroll Down controls on all three clients:

  • Use existing Ghostty scroll actions for native scrollback and wheel input for application-owned history.
  • Calculate a page step from the Host pane height with overlap. An internal Codex wheel-unit profile compensates for the observed three-rows-per-event behavior and reserved composer rows; unknown Agents retain the default mapping, and native scrollback bypasses these profiles.
  • Anchor completed pages at the top for reading in either direction. Keep loading feedback within a stable toolbar row.
  • Disable a direction only when a frame-matched boundary is known. Unknown TUI boundaries remain unknown; unchanged output is not treated as proof of an edge.
  • Keep wheel/touch gestures local to the reader; remote scrolling uses the explicit buttons, bounded waiting, and no automatic replay of uncertain input.

Remote scrolling changes the Host's shared live viewport. It is not a separate archive or an independent per-client history position.

Preserve Mac terminal appearance while browsing native scrollback

The plain-text fallback made native history look substantially different from live terminal output, including font shaping and layout as well as colors.

Add negotiated styled-scrollback-v1 support for Mac. Existing Ghostty VT export APIs provide a bounded archive that is replayed in the same replica terminal, with viewport verification before display. Oversized archives or verification failures retain the text fallback. Exports are limited to 5,000 retained rows, 250,000 cells, and 2 MiB.

Also prevent replay-generated focus/color/size reports from being forwarded back to Host, which could otherwise cause the viewport to jump back to the bottom immediately after scrolling. Genuine user input remains supported.

Ghostty source and bundled framework are unchanged. Mobile text-v1 and retained History remain intact.

Make pairing and recovery straightforward

Copying an address, port, and code separately is error-prone. After Host revokes access, a client may also continue trying saved credentials without an obvious way to replace them.

  • Host displays a QR payload containing the address, port, short-lived pairing code, and expiry; iOS and Android can scan it.
  • Host provides one Copy Connection Details action. Pasting into any Mac connection field fills the address, port, and code together and bypasses old saved access. Expired/malformed payloads are rejected without replacing the current fields.
  • Mac offers Pair Again independently of transport-error classification.
  • Revoke All Devices removes persisted authorization for online and offline devices, disconnects clients, and invalidates the current pairing window. Devices must pair again; local terminals keep running. Vault-write failures are surfaced before reporting revocation as successful.
  • Android's scanner opens in portrait without the laser overlay, and its scroll controls sit at opposite toolbar edges.

The final copy/paste UI change is Mac-only; mobile continues to use QR scanning.

Validation

Merged current upstream main (436fc1e3) without conflicts. Its Canvas toolbar-spacing change does not overlap the Mirror implementation.

After integration:

  • 162 selected Mac tests passed, covering Mirror protocol/state, pairing and revocation, input delivery, scrolling, real Ghostty integration, and toolbar behavior.
  • macOS Debug build, localization checks, and workflow naming checks passed.
  • Fixed the branch's test-variable lint issue. Repository-wide lint remains blocked by five pre-existing legacy_swiftui_aspect_ratio violations in RepositoryIconImage.swift.

Earlier feature validation included iOS/iPad simulator and UI tests, Android unit/instrumentation tests and lint, CLI/relay tests, and physical-device/remote-Host acceptance. Mobile suites were not rerun for the final upstream merge, which changes only Mac toolbar layout and documentation. Earlier full CLI unit validation also encountered existing WorkflowHistoryRetentionTests export failures; this is not a claim that every repository-wide suite is green.

@onevcat
onevcat merged commit 3670511 into onevcat:main Oct 2, 2026
0 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants