116 lines
4.4 KiB
Markdown
116 lines
4.4 KiB
Markdown
---
|
|
created: 2026-08-29
|
|
status: done
|
|
requirements:
|
|
- REQ-UI-TRANSCRIPT-AUTO-SCROLL-001
|
|
system_design:
|
|
- ../../specs/ui/system-design/transcript-auto-scroll.md
|
|
legacy_specs: []
|
|
---
|
|
|
|
# Implementation Plan: Keep WebKit Transcripts at the Bottom
|
|
|
|
## Overview
|
|
|
|
Replace the unsafe bottom-scroll offset with the largest signed 32-bit integer.
|
|
Keep the write-only message path and all current scroll-owner guards.
|
|
|
|
## Scope
|
|
|
|
### In scope
|
|
|
|
- Keep enabled transcripts at the bottom in Safari, Orion, Firefox, and
|
|
Chromium browsers.
|
|
- Preserve the write-only scroll command for live message commits.
|
|
- Add a regression test for the WebKit-safe offset.
|
|
- Keep the existing desktop and mobile scroll behavior.
|
|
|
|
### Out of scope
|
|
|
|
- Transcript controls, copy, layout, pagination, or navigation.
|
|
- Disabled auto-scroll behavior and saved scroll positions.
|
|
- A new Playwright browser project or a change to the CI browser matrix.
|
|
- Browser-specific user-agent branches.
|
|
|
|
## Confirmed root cause
|
|
|
|
[PR #3093](https://github.com/kdlbs/kandev/pull/3093) changed the native bottom
|
|
write from `scrollTop = scrollHeight` to `scrollTop = Number.MAX_SAFE_INTEGER`.
|
|
The change removed a synchronous layout read.
|
|
|
|
A Playwright WebKit 26.5 probe used a container with a 900 px scroll range.
|
|
The old `scrollHeight` write produced 900. The new `Number.MAX_SAFE_INTEGER`
|
|
write produced 0. A `2_147_483_647` write produced 900.
|
|
|
|
The live-message effect calls this helper after each streamed update. WebKit
|
|
therefore moves the transcript to the top after each update. Chromium and
|
|
Firefox clamp the same unsafe value to the bottom.
|
|
|
|
## Technical approach
|
|
|
|
### Bottom-scroll helper
|
|
|
|
- Change `NATIVE_BOTTOM_SCROLL_TOP` in
|
|
`apps/web/components/task/chat/message-list-native-scroll.ts` to
|
|
`2_147_483_647`.
|
|
- Keep `scrollNativeToBottom` as one write-only operation.
|
|
- Keep the work-start, live-append, and catch-up call sites unchanged.
|
|
|
|
### Regression test
|
|
|
|
- Update `apps/web/components/task/chat/message-list-native.test.tsx`.
|
|
- Name the failing regression `uses a WebKit-safe maximum offset for pinned appends`.
|
|
- Make the `scrollHeight` getter throw to protect the write-only requirement.
|
|
- Record the setter value and require `2_147_483_647`.
|
|
|
|
## Tests
|
|
|
|
| Acceptance criterion | Test evidence |
|
|
| --- | --- |
|
|
| `AC-UI-TRANSCRIPT-AUTO-SCROLL-001.5` | The native-scroll regression requires a WebKit-safe bottom offset after an append. |
|
|
| `AC-UI-TRANSCRIPT-AUTO-SCROLL-001.7` | The same regression fails on any synchronous `scrollHeight` read. |
|
|
|
|
## E2E tests
|
|
|
|
- Desktop: run the enabled live-message scenario in
|
|
`apps/web/e2e/tests/chat/auto-scroll-toggle.spec.ts` with the `chromium` project.
|
|
- Mobile: run the matching scenario in
|
|
`apps/web/e2e/tests/chat/mobile-auto-scroll-toggle.spec.ts` with the
|
|
`mobile-chrome` project.
|
|
- WebKit: the direct browser probe proves the browser-specific offset behavior.
|
|
The unit regression applies that safe value to the product helper.
|
|
|
|
## Mobile design contract
|
|
|
|
- Desktop outcome: live messages keep the transcript at the bottom.
|
|
- Mobile entry point: the existing Chat tab in the full-height task layout.
|
|
- Nearest exemplar: `apps/web/components/task/task-layout.tsx` and the current
|
|
mobile auto-scroll test.
|
|
- Hierarchy and surface: the transcript remains the only vertical scroll owner.
|
|
- Shared logic: desktop and mobile use the same bottom-scroll helper.
|
|
- Mobile proof: the current mobile live-message scenario covers the shared path.
|
|
|
|
## Work orders
|
|
|
|
- [x] [Task 01: Use a WebKit-safe bottom offset](task-01-use-webkit-safe-bottom-offset.md)
|
|
|
|
## Verification results
|
|
|
|
- RED: the regression received `9_007_199_254_740_991` and expected
|
|
`2_147_483_647`. The other 26 focused tests passed.
|
|
- GREEN: all 27 native-scroll tests passed after the one-line correction.
|
|
- TypeScript typecheck and targeted ESLint passed.
|
|
- All specification files passed lint. The specification linter's 20 tests
|
|
passed.
|
|
- Desktop Chromium E2E passed one live-message scenario with retries disabled.
|
|
- Mobile Chrome E2E passed the matching Pixel 5 scenario with retries disabled.
|
|
- Direct probes showed that `2_147_483_647` reaches the bottom in Chromium 151,
|
|
Firefox 152, and WebKit 26.5.
|
|
|
|
## Risks
|
|
|
|
- A smaller sentinel can fail if a transcript exceeds that scroll range.
|
|
The signed 32-bit maximum is larger than a practical browser scroll range.
|
|
- A direct WebKit E2E project expands the CI browser matrix.
|
|
This fix keeps that expansion out of scope.
|