uttrflow / uttrflow/uttrflow-swift
A suggestion hidden for lack of caret space still reports isShowing and keeps idle polling active
- Dominant language
- Swift
- Stars
- 4
- Forks
- 17
- Avg merge
- 3h 32m
- Merged PRs (30d)
- 277
Description
## Problem
On main `5c310667cd35c44abd8bbd26c19b0905471287b4`, `SuggestionPanelController.isShowing` tests whether the requested suggestion is non-silent, even when rendering deliberately hides its native window. The coordinator uses that flag to keep its one-second polling timer running after the activity window expires.
This happens when an otherwise valid caret is exactly at the known field right edge: there is no room for a ghost, `SuggestionGeometry.anchor` returns nil, and `render()` orders the panel out. The request remains `.certain(...)`, so `isShowing` remains true.
## Evidence
- [SuggestionPanelController.swift](https://github.com/uttrflow/uttrflow-swift/blob/5c310667cd35c44abd8bbd26c19b0905471287b4/Sources/Uttrflow/Suggestion/SuggestionPanelController.swift#L86-L87): visibility is inferred only from the request. `render()` and `resize()` can order the window out when placement fails without changing that request.
- [SuggestionTicking.swift](https://github.com/uttrflow/uttrflow-swift/blob/5c310667cd35c44abd8bbd26c19b0905471287b4/Sources/Uttrflow/Suggestion/SuggestionTicking.swift#L26-L34): `isShowing` bypasses the elapsed activity-window check.
- [SuggestionCoordinator.swift](https://github.com/uttrflow/uttrflow-swift/blob/5c310667cd35c44abd8bbd26c19b0905471287b4/Sources/Uttrflow/Suggestion/SuggestionCoordinator.swift#L245-L252): passes the panel flag to the ticking rule; admitted ticks call `wake(.tick)`, whose turn reads the focused field.
A macOS test using the unmodified production controller, SwiftUI view, geometry, and ticking rule:
1. Obtain a real screen visible frame and construct a valid caret inside it.
2. Set the field rectangle to end exactly at the caret x coordinate, with width 100 points.
3. Show `.certain("meeting")` using `.inlineGhost` placement.
4. Read `panel.window.isVisible` and `panel.isShowing`.
5. Record an activity in `SuggestionTicking`, then call `tick` with a timestamp one hour later and the panel flag.
Observed:
```text
windowVisible=false isShowing=true
tickAfterOneHour=true
```
Both expected-false assertions fail. A control with sufficient space shows the panel and reports true, then reports false after `hide()`. All 23 existing suggestion surface, log, ticking-policy and pressure-policy tests passed in the isolated harness.
The hour is a supplied test timestamp, not a wall-clock energy experiment. This verifies native visibility and the production timer decision; it does not measure CPU use or run a full external-app Accessibility session.
## Impact
With a retained suggestion that cannot be placed, the idle timer is admitted even though there is no visible surface to follow. Its coordinator can continue unnecessary focused-field reads while the user is idle. Priority P2.
## Acceptance criteria
- Make the visibility signal consumed by ticking reflect whether rendering actually shows a surface, including placement failure and hidden presentation paths.
- Preserve requested content as needed for legitimate later redraws, without treating a hidden request as visible.
- Cover a caret at the field right edge, a visible suggestion, explicit hide, and a later valid placement.
- Verify that polling stops after the activity window when placement leaves nothing visible and restarts on the next activity.
## Related work
This is a specific remaining visibility-state defect after closed #374 introduced activity-bounded polling. That original unconditional-timer behavior is no longer the implementation. #548 concerns bounding long ghosts; this issue concerns the idle timer after the current geometry correctly refuses to draw one.
Contributor guide
Research direction
Start with Sources/Uttrflow/Suggestion/SuggestionPanelController.swift, SuggestionTicking.swift, and SuggestionCoordinator.swift, then run the existing suggestion surface and ticking-policy tests. Reproduce the caret-at-field-right-edge case from the issue and add coverage for visible, hidden, explicit-hide, and later-valid-placement states. Done means native visibility and polling agree, including after the activity window expires.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- macos, swift
- Domain
- desktop, performance
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100