Skip to content

feat(mobile): native iOS composer actions and dictation gestures - #2576

Merged
DoodleBears merged 1 commit into
mainfrom
codex/2575-ios-native-composer-controls
Oct 22, 2026
Merged

DoodleBears merged 1 commit into
mainfrom
codex/2575-ios-native-composer-controls

Conversation

@DoodleBears

@DoodleBears DoodleBears commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Closes #2575 · Agent claim · Follow-up to #2567

Why the change

iOS composer controls should use native button feedback and native touch recognition while preserving the existing text entry, send and dictation workflow.

Special things to note

  • Merge risk: two-way door — revert this commit; medium blast radius — iOS message entry actions and dictation, with missed/duplicate taps or recording cleanup as the main risks. Review native hit targets, gesture termination and the unchanged owning speech/send callbacks.
  • The input remains the existing UIKit-backed React Native TextInput; Android retains its previous controls and gestures. No dependency, native configuration, speech backend, message contract or automatic send changes. The standalone return button has glass and opaque fallbacks; embedded buttons share the composer island.
  • Human authorized merge after green CI and before unified native QA on 2026-10-22; this PR merged as 46bb44b2ff4f1ca9ca682ac9dce423a310c3bb2d. Batch54 has partial native UI evidence below; recording startup remains unresolved and full native acceptance is pending. Batch 51 exercised the earlier fix(mobile): unify the iOS message composer island #2567 layout and encountered immediate dictation termination after permission, so it does not validate these new gestures or prove that audio issue fixed. Physical microphone, VoiceOver, Release feel/performance and Android acceptance are separate.

Change outline

Batch56 — native startup reached; recording still fails

Latest native follow-up on candidate 914bfa57ed048 containing merged #2595, not a rerun of the earlier gesture-only heads. iOS 27 Simulator, English, signed Debug binary 55de08577ac5, strict native fingerprint b3e74315f0c2; full identities and image hashes are in the provenance report.

actual center / edge tap → native Button → available + permission granted
  → native start / audiostart
  → audio-capture after 21 / 22 ms → end at 30 / 31 ms from call:start
  → failure notice; no app stop or abort

FAIL: sustained recording. The centered button now reaches speech startup, but the native task ends immediately; root cause remains undetermined. An uninstrumented real tap reaches the same failure UI. Valid v2 observations stayed current and fully restored. Initial Batch56 traces from the withdrawn v1 observer were contaminated by Hermes eval loop-closure binding and are excluded. No Simulator-wide limitation is inferred from the remaining failure.

Actual candidate failure — light, large, 402 × 874, software keyboard absent. The microphone is centered. This is not a successful Listening state or a matched failure-state Before/After pair; the earlier Batch55 baseline above/below retains its own source, fixture and missing-start result.

Batch56 actual recording failure after startup; centered native microphone

Scoped interaction result: a synthetic three-line draft survived expand/done, attachment-tray open/close and six theme/text-size changes, then was sent once with exact canonical Host text and a real direct DeepSeek reply 42. The software keyboard did not appear, so keyboard avoidance is UNTESTED. Later draft lines are visible in the larger-text fields; full editing, caret and scroll acceptance is not inferred.

Batch56 synthetic multiline draft sent once; real reply 42

Six candidate draft captures — keyboard absent; no matched Batch56 Before matrix

Light: large, accessibility-large, accessibility-extra-extra-extra-large, respectively.

Candidate light large draft; keyboard absent Candidate light accessibility-large draft; keyboard absent Candidate light maximum accessibility text draft; keyboard absent

Dark: the same three OS text sizes, respectively.

Candidate dark large draft; keyboard absent Candidate dark accessibility-large draft; keyboard absent Candidate dark maximum accessibility text draft; keyboard absent

Still unaccepted: stable Listening, second-tap Stop, explicit Cancel, recording interruption/retry, keyboard clearance, full matched Before/After, physical microphone, VoiceOver and Android. The bounded exact-app-PID native log read parsed 1003 records but yielded no allowlisted error facts; that does not locate the native cause. New source repairs, if required, need their own branch/PR and a new owner-cut test batch. No product source change or new merge/release authorization is implied by this evidence update.

Evidence follow-up: t3code/local/mcp:dbfb1c61-ea88-4449-82fe-bce3ae129811, successor claim. Earlier Batch54/55 reports below remain historical and retain their stated limits.

 Composer (stable input and existing action callbacks)
- React Native Pressable: attach/cancel, Send, Expand, return-to-latest
+ iOS SwiftUI Button + SF Symbol, native press feedback
+   plain inside the island; system glass for return-to-latest
- iOS PanResponder dictation
+ iOS native gesture recognizer + UI-thread threshold decisions
+   boundaries → existing start / stop / cancel callbacks
  Android Pressable and PanResponder remain in platform fallback files
Original recording interaction Original intent (superseded on iOS by #2595)
Short tap Start and keep listening; next tap stops
Hold ≥300ms, release Stop and keep transcript for editing
Drag left ≥80pt Cancel once, discard dictated text
Drag up ≥60pt Lock; release keeps listening
System interruption / second finger before lock Cancel the held recording
Accessibility activation Start / stop without dragging

Verification: 55 focused tests passed (composer actions/dictation, native gesture boundaries, style and system-text guards); mobile typecheck and repository lint passed (existing unrelated lint warnings remain). Expo's pinned Babel preset compiles all eight gesture/reaction worklets. All applicable CI39736127350 jobs also passed, including mobile verification and isolated Host smoke. These are automated checks, not native device acceptance.

Batch54 — native composer, light, 402 × 874 pt

Before — React Native action controls, with the same integrated island and two-line draft.

Before: native iOS composer, light appearance, multiline draft and keyboard

After — SwiftUI action buttons in the same island; the existing text input and draft are preserved.

After: native iOS composer with SwiftUI action buttons, same draft and keyboard

Actual iOS 27 Simulator captures, English, system text size large, same synthetic chat, viewport and visible software keyboard; the blinking caret/status-bar return affordance can differ. Before d402bb92d80a removes only #2576 from After ea36821ea929. Both share qualified native fingerprint 2ab2d67bec3a and installed executable SHA256 cbf75e97a9776956bca0bcdf081a0dc5da2a6ba858d5bf4fbd26a58e3e5c62e7 (native build source 683b0cbe9c0f). This Human-approved compatible comparison excludes later main splash inputs: scoped evidence, not latest-main/release acceptance. Exact provenance and partial results.

Draft/keyboard/multiline, expansion/return and attachment-menu interactions were captured on both versions. Before send/direct DeepSeek reply and exact canonical message passed. Further After send/attachment/return-button acceptance is still being completed by the sole runner. The initial dark keyboard mismatch has been corrected in the matched pair below. Physical microphone, VoiceOver, CJK IME and Android remain untested.

Recording investigation remains open: actual short taps and an 800ms hold reached onHeld/finalization but not onStart or native speech; the callback observer remained installed. The pinned RNGH 3.3 iOS touch-down event precedes BEGAN, and early activation is ignored. Repair #2592 moves activation to onBegin; its regression reproduces the missing-start failure and passes after the fix, but unified native retesting is pending. This is distinct from Batch51's historical immediate termination. Stop, lock, cancel and transcription cannot be accepted until recording starts. No success-state recording screenshot has been fabricated.

Batch54 — native composer, dark, 402 × 874 pt

Before — React Native action controls, multiline draft and dark software keyboard.

Before: native iOS composer in dark appearance with multiline draft and keyboard

After — SwiftUI action controls with the same draft, caret position and dark keyboard.

After: native iOS composer with SwiftUI controls and matching dark keyboard

Both are actual native Batch54 captures with the same English locale, system text size large, 402 × 874 pt viewport, synthetic chat, two-line draft, end-position caret and keyboard candidates. Before d402bb92d80a / After ea36821ea929; native fingerprint 2ab2d67bec3a, executable cbf75e97a977…, matching the full binary identity above. Updated provenance and limitations. These compare composer controls; they are not evidence of successful recording or latest-main/release acceptance.

Task: codex/local/01a1621c-a81b-7670-86be-1ddaf404c2e2.

Batch55 update — recording startup still fails

This result supersedes the earlier pending-retest status: native Batch55 on source 87724a3b58d5 (including #2592) still produced only onHeld/finalize on real short tap and 800ms hold, with no onStart, availability/permission calls or native speech events. The observer stayed installed/current, the loaded Hermes code contains #2592, and both observers were stopped/restored. Historical immediate-end remains a separate unlocated symptom; no successful recording state is claimed.

The Human also reported the offset/clipped microphone. Merged repair #2595, issue #2594, replaces the iOS manual gesture with a centered native SwiftUI button (tap start / tap stop, explicit cancel). Its Batch56 native result is recorded above; sustained recording remains unaccepted.

Actual failure state — light, large text, 402 × 874 pt, keyboard hidden; Debug strict fingerprint b3e74315f0c2, final signed executable 55de08577ac5. This image is a failure baseline, not an After-success comparison. Provenance.

Batch55 failure: microphone remains idle and clipped after actual tap and hold

Issue: #2575

Agent-Task: codex/local/01a1621c-a81b-7670-86be-1ddaf404c2e2
@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 23 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 1ed70dda-237d-4ba3-b0be-1c9d57c5e9d3
📥 Commits

Reviewing files that changed from the base of the PR and between 55e0ce9 and 9b68129.

📒 Files selected for processing: 15
  • .agents/skills/verify-deepseekbot/features/phone-composer.md
  • apps/mobile/DESIGN.md
  • apps/mobile/src/ui/chat/composer-action.ios.tsx
  • apps/mobile/src/ui/chat/composer-action.tsx
  • apps/mobile/src/ui/chat/composer-chrome.ios.tsx
  • apps/mobile/src/ui/chat/composer-chrome.tsx
  • apps/mobile/src/ui/chat/composer-symbol.ts
  • apps/mobile/src/ui/chat/composer.tsx
  • apps/mobile/src/ui/chat/jump-to-latest.tsx
  • apps/mobile/src/ui/chat/native-composer-button.ios.tsx
  • apps/mobile/src/ui/chat/voice-button.ios.tsx
  • apps/mobile/src/ui/chat/voice-button.tsx
  • apps/mobile/test/composer-chrome-ios.test.tsx
  • apps/mobile/test/voice-button-ios.test.tsx
  • changes/2575-ios-native-composer-controls.md
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@DoodleBears
DoodleBears marked this pull request as ready for review October 22, 2026 06:58
@DoodleBears
DoodleBears merged commit 46bb44b into main Oct 22, 2026
10 checks passed
DoodleBears added a commit that referenced this pull request Oct 22, 2026
Agent-Task: codex/local/01a1621c-a81b-7670-86be-1ddaf404c2e2
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.

iOS: native composer actions and dictation gestures

1 participant