uttrflow / uttrflow/uttrflow-swift
Home says "Listening · ready" with the ring lit when the speech model was never downloaded
- Dominant language
- Swift
- Stars
- 4
- Forks
- 17
- Avg merge
- 3h 32m
- Merged PRs (30d)
- 277
Description
## What happens
With the speech model not downloaded, the main window tells the user everything is ready.
- Home's status comes from `HomePresenter.status(blocked:speechModel:)` (`Sources/UttrflowUX/HomePresentation.swift:378-382`). It is "Not listening" only for a missing permission, and the model's own line only for a `SpeechModelLoad`. The app passes `speechReadiness.load(since:now:)` (`Sources/Uttrflow/AppDelegate.swift:61-63`, `:1405`), and `SpeechModelReadiness.load` returns `nil` for `.notInstalled` (`Sources/UttrflowUX/MenuBarPresentation.swift:32`). So with no model, Home shows **"Listening · ready"** with the ring lit (`isReady: true`), and the subtitle "Nothing dictated yet. Hold the shortcut anywhere and talk." (`HomePresentation.swift:445`).
- The Dictation page's blocking state is `MainPresenter.obstruction(in:)`, which checks permissions only (`Sources/UttrflowUX/MainPresentation.swift:181-210`). With no model its empty state invites "Hold ⌥Space anywhere and talk" (`Sources/UttrflowUX/MainDictationPresentation.swift:392-401`).
- The menu bar, on the same state, says "Setup hasn't finished" (`MenuBarPresentation.swift:388-389`), so two surfaces disagree.
Separately, "Listening · ready" is the wording at rest. Everywhere else "Listening" means the microphone is open: the menu bar's "Listening…" (`MenuBarPresentation.swift:508`) and the floating button's "Listening. Let go to finish." (`Sources/UttrflowPipeline/DictationPresenter.swift:47`). A resting status that says "Listening" suggests the microphone is always on.
## Why it matters
A user who skipped or cancelled the download (a choice onboarding offers) opens the app, sees a green "Listening · ready", holds the shortcut, and gets a failure. The page that should explain what is missing says the opposite. For a privacy-minded user, "Listening" at rest reads as the microphone being open.
## How to reproduce
Remove the installed speech model for the dev bundle, launch with onboarding already finished, and open Home and Dictation. Or in a test: `HomePresenter.page(for:)` with `speechModel: nil` and all permissions granted returns status "Listening · ready", whatever `SpeechModelReadiness` the app holds.
## Acceptance criteria
- Home and the Dictation page know when the speech model is not installed (or failed to load) and show it: status not ready, and a notice with the download action (the action itself is #671's fix; the notice can use `RecoveryAction.downloadSpeechModel` either way).
- The resting status no longer uses "Listening"; for example "Ready" (the menu bar's word) and "Not ready".
- Home, Dictation and the menu bar agree for `.notInstalled`, `.loading`, `.loadFailed` and `.ready`.
- Tests: `Tests/UttrflowUXTests/HomePresentationTests.swift:255` and `Tests/UttrflowUXTests/SpeechModelLoadingSurfacesTests.swift:50` updated, plus a case for a missing model on both pages.
Contributor guide
Research direction
Start with HomePresenter.status and page(for:) in Sources/UttrflowUX/HomePresentation.swift, then compare MainPresenter.obstruction and the empty state in MainPresentation.swift and MainDictationPresentation.swift. Run the cases in Tests/UttrflowUXTests/HomePresentationTests.swift and SpeechModelLoadingSurfacesTests.swift; done means Home, Dictation, and the menu bar agree for missing, loading, failed, and ready model states, with updated wording and missing-model coverage.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- macos, swift
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100