cmux-next iPhone: reply thread line, single Chief push, swipe actions close before the sidebar
Fixes three user-reported UI bugs in the cmux-next iPhone app, plus the Feed swipe-button style.
1. Reply quote thread line (CmuxConversation)
Cause. MessageCellLayout started the thread line beside the quote at its vertical middle (min(quoteBodyLeading - 4, x + 14), then a curve into the avatar column at x = margin + 16). In a 1:1 chat that put a hook outside the quote's leading edge, through the quote's tail and down into a second curve under it, which is the stray "hook" in the user's screenshot.
Fix. The line's geometry moves into CmuxConversationGeometry/ConversationReplyThread (shared, unit-tested). As in Messages, the line drops from under the quoted bubble's tail (or, in a group, from the avatar column beside an incoming quote). For a reply on the other side it turns toward the reply in a short curve level with the reply's middle and ends short of it. For a reply on the same side it is a straight line that stops above the reply (or the replier's avatar). The row now keeps room for the quote's tail. The quote bubble itself is unchanged: an outlined bubble with its tail, in the colors the iMessage team measured.
Compared with Messages screenshots (How-To Geek, Gadget Hacks; Messages' line drops from under the quoted bubble and curls toward the reply), in both directions and in both modes:
Packages/iOS/CmuxConversation stays byte-identical to feat-imsg-parity-gui; the same commits land there too (linked below). The macOS surface (CmuxConversationMacUI) has its own thread layout in MacConversationRows and does not share this code; it already drops the line from under the quote.
2. Opening a Chief pushes twice
Cause. With the Cloud Chief ready and a Mac linked, Home pins the Cloud Chief and the Mac Chief side by side in one List row. Both pins were NavigationLink(value:). List treats the row as one cell, and a tap there activates every link in it, so the stack pushed the Cloud Chief and then the Mac Chief over it. That matches the user's recording: a push, then a second push about a second later. A minimal SwiftUI app reproduces it (two links in one List row, one tap gives depth 2):
Fix. The pins are plain buttons that push their own route through Home's open handler. Update: #24170 (Home parity) landed first and rebuilt Home with button rows and tiles, which carries the same fix, so after the merge this PR's ConversationsHome.swift matches the base. This PR keeps ChiefPinPushTests as the regression guard (it passes on the merged head).
3. Swipe actions vs the hamburger drawer
Cause. The drawer's pan began for any rightward pan on a tab root, including one over a row whose trailing actions were open (Workspaces, Home, Settings > Devices and the other Lists). The Feed's custom rows already closed first.
Fix. As in Mail and Messages, while a row's actions are open, a rightward swipe closes them and never opens the drawer; the next one does. RowSwipeActions (CmuxMobileDesign) tells the drawer about open rows. List rows are read at touch-down, because UIKit starts closing the row as the finger lands. An open row is a cell slid off the leading edge its siblings share (SwiftUI's List slides the layout attributes with it, and UIKit leaves its actions view in place after closing). Feed rows report their open state and name their pan, and the drawer waits for that pan.
Videos: Home before, Home after, Workspaces before, Workspaces after, Feed before, Feed after
Feed swipe buttons. The Feed drew its own full-height rectangles. It now uses RowSwipeActionButton, the same tinted capsules with captions that List's swipe actions draw on Workspaces and Home (measured on iOS 26.5: 66 x 50 pt, 12 pt apart, 10 pt from the edge). Its actions, haptic, full swipe and close-first rule are unchanged. The swipe row is not in CmuxTabsKit, so feat-cmux-next-tabs-kit needs no change.
Tests (red on base, green on head)
| Bug | Command (fleet: mini-cmux8s for the iOS simulator, mini-cmux11s and mini-cmux8s for swift test) | Base | Head |
|---|---|---|---|
| 1 | swift test --filter ConversationReplyThreadTests (Packages/iOS/CmuxConversation) | 6 tests, 13 issues at d4111bfa | 6 pass; all 255 CmuxConversationGeometryTests pass |
| 2 | cmux_mobile_kit-test-ios.sh CmuxMobileChiefTests/ChiefPinPushTests | fails: one tap pushed 2 Chiefs (stack depth 3) at 61c4994a | passes; all CmuxMobileChiefTests pass (68) |
| 3 | cmux_mobile_kit-test-ios.sh CmuxMobileShellTests/RowSwipeActionsDrawerTests | 4 of 5 failed at 6533d5b0 | passes (7 tests) |
Observed / Not run
- Observed: before and after on an iOS 26.5 iPhone 17 Pro simulator (Demo builds of the base and the head, mock data, light and dark): the reply line in both directions, Home and Workspaces swipe-then-right-swipe, Feed swipe and buttons, and the two-pin repro app.
- Not run: the Chief flows against a hidden Mac rig over the real link. The double push needs the Cloud Chief ready and a Mac linked at once, which the rig account has no Cloud Chief for; the cause and fix are shown with the minimal repro and the unit test instead. No real-phone check (not requested).
- Not run: macOS conversation UI (unchanged code).
Changelog
- Fixed: iPhone Chief reply quotes draw their thread line from under the quoted bubble, as in Messages.
- Fixed: Opening a Chief on Home no longer pushes it twice.
- Fixed: With a row's swipe actions open, a rightward swipe closes them instead of opening the sidebar.
- Changed: Feed swipe actions use the same rounded buttons as the other lists.
No capture: native iPhone UI; simulator captures above (cmux-ci capture drives the Mac app).
🤖 Generated with Claude Code