Repository navigation
fix(voice): start iPhone dictation from a tap, and stop asking for the mic on On in a home-screen app (#2343) - #2370
Conversation
…e mic on On in a home-screen app (#2343) iOS WebKit starts SpeechRecognition only from a user activation, and a touch's pointerdown is none, so every touch take on the browser engine ended in "not-allowed", shown as "Microphone permission was denied". A touch on the browser engine now starts and finishes the take from the tap's click; a mouse and the server engine keep press-and-hold. Choosing On in Settings asked getUserMedia at once, which closed or reloaded an iPhone home-screen app; there the first mic tap asks now. "service-not-allowed" (the device's recognizer is off or lacks the language) gets its own message, and the iOS app declares NSSpeechRecognitionUsageDescription for WKWebView's speech recognition.
…nter releases a hold (#2343) Review round (Codex Sol): a touch tap now picks start or finish at its pointerdown, so a take that ended before the click is not started again, and a pointerup from another pointer (a right-click) no longer finishes a hold. The browser engine's mic is labelled "Dictate", since a touch cannot hold it. The changelog no longer claims the home-screen reload is confirmed fixed.
…Enter or Space (#2343) Codex Sol final round: a touch hold on the stop button ends with no click, so its "finish" stayed pending and a later keyboard activation finished an idle take instead of starting one. A tap's gesture now counts only for a click within 500 ms of its pointerup.
|
Warning Review limit reachedYou'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 11 minutes. View limit details
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. Comment |
|
CodeRabbit hit its review limit on this PR, so no CodeRabbit review ran. Two reviews ran in its place, both adversarial and read-only. Their findings and how each was resolved: omo-native-code-reviewer (on 87da4206): APPROVE
Codex Sol 6.1 Fast xhigh, round 1 (on 87da4206): REQUEST_CHANGES
Codex final round, on the delta only (on 84153973): REQUEST_CHANGES
|
|
Merging b5776e6 (closes #2343, thanks @osamahbeig for the report). iOS starts speech recognition only from a user activation, and a touch's pointerdown is none, so with the browser engine a touch now starts or finishes a take from the tap's click (a mouse, and the server engine, keep hold-to-talk); only the pointer that pressed may release a hold. In an iPhone home-screen app, Settings > Voice input On no longer calls getUserMedia (the request that closed or reloaded it); the first mic tap asks instead. service-not-allowed gets its own note, and the iOS app gains NSSpeechRecognitionUsageDescription (takes effect with the next iOS build). Reviewed by the merge pass: the change is confined to MicButton's pointer handlers, the Settings request and one new error key (translated in ja/ko/zh); 4 fail-first tests. Known limits (from the PR body): a whole recording through to text and the home-screen reload were not observed in the Simulator and need a real-iPhone check; with the browser engine a touch hold no longer does push-to-talk (Android Chrome included), a tap works everywhere; the home-screen Settings page no longer shows the denied note (the first tap does). Unreproduced, below HIGH: with the server engine, two overlapping touches whose first click lands while the second is still down can start a second take; that path is unchanged from main. |
## Release 0.4.21 The release PR, prepared by step 1 of [docs/development.md#releasing](https://github.com/devswha/ovis/blob/main/docs/development.md#releasing), now re-folded on main `1ef6ea329108a0985b823e8235b03daddcd0a5f4` after the owner held 0.4.21 for more work. The merge pass reported the 0.4.21 queue empty. **Do not merge yet:** the owner judges the final build before release. No tag and no Release run from this PR's author. - **Version:** `version` is 0.4.21 in `package.json` and `herdr-plugin.toml`. - **Changelog:** `bun scripts/changelog-fold.ts 0.4.21 2026-10-30` folded 63 fragments into the 2026-10-30 section of `CHANGELOG.md` (71 entries), linked their PRs and deleted the fragments; `changelog.d` holds only `README.md`. - `by @login` on every outside contributor's PR link: #2361 @eddieparc, #2333 @IdrisGit, #2355 @imurfutur3, #2341 @sulthonzh, #2359 @YuuuXie, #2377 @IdrisGit, #2269 @CharleoYang, #2405 @jpcarranza94, #2414 @homebodify, #2417 @cheol-95, #2418 @eddieparc, #2435 @cheol-95. - Two outside PRs had no fragment and got an entry by hand: #2341 (OpenCode background commands, Added) and #2359 (installer test timing, Maintenance). - Issue reporters are credited in their entries: @blaker3115-Agri (#1488), @Hyd3-14 (#2026), @osamahbeig (#2343), @Shunamxiao (#2329), @matiemer (#2362), @kilhyeonjun (#2344), @eddieparc (#2360), @Ploppy3 (#2236), @sulthonzh (#2340), @WOULDU-pres (#2056), @odfalik (#2038), @aNNdii (#511), @Noobear (#2281, fixed by #2424); #2435 fixes @cheol-95's own #2434. - Two fragments ran past the changelog's width (#2418, #2269); their entries are re-wrapped, words unchanged. #2382's entry reads "OpenCode's installer puts its folder first" (reviewed earlier on this PR). - Maintainer PRs without a fragment and without a user-facing change have no entry: #2353 (check tool), #2371 (guide docs), #2396 and #2397 (tests), #2403 and #2433 (server and client transport seams, no behaviour change), #2437 (test-only fix for #2435's check). #2438's own entry (the sidebar selection marked by its fill again) is dropped: the stroke #2352 added never shipped, so users see no change; #2352's entry keeps only the Windows high-contrast Highlight bar. - **Release summaries:** en, ko, ja and zh each have 5 new, 5 improved and 5 fixed lines. Since the first review: New lists the in-browser Claude Code / Codex sign-in (#2428) in place of the tab/workspace shortcut line; Improved lists Settings → Agents (#2427) in place of the sidebar fold line; Fixed lists the Codex wrong-session fix (#2426, #2430) in place of the phone screenshot paste line. The Artifacts and Claude status-line lines keep their reviewed wording (turned on in Settings). Not in 0.4.21: #2420 (Codex chat behind Kiro), #2065 (Portal sign-in) and #2436 (relay foundations) stay open for a later release. - **Remote bundle:** `REMOTE_BUNDLE_VERSION` rises from 39 to 40 because the bundle inputs (`server/`, `shared/`, `patches/`) changed since remote-v39. After merge: push `remote-v40` on the merged commit and wait for the Remote PC bundles workflow before running Release. - **iOS:** `CURRENT_PROJECT_VERSION` stays 3 (#2370 changed `Info.plist`; build 3 is not known to be uploaded yet). ## Validation - `bun scripts/release-guards.ts --base v0.4.20`: "Release 0.4.21: remote bundle, telemetry Worker and release metadata are in order." - `dori heavy … bun run check fast` on this branch: workflow syntax, generated types and themes, typecheck, build, unit tests all ok. Unit: 9166 pass, 0 fail, 36 skip in 591 files. - Every PR in `v0.4.20..main` was pre-run for the owner's hands-on guide on isolated copies (own herdr session, fresh state, never the owner's live session). Items that need a real device (iPhone tap dictation #2370, dictation keyboards #2386, the installed desktop app's title-bar overlay #2381) are left for the owner's final check. This is a release-metadata PR: it changes no code path. Per the release steps, merge it after CI and the owner's go, then push `remote-v40`, wait for its bundles, and run Release with `0.4.21`.
Closes #2343. Thanks to @osamahbeig for the report and the diagnosis.
What was wrong
MicButtonstarted a take onpointerdown. iOS WebKit letsSpeechRecognition.start()run only from a user activation, and a touch'spointerdownis not one. On the iOS 27 Simulator, a tap logsnavigator.userActivation.isActiveas false onpointerdown/touchstartand true onpointerup/touchend/click. A 900 ms hold is false throughout and fires no click. So every touch take ended innot-allowed, which we showed as "Microphone permission was denied". The click fallback never ran for a touch, because it skips clicks within 500 ms of a pointerup.getUserMediaright away. Nothing else in that path differs from a Safari tab.Change
MicButton: with the browser engine and a non-mouse pointer (touch or pen), the tap's click starts or finishes the take. Which of the two is decided atpointerdown, so a take that ends on its own before the click is not started again. A mouse keeps press-and-hold and tap-to-toggle. The server engine (MediaRecorder) keeps press-and-hold on touch too: itsgetUserMediaand AudioContext resume work frompointerdown, and the Simulator shows the AudioContext reachingrunning. Only the pointer that pressed may release a hold, so a right-click'spointerupno longer finishes a take. The browser engine's mic is labelled "Dictate" instead of "Dictate (hold)".navigator.standalone), On no longer asks for the microphone at once; the first tap of the mic asks.service-not-allowed(the device's recognizer is off, i.e. Siri & Dictation, or has no model for the language) now says "Speech recognition is off or unavailable on this device" instead of blaming the permission. Translated into ko/ja/zh.NSSpeechRecognitionUsageDescriptioninInfo.plist. WKWebView's speech recognition asksSFSpeechRecognizerfor access from the app process, and an app without that key is terminated when it asks. Mirrored inapp/store/ios.md.Design reference: the Claude and ChatGPT iOS apps, and Safari's own dictation fields, start dictation with a tap and stop it with a second tap; a hold is not their gesture. A mouse keeps push-to-talk here because desktop browsers allow it.
Verification
src/components/VoiceInput.test.tsxfailed on main'sMicButton(presson touchpointerdown). Its stop-tap and right-click cases failed on the first fix commit. All pass now.src/lib/voice.test.tspinsservice-not-allowed->speech_unavailable.bun run check fastis green on b5776e6 (rebased on ddc74b0): 8666 unit tests pass, 0 fail; typecheck, build and generated types are clean.getUserMedia. On the probe page, a touchpointerdownstart runs withisActive=false.pointerdown(isActive=false) ->pointerup->click->SpeechRecognition.start isActive=true. The pill shows "Ready…" and the keyboard stays closed.getUserMedia, and the app does not reload. A mic tap then starts recognition from the click (isActive=true).Known limits
getUserMediakept its sheet up for 6 minutes with no reload. The Settings change removes the only request that path made. Whether the first mic tap in a home-screen app now triggers the same reload on a real iPhone is unconfirmed, and needs a check on the device (the reporter offered to test a build).Reviews