PR #12892: navigate queued messages without losing unsaved edits

Test report · head c808dedbf2f23f567bd7bc116047d0ce6c034b5c · base V2 t3code/codex-turn-mapping (not rebased, not merged)

1. What the PR solves

pingdotgg/t3code#12892 (extends #12599). In the shared web/desktop composer:

Native mobile has its own code and is not changed by this PR.

2. Proof

Setup: an isolated worktree with its own .t3 state, vp i, and vp run dev (server on 13838, web on 5798), with no VITE_* origins set. A real Codex provider ran a long sleep turn with four queued messages behind it: a short one, a long multiline one, one with an image attachment, and one more. Every save, cancel, reorder and removal was checked against the server's SQLite state, not just the UI.

Devices: Mac web on macOS 27.0 (T3 Browser panel) · iPhone 17 Pro simulator on iOS 26.5 · T3Code_API_36 emulator on Android 16 (arm64). Both mobile devices were driven through T3's Device panel with AgentDevice.

BehaviorWeb (Mac)iOSAndroid
Option+Up backward; stays on first entry at the boundaryPassNot in mobileNot in mobile
Option+Down forward; past last entry restores the draftPassNot in mobileNot in mobile
Unsaved edit blocks switching (typed text, removed attachment, pencil click)Pass (toast)Fail: silent lossFail: silent loss
Save persists and reopening shows it (Enter; ⌘Enter during a turn; "⌘ + Enter always" setting)PassPass (Update)Pass (Update)
Cancel / Escape discards the edit; saved message unchanged; independent draft restoredPassPassPass
Escape closes @ suggestions and image preview first; held Escape doesn't discardPassn/an/a
Shift+Enter / Return inserts a newline while editingPassPassPass
Reorder and removal persist; navigation follows the current orderPassPassPass
Queue held after server restart → Resume drains itPassPassPass
Image thumbnail aligned with text; long multiline entry truncates cleanlyPassPassPass
New keybindings listed in Settings → KeybindingsPassn/an/a
iOS hardware-keyboard Option+Up—Not tested: AgentDevice can't send modifier keys, and mobile has no handler—

Failure: mobile loses unsaved edits (pre-existing, not caused by this PR)

Platforms: iOS 26.5 simulator and Android 16 emulator (native app).

Steps: open the queue → edit "Saved with keyboard" → append X → open the image entry without Save or Cancel → return to the first entry.

Expected: a Save/Cancel prompt, or the X kept. Actual: it switches with no prompt and the field shows "Saved with keyboard" again; the X is gone.

Cause: the native queue sheet has no dirty-edit guard. This PR only changes the web/desktop composer, so mobile needs a separate fix.

Other issues found (none caused by this PR)

Limitations

Not tested: the Electron desktop shell, Windows/Linux, IME input, and relay/tunnel connections. Web was tested in T3's built-in Chromium panel on macOS.

3. Recordings

These are real interactions. Idle waits between actions are cut, and each action plays at recorded speed.

Web queue navigation demo
Mac web (82 s): Option+Up to the first entry (boundary) → dirty edit blocked by Option+Down and by a pencil click (toast) → Enter saves → reopen shows the saved text → Escape cancels and restores the draft → the first Escape closes @ suggestions, the second cancels → Option+Down forward to the draft → ⌘Enter saves during an active turn.
iOS native queue workflow
iOS simulator (75 s): held queue with Resume → edit with X, switch to the image entry, return: X lost (failure) → Update persists and the draft comes back → Cancel keeps the saved text → Move up → Remove → Resume drains.
Android native queue workflow
Android emulator (119 s): same flow as iOS, connected → X lost after switching (failure) → Update persists → Cancel → Move up → Remove → Resume drains.

Verdict

Every web/desktop behavior this PR adds works on the Mac. I found no web merge blockers beyond the human review Macroscope already requires. Unsaved-edit loss on native mobile is confirmed on both iOS and Android; it predates this PR and needs its own fix. Not merged.