Files
kandev/docs/plans/changes-file-action-feedback/plan.md
T
Carlos Florêncio 256c0fc04d fix(web): keep change action spinners visible (#3339)
Co-authored-by: Jelloeater's Agent <agent@jello.dev>
Co-authored-by: Henry <nova28@users.noreply.github.com>
Co-authored-by: nova28 <17953305+nova28@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 05:37:31 +00:00

133 lines
5.3 KiB
Markdown

---
created: 2026-09-03
status: implemented
requirements:
- REQ-UI-CHANGES-FILE-ACTION-FEEDBACK-001
system_design:
- ../../specs/ui/system-design/changes-file-action-feedback.md
legacy_specs: []
---
# Implementation Plan: Keep Changes File Action Feedback Visible
## Overview
Make per-file stage and unstage pending feedback override the tree row's idle
hover swap. One focused work order first captures the failure by pausing the
worktree request in Chromium, then makes the minimum shared component change
and verifies the completed stage and unstage transitions.
## Confirmed root cause
- `StageButton` correctly renders `IconLoader2` whenever `FileRow.isPending` is
true.
- In tree mode, `TreeModeFileActionSlot` wraps that button in a fine-pointer
layer whose visibility is controlled only by `group-hover` classes.
- The adjacent `FileIcon` also follows only the inverse hover rule. It does not
consider `isPending`.
- Leaving the row therefore makes the action layer, including its spinner,
transparent and restores the file-type icon even though the repository/path
pending key remains active.
- Coarse-pointer rows do not receive the hover-only classes, so their action
and spinner already remain visible.
## Scope
### In scope
- Give pending state precedence over the fine-pointer file-icon/action hover
swap.
- Keep the existing spinner visible after pointer leave for both stage and
unstage operations.
- Preserve per-repository, per-path pending-state scoping.
- Add deterministic Chromium coverage that holds each WebSocket request open,
leaves the row, and observes the pending and completed states.
- Preserve the existing coarse-pointer action visibility and touch sizing.
### Out of scope
- Git operation dispatch, status polling, WebSocket contracts, and pending-key
lifecycle changes.
- Bulk actions, discard/edit feedback, cancellation, notifications, and new
copy.
- Changes-panel layout, path truncation, navigation, or mobile composition.
## Technical approach
Update `TreeModeFileActionSlot` in
`apps/web/components/task/changes-panel-file-row.tsx` so the fine-pointer idle
hover classes apply only when `isPending` is false. While pending, keep the
file-type icon hidden and the spinner layer visible independent of the row's
hover state. Continue rendering the same `StageButton`; do not introduce
component-local request state.
Extend `apps/web/e2e/tests/git/git-changes-panel.spec.ts` with a narrow
WebSocket transport controller patterned after the existing request-pause
helpers. It shall pause only the armed `worktree.stage` or
`worktree.unstage` frame and forward all other frames. The regression creates a
target file (`pending-action.txt`) and a sibling file (`pending-sibling.txt`),
starts each action from the UI, waits until the request is paused, moves the
pointer to a neutral location, and asserts the existing spinner is still
visible only for the target row before releasing the request and observing the
file's new section.
## Tests
- **AC-UI-CHANGES-FILE-ACTION-FEEDBACK-001.1 through .4:** the focused
Chromium scenario proves spinner visibility for both request types after
pointer leave, per-row scoping, and the final staged/unstaged section.
- **AC-UI-CHANGES-FILE-ACTION-FEEDBACK-001.5:** the existing
`changes-panel-file-row.test.tsx` coarse-pointer assertion and
`mobile-changes-panel.spec.ts` stage/unstage path remain unchanged and green.
## E2E tests
- Add `keeps stage and unstage progress visible after pointer leaves` to
`apps/web/e2e/tests/git/git-changes-panel.spec.ts` in the Chromium project.
- No new Pixel 5 case is planned because the coarse-pointer branch already
keeps the action slot visible and this work changes neither its markup nor
its interaction. Existing mobile coverage exercises both stage and unstage
controls through the shared row.
## Work orders
- [x] [Task 01: Keep pending file actions visible](task-01-keep-pending-file-actions-visible.md)
## Dependency order
```text
Task 01
```
The browser regression and component correction share one presentation
boundary and proceed sequentially through Red-Green-Refactor.
## Verification results
- RED: the focused Chromium scenario failed after pointer leave because the
pending action layer reached `opacity: 0` while `worktree.stage` remained
paused.
- GREEN: the production-build Chromium scenario passed both the paused stage
and paused unstage flows. A final no-build rerun after test formatting also
passed.
- The existing `changes-panel-file-row.test.tsx` suite passes all 16 tests,
including the coarse-pointer action visibility and touch-size assertion.
- Targeted ESLint, web typecheck, specification lint, and scoped diff checks
pass.
## Risks
- A visibility change that hides the file icon without explicitly revealing
the spinner layer could leave the slot blank after pointer leave. The E2E
assertion observes the rendered spinner, not only class names.
- A WebSocket pause helper can disrupt unrelated live updates if it buffers a
whole message instead of only the matching newline-delimited frame. The
helper must partition and forward unrelated frames immediately.
- Completion can move the row between sections and detach its locator. The
test must reacquire the row from the destination section before testing the
inverse action.
## Open questions
None.