uttrflow / uttrflow/uttrflow-swift
AI suggestions draw and take Escape and arrows while an input method is composing, even where the field reports marked text
- Dominant language
- Swift
- Stars
- 4
- Forks
- 17
- Avg merge
- 3h 32m
- Merged PRs (30d)
- 277
Description
## What happens
AI suggestions are drawn and their keys armed while an input method is mid-composition, even where the field itself says it is composing.
- `FocusedFieldReader.snapshot` computes `isComposing` from the field's `AXTextInputMarkedRange` and the input source (`Sources/UttrflowContext/FocusedFieldReader+System.swift:155-157`, `Sources/UttrflowPredict/Composition.swift:27-33`).
- `Quieting.reason` deliberately ignores it ("composition is deliberately not consulted", `Sources/UttrflowPredict/Quieting.swift:14-26`).
- `Docs/predict-ime.md` explains why the gate is off: the input-source *fallback* (`MarkedText.unanswered`) silenced CJK, Vietnamese and Hindi-transliteration users in every browser, Electron app, terminal and single-line field. But that bill is for the fallback only. The same document measures `AXTextInputMarkedRange` as a clean, definite signal in AppKit text views (Notes, Mail, TextEdit), where `MarkedText.present` means composition is happening.
While a suggestion is shown, `KeyRouting.decision` swallows Escape (minimise) and, for a list, Down/Up (`Sources/UttrflowPredict/KeyRouting.swift:85-107`). Those are the keys a Japanese or Chinese input method uses to cancel a composition and walk its candidate window. The typed line also includes the uncommitted marked text (`AXValue` holds it, per the doc), so the suggestion is matched against a reading that is about to change.
## Why it matters
For a Japanese, Chinese or Korean typist in Mail or Notes, pressing Escape to cancel a conversion instead dims an AI suggestion. Arrow keys may move Uttrflow's list instead of the IME candidates. That disrupts the input method, which must never happen.
## How to reproduce
In TextEdit with Kotoeri (Romaji) on, and a remembered line that begins with the romaji being typed, compose text until a ghost appears, then press Escape. The ghost minimises and the composition is not cancelled. (Not run live here: no input method is enabled on the audit machine, see the doc's residual risk 5. The code path is verified.)
## Acceptance criteria
- When the field answers `MarkedText.present`, nothing is drawn and nothing is armed. A named `Quieting.Reason` (for example `composing`) is logged.
- `.unanswered` keeps today's behaviour, so the fallback's bill is not paid again.
- Tests in `Tests/UttrflowPredictTests` cover present → quiet, absent → normal, unanswered → normal. `Docs/predict-ime.md` is updated.
Contributor guide
Research direction
Start with FocusedFieldReader+System.swift, Composition.swift, Quieting.swift, and KeyRouting.swift, then read Docs/predict-ime.md to understand the marked-text signals. Run the relevant Tests/UttrflowPredictTests cases before changing behavior. Done means present composition quiets suggestions and logs a named reason, absent and unanswered retain normal behavior, and the documentation is updated.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- accessibility, desktop, documentation, testing-qa
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100