uttrflow / uttrflow/uttrflow-swift
VoiceOver reads shortcut rows by their code names ("pasteLastTranscript shortcut") and never hears that the field is recording
- Dominant language
- Swift
- Stars
- 4
- Forks
- 17
- Avg merge
- 3h 32m
- Merged PRs (30d)
- 277
Description
## What happens
Each shortcut row in Settings → General is a `SettingsShortcutField`, whose accessibility label is built from the enum's raw value:
```swift
// Sources/Uttrflow/Settings/SettingsControlView.swift:222-224
.accessibilityElement(children: .contain)
.accessibilityLabel("\(action.rawValue) shortcut, \(keys.joined(separator: " "))")
```
`ShortcutAction`'s raw values are `dictate`, `clipboard`, `pasteLastTranscript` and `copyLastTranscript` (`Sources/UttrflowCore/Keyboard/ShortcutAction.swift:4-13`). So VoiceOver reads "pasteLastTranscript shortcut, ⌃ ⌘ V" (spoken as one run-together word), while the visible label is "Paste last transcript" (`Sources/UttrflowUX/ShortcutRegistry.swift:41`).
Two more gaps in the same label:
- A shortcut with nothing bound draws "None" (`SettingsControlView.swift:201-204`), but the label ends "shortcut, " with nothing after it.
- While recording, the row shows "Press the new shortcut" (`SettingsShortcutRecorder.prompt`) and the Change button becomes Cancel, but the label still reads the old keys, so a VoiceOver user is not told the field is listening or that Escape cancels.
## Why it matters
Changing a shortcut is one of the first things a keyboard or VoiceOver user does. They hear code identifiers instead of the names on screen, and no feedback when the field starts listening.
## How to reproduce
Open Settings → General with VoiceOver on and move to the shortcut rows. Or read line 224.
## Acceptance criteria
- The label uses `ShortcutRegistry.label(for: action)`: "Paste last transcript shortcut, Control Command V" or similar.
- With nothing bound it reads "…shortcut, none".
- While recording it reads "…shortcut, press the new shortcut, or Escape to cancel", and the change is announced when recording starts.
- Modifier glyphs are read as words if VoiceOver does not already speak them (check with VoiceOver; the onboarding keycaps have the same question).
- If practical, move the label text into `UttrflowUX` (for example a static on `SettingsShortcut`) so a test in `Tests/UttrflowUXTests/SettingsShortcutTests.swift` can cover all three states.
## Where to start
- `Sources/Uttrflow/Settings/SettingsControlView.swift:180-225`.
- Run `make verify` (export `DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer` first) and read CONTRIBUTING.md.
**Size:** S, about 1–2 hours.
Contributor guide
Research direction
Start in Sources/Uttrflow/Settings/SettingsControlView.swift:180-225 and compare the current label with ShortcutRegistry.label(for: action), SettingsShortcutRecorder.prompt, and the bound-key states. Check whether the label can move into SettingsShortcut and cover it in Tests/UttrflowUXTests/SettingsShortcutTests.swift. Run the relevant tests or make verify, then confirm VoiceOver announces the friendly name, none state, and recording/cancel state.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- macos, swift
- Domain
- accessibility, desktop
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 84/100