uttrflow / uttrflow/uttrflow-swift

[P2] Restore the dictation shortcut when Settings closes or leaves the shortcut recorder

Open
#808 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Swift
Stars
4
Forks
17
Avg merge
3h 32m
Merged PRs (30d)
277

Description

Starting shortcut recording stops the live dictation shortcut controller. Closing Settings bypasses the callback that restarts it, and switching away from General leaves the recording session active after its keyboard source disappears. Ordinary navigation can therefore leave the dictation shortcut stopped even though no shortcut change was saved.

On main `533e6cbef5046f8bd681246d99ebf9ef9553fa40`:

- `SettingsViewModel.beginRecordingShortcut` calls `onShortcutRecording(true)`. In `AppDelegate.shortcutRecordingChanged`, this stops `controller`; the `false` branch calls `startWatchingForTheShortcut()`.
- [SettingsWindowController.windowWillClose](https://github.com/uttrflow/uttrflow-swift/blob/533e6cbef5046f8bd681246d99ebf9ef9553fa40/Sources/Uttrflow/Settings/SettingsWindowController.swift#L74-L79) calls `model.session.cancelRecordingShortcut()` directly. It never calls the view model's cancellation method, which emits `onShortcutRecording(false)`.
- `SettingsRootView.tab` assigns `model.session.tab` directly. The disappearing `SettingsShortcutField` only calls `stopListening()`, which stops its own keyboard source, without ending the session or notifying the app.

User paths to cover: General → Change on a shortcut → close Settings without entering a key; and General → Change → select another settings tab. Once the start callback has stopped the dictation controller, neither navigation path sends the matching resume callback. Explicit Cancel is a working control path. This report is about dictation shortcut restoration, not every global shortcut or a microphone left recording.

Validation used the unmodified production `SettingsViewModel` in an isolated Swift target, fake settings/personalisation stores, and a callback recorder. After beginning recording, executing the exact session operation from `windowWillClose` leaves `isRecording == false` but callbacks `[true]`, rather than `[true, false]`. Executing the tab button's assignment leaves `isRecording == true` and callbacks `[true]`. Calling the real view model's Cancel method produces `[true, false]` and ends recording. Two regression probes fail (three assertions); the Cancel control passes. The view/controller/AppDelegate call paths were inspected in source; no live window interaction or native hotkey test was performed.

Route window-close and tab-exit cleanup through a single view-model operation that both ends recording and restores the live controller. Test close, tab change, explicit Cancel, accepted/rejected recordings, and repeated cleanup; account for queued keyboard callbacks so a stale event cannot restore or modify a newer recorder. Preserve the previous binding whenever no replacement was accepted.

Checked all-state issues and open PRs for this lifecycle failure. Existing keyboard teardown/crash reports concern different causes.

Contributor guide

Open the contributing guide

Research direction

Start with SettingsWindowController.windowWillClose, SettingsViewModel's recording operations, SettingsRootView.tab, and AppDelegate.shortcutRecordingChanged to trace the existing cleanup paths. Run the two regression probes described in the issue and cover close, tab change, Cancel, accepted and rejected recordings, and repeated cleanup. Done means every exit path restores dictation, stale keyboard callbacks cannot affect a newer recording, and an unaccepted change preserves the previous binding.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
desktop, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.