fix: recognize visible Dockview chat panels (#3481)
This commit is contained in:
@@ -41,6 +41,14 @@ A focused throwaway hook test reproduced the race: session A's delayed response
|
||||
produced no `updateSessionReadCursor("session-1", "m2")` call after session B
|
||||
dispatched. The temporary test was removed after recording the red evidence.
|
||||
|
||||
The follow-up trace exposed a separate visibility defect. Dockview restored the
|
||||
incoming Chat tab as selected in the center group while the side-by-side
|
||||
Changes group retained global focus. `usePanelActive` read Dockview's
|
||||
`isActive`, which combines group-local tab visibility with global group focus,
|
||||
so the visibly rendered transcript reported `isVisible=false` and skipped
|
||||
initial placement. Completed agents produced no later message or work-state
|
||||
write to mask the skipped placement.
|
||||
|
||||
## Scope
|
||||
|
||||
### In scope
|
||||
@@ -119,6 +127,7 @@ changes.
|
||||
## Work orders
|
||||
|
||||
- [x] [Task 01: Preserve read cursor across task switches](task-01-preserve-read-cursor-across-task-switches.md)
|
||||
- [x] [Task 02: Recognize visible Dockview chat panels](task-02-recognize-visible-dockview-chat-panels.md)
|
||||
|
||||
## Verification results
|
||||
|
||||
@@ -132,6 +141,15 @@ changes.
|
||||
- Mobile Chrome completed-task switch E2E passed (1 test).
|
||||
- Specification linter tests passed (30 tests); all specification files passed.
|
||||
- `git diff --check` passed.
|
||||
- Follow-up TDD regression: the visibility hook returned false when Dockview
|
||||
reported `isActive=false` and `isVisible=true`; it passed after switching to
|
||||
the group-local visibility contract.
|
||||
- Follow-up focused Vitest passed: 1 file, 6 tests.
|
||||
- Follow-up TypeScript typecheck and scoped ESLint passed.
|
||||
- Follow-up Chromium task-switch E2E passed with Changes retaining global
|
||||
Dockview focus while the visible Chat transcript returned to the bottom.
|
||||
- Follow-up Mobile Chrome completed-task switch E2E passed.
|
||||
- Follow-up specification linter tests and full specification lint passed.
|
||||
|
||||
## Risks
|
||||
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
---
|
||||
id: "02-recognize-visible-dockview-chat-panels"
|
||||
title: "Recognize visible Dockview chat panels"
|
||||
status: done
|
||||
wave: 2
|
||||
depends_on:
|
||||
- "01-preserve-read-cursor-across-task-switches"
|
||||
plan: "plan.md"
|
||||
requirements:
|
||||
- REQ-UI-TRANSCRIPT-AUTO-SCROLL-001
|
||||
acceptance_criteria:
|
||||
- AC-UI-TRANSCRIPT-AUTO-SCROLL-001.11
|
||||
- AC-UI-TRANSCRIPT-AUTO-SCROLL-001.14
|
||||
- AC-UI-TRANSCRIPT-AUTO-SCROLL-001.15
|
||||
system_design:
|
||||
- ../../specs/ui/system-design/transcript-auto-scroll.md
|
||||
---
|
||||
|
||||
# Task 02: Recognize Visible Dockview Chat Panels
|
||||
|
||||
## Summary
|
||||
|
||||
Treat the selected Chat tab as visible independently of which side-by-side
|
||||
Dockview group owns global focus. Prove that a completed task restores its
|
||||
transcript placement without relying on a later agent update.
|
||||
|
||||
## In scope
|
||||
|
||||
- Subscribe to Dockview panel visibility rather than global active state.
|
||||
- Cover the focused-sibling-group contract in the visibility hook.
|
||||
- Extend the desktop completed-task switch regression with Changes-group focus.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Dockview layout persistence or active-group restoration.
|
||||
- Transcript auto-scroll preference semantics or stored offsets.
|
||||
- Mobile composition, which does not mount Dockview.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- A selected Chat tab reports visible when another Dockview group owns focus.
|
||||
- Hiding Chat behind another tab in its own group reports not visible.
|
||||
- Returning to an idle completed task places the transcript at the bottom while
|
||||
Changes remains the globally active panel.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
(cd apps && pnpm --filter @kandev/web test -- --run hooks/use-panel-active.test.ts)
|
||||
(cd apps/web && pnpm exec eslint hooks/use-panel-active.ts hooks/use-panel-active.test.ts e2e/tests/chat/unread-divider.spec.ts)
|
||||
(cd apps/web && pnpm run typecheck)
|
||||
(cd apps/web && pnpm e2e:run --host --project chromium tests/chat/unread-divider.spec.ts -- --grep "completed task switch" --retries=0)
|
||||
(cd apps/web && pnpm e2e:run --host --no-build --project mobile-chrome tests/chat/mobile-unread-divider.spec.ts -- --grep "completed task switch" --retries=0)
|
||||
python3 scripts/lint-spec-files.test.py
|
||||
python3 scripts/lint-spec-files.py --all
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## Files likely touched
|
||||
|
||||
- `apps/web/hooks/use-panel-active.ts`
|
||||
- `apps/web/hooks/use-panel-active.test.ts`
|
||||
- `apps/web/e2e/tests/chat/unread-divider.spec.ts`
|
||||
- `docs/specs/ui/requirements/transcript-auto-scroll.md`
|
||||
- `docs/specs/ui/system-design/transcript-auto-scroll.md`
|
||||
|
||||
## Dependencies
|
||||
|
||||
Task 01 established the environment-switch placement lifecycle and its
|
||||
completed-task regression.
|
||||
|
||||
## Risks
|
||||
|
||||
- Using global Dockview focus recreates the defect when another visible group
|
||||
remains active after layout restoration.
|
||||
- Treating every mounted portal as visible could mark hidden session tabs read.
|
||||
|
||||
## Parallelism
|
||||
|
||||
`sequential`
|
||||
|
||||
## Inputs
|
||||
|
||||
- `REQ-UI-TRANSCRIPT-AUTO-SCROLL-001`, especially acceptance criteria `.11`,
|
||||
`.14`, and `.15`.
|
||||
- UI transcript auto-scroll system design.
|
||||
- Dockview panel visibility and active-state contracts.
|
||||
|
||||
## Results
|
||||
|
||||
- Replaced Dockview's global active-state subscription with the group-local
|
||||
visibility property and event.
|
||||
- Added unit coverage for a visible Chat panel whose sibling group owns focus.
|
||||
- Extended the desktop completed-task switch regression to retain Changes as
|
||||
the active panel and prove the visible transcript returns to the bottom.
|
||||
- Focused Vitest, scoped ESLint, TypeScript typecheck, desktop Chromium E2E,
|
||||
Mobile Chrome parity E2E, specification lint, and diff checks passed.
|
||||
@@ -2,7 +2,7 @@
|
||||
status: active
|
||||
system: ui
|
||||
created: 2026-07-30
|
||||
updated: 2026-09-04
|
||||
updated: 2026-09-07
|
||||
owners:
|
||||
- cfl
|
||||
---
|
||||
@@ -72,6 +72,11 @@ hand-off it is designed to protect.
|
||||
newest cached message and a disabled transcript shows its saved reader
|
||||
position as soon as the panel is measurable, without first exposing the
|
||||
browser's default top position.
|
||||
- **AC-UI-TRANSCRIPT-AUTO-SCROLL-001.15:** **GIVEN** a desktop transcript is the
|
||||
selected tab in its Dockview group while another visible group owns global
|
||||
focus, **WHEN** a task switch restores that layout, **THEN** the transcript
|
||||
is treated as visible and completes its enabled or disabled initial
|
||||
placement.
|
||||
|
||||
## Migrated source detail
|
||||
|
||||
@@ -127,6 +132,9 @@ the asynchronous hand-off it is designed to protect.
|
||||
**WHEN** the user switches between them and returns, **THEN** the incoming
|
||||
enabled transcript shows the newest cached message immediately and remains
|
||||
at the newest message after its history refresh.
|
||||
- **GIVEN** the Changes panel owns Dockview focus while Chat remains the
|
||||
selected center tab, **WHEN** the user returns to that task, **THEN** Chat
|
||||
completes initial transcript placement without requiring an agent update.
|
||||
- **GIVEN** the incoming transcript has auto-scroll disabled, **WHEN** that
|
||||
environment-switch placement runs, **THEN** it restores the incoming
|
||||
session's saved position rather than the outgoing transcript's position.
|
||||
|
||||
@@ -4,7 +4,7 @@ system: ui
|
||||
requirements:
|
||||
- REQ-UI-TRANSCRIPT-AUTO-SCROLL-001
|
||||
created: 2026-08-27
|
||||
updated: 2026-09-04
|
||||
updated: 2026-09-07
|
||||
owners:
|
||||
- kandev
|
||||
---
|
||||
@@ -50,8 +50,15 @@ if that path reads it. The scroll setter records the WebKit-safe offset.
|
||||
Desktop Dockview renders panel content through persistent portals. An inactive
|
||||
session transcript can therefore be mounted and receive messages while its
|
||||
scroll container has zero or detached geometry. `usePanelActive` supplies the
|
||||
authoritative active-tab signal to `TaskChatPanel`, which passes it to the
|
||||
native transcript scroll coordinator as `isVisible`.
|
||||
authoritative group-local visibility signal to `TaskChatPanel`, which passes it
|
||||
to the native transcript scroll coordinator as `isVisible`.
|
||||
|
||||
Dockview's panel `isVisible` property means the panel is the selected tab in
|
||||
its own group. Its `isActive` property additionally requires that group to own
|
||||
global Dockview focus. The transcript uses `isVisible` and
|
||||
`onDidVisibilityChange`; clicking a side-by-side Files or Changes group cannot
|
||||
make a still-rendered Chat panel ineligible for initial placement or read
|
||||
tracking.
|
||||
|
||||
Geometry-based initialization and divider placement do not consume their
|
||||
one-time completion state while `isVisible` is false. When a transcript first
|
||||
@@ -194,10 +201,11 @@ pinning, and disabled position restoration.
|
||||
## Failure modes
|
||||
|
||||
- If the container is not mounted, the helper performs no work.
|
||||
- If a transcript is mounted but inactive, one-time placement remains pending
|
||||
until the panel becomes active and measurable.
|
||||
- If a panel becomes inactive again before the post-restore frames complete,
|
||||
the scheduled placement is cancelled.
|
||||
- If a transcript is mounted but hidden behind another tab in its group,
|
||||
one-time placement remains pending until the panel becomes visible and
|
||||
measurable.
|
||||
- If a panel becomes hidden again before the post-restore frames complete, the
|
||||
scheduled placement is cancelled.
|
||||
- Chromium, Gecko, and WebKit clamp the signed 32-bit maximum to the native
|
||||
maximum scroll position.
|
||||
- An offset outside WebKit's safe range can resolve to zero and move the
|
||||
|
||||
Reference in New Issue
Block a user