Skip to content

fix(voice): keep on-device transcription alive across transient window-hide#26

Merged
leinss merged 1 commit into
mainfrom
feature/dictation-accept
Jul 14, 2026
Merged

fix(voice): keep on-device transcription alive across transient window-hide#26
leinss merged 1 commit into
mainfrom
feature/dictation-accept

Conversation

@leinss

@leinss leinss commented Jul 14, 2026

Copy link
Copy Markdown
Owner

What

On-device (sherpa-onnx / Parakeet) dictation showed "Recording", but pressing accept closed the overlay and inserted nothing — no error, every time, with the model marked Ready.

Root cause: the lifecycle cancel path (onWindowHidden / onFinishInputViewcancelVoiceRecordingIfCapturing) aborted the in-flight local decode before it could commit. That abort reached finishTranscription with no result and no error, so the overlay closed silently.

Changes

  • VoiceInputManager.cancelForLifecycle(): a lifecycle-driven cancel no longer aborts an in-progress on-device transcription — it finishes and commits. Live recording and cloud uploads still cancel on dismiss (no stale-field insert). The X button and onDestroy still hard-cancel.
  • LocalSherpaEngine: a native recognizer-init failure now throws a typed LocalModelLoadException (mapped to the actionable "re-download the model" message) instead of being swallowed into a generic error; the pre-warm worker is guarded against it.
  • Always-log a cancelled in-flight transcription for field diagnosis.
  • Settings: the On-device models row icon rendered black in dark theme (a raster icon bypasses IconOrImage's vector-only tint); replaced with a tintable vector.

Test plan

  • make check — 355 unit tests / 0 failures / 7 skipped, lint clean
  • Install on device (Play internal), on-device dictation: speak → accept → text inserts
  • X button still discards; dismissing keyboard mid-cloud-upload still aborts
  • On-device models settings icon is white in dark theme, dark in light theme

…w-hide

A completed local (sherpa-onnx) transcription was being discarded before it
committed: the lifecycle cancel path (onWindowHidden / onFinishInputView ->
cancelVoiceRecordingIfCapturing) aborted the in-flight decode, so pressing the
accept button closed the overlay and inserted nothing, with no error shown.

- Spare an on-device transcription from the lifecycle-driven cancel so it can
  finish and commit (it is fast and offline). Live recording and cloud uploads
  still cancel on keyboard dismissal, preserving the no-stale-insert behaviour.
  Explicit cancel (X button) and onDestroy still abort unconditionally.
- Surface a native recognizer init failure as a typed LocalModelLoadException so
  the user gets an actionable "re-download the model" message instead of a
  generic "Transcription failed"; guard the pre-warm worker against it.
- Always-log a cancelled in-flight transcription so field failures are
  diagnosable from a release build.
- Settings: the On-device models row icon rendered black in dark theme because a
  raster icon bypasses IconOrImage's vector-only tint path; use a tintable
  vector so it matches the other rows.

Adds a pure unit test for the lifecycle-cancel decision and an STT case for the
load-failure message mapping.
@leinss
leinss merged commit 918d4e1 into main Jul 14, 2026
1 check passed
@leinss
leinss deleted the feature/dictation-accept branch July 14, 2026 20:56
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.

1 participant