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

Open
#535 0 comments 0 reactions 0 assignees View on GitHub
accessibility area:ai-suggestions bug P2
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.