uttrflow / uttrflow/uttrflow-swift
AI suggestion ghost text takes Uttrflow's appearance, not the field's, so it is invisible on a white page in Dark Mode
- Dominant language
- Swift
- Stars
- 4
- Forks
- 17
- Avg merge
- 3h 32m
- Merged PRs (30d)
- 277
Description
## What happens
The ghost text's colour follows Uttrflow's own appearance, not the field it is drawn over.
- `SuggestionView` draws the ghost as `.foregroundStyle(.primary.opacity(presentation.opacity))` with no background (`Sources/Uttrflow/Suggestion/SuggestionView.swift:56-59`, rows at `:78`, the dot at `:41`).
- The panel is transparent (`isOpaque = false`, `backgroundColor = .clear`, `SuggestionPanelController.swift:164-165`) and never sets its own `appearance`. So `.primary` resolves from `NSApplication.shared.appearance`, which `AppDelegate.applyAppearance` sets from Uttrflow's Light/Dark/System setting (`Sources/Uttrflow/AppDelegate.swift:1762-1769`).
- `ghostOpacity` is 0.45 (`Sources/UttrflowUX/SuggestionPresentation.swift:62`).
Worked out for the common mismatches:
| Mac or Uttrflow appearance | Field background | Ghost colour | Contrast |
|---|---|---|---|
| Dark | white web page or document | white at 45% = white | 1.0 : 1 (invisible) |
| Light | dark editor or terminal (#1E1E1E) | black at 45% over #1E1E1E ≈ #111111 | ≈ 1.1 : 1 (invisible) |
| Light | white | ≈ #8C8C8C | ≈ 3.4 : 1 |
| Dark | #1E1E1E | ≈ #838383 | ≈ 4.4 : 1 |
Most web pages and many documents stay white in Dark Mode, and many terminals are dark in Light Mode. So the mismatch is common, and in it Tab is still armed for a suggestion nobody can see.
(This is not one of the overlay stacking, screen-edge or staleness bugs already being fixed.)
## Why it matters
An invisible suggestion that still takes Tab looks to the user like Tab randomly inserting text. Low-vision users are hit hardest.
## How to reproduce
Set the Mac to Dark Mode, turn AI suggestions on, and type a remembered line into a text field on a white web page. Nothing visible appears, but Tab inserts a completion.
## Acceptance criteria
- The ghost reads against the field it sits on. For example, sample the field's text colour from `AXAttributedStringForRange` (`AXForegroundColor`, which `FocusedFieldReader.typeStyle` already fetches) and draw at a share of that, or give the ghost a subtle backing that guarantees contrast.
- Where the colour cannot be known, the choice is at least made from the screen content's luminance, not from Uttrflow's appearance setting.
- A test in `Tests/UttrflowUXTests` covers the colour decision for light-on-dark and dark-on-light.
Contributor guide
Research direction
Start with SuggestionView.swift, SuggestionPanelController.swift, AppDelegate.swift, and FocusedFieldReader.typeStyle to trace how the ghost colour and appearance are chosen. Add the colour decision coverage under Tests/UttrflowUXTests for light-on-dark and dark-on-light cases; done means suggestions remain readable when the field and Uttrflow appearances differ.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- macos, swift
- Domain
- accessibility, desktop
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100