feat(chat): thinking window scrolls again, follows the tail only while pinned
TODO.md L7: "Add scrolling back to the thinking block, but have it autoscroll while thinking content is generating." Owner direction 2026-08-27 (roadmap A2) reverses the phase-21 no-scroll choice (2026-08-24): details.thinking .thinking-text is user-scrollable again (overflow-y: auto — the 320px clip stays, owner-confirmed), and the phase-17 per-chunk bottom-pin is GATED: the window follows the live tail only while the user is pinned near its bottom (THINKING_NEAR_ BOTTOM_PX = 32); scrolling up pauses the follow, returning to the bottom re-arms it on the next chunk (the gate re-runs on every frame). - frontend/assets/styles.css: .thinking-text overflow-y: hidden -> auto; the phase-21 owner-choice comment is replaced with the 2026-08-27 direction; max-height: 320px and every other declaration in the rule byte-identical. - frontend/assets/app.js: export const THINKING_NEAR_BOTTOM_PX = 32 + isThinkingNearBottom(textEl) (scrollHeight - scrollTop - clientHeight <= band); the thinking-handler pin becomes `if (block.open && isThinkingNearBottom(textEl))` — a scrolled-up reader is never re-pinned and a closed (restored) block is never pinned; everything else in the handler (and phase 42's no page scroll) untouched. - tests/unit/test_thinking_scroll.py (new, replaces the deleted tests/unit/test_thinking_no_scroll.py): pins the CSS contract (auto + 320px + owner-direction comment, no hidden/scroll left), the exported 32px band, the gate math, the gated pin (no unconditional `if (block.open)` remains), and the surviving collapsed-restore pin. - tests/e2e/test_thinking_scroll.py (new, mock-only, replaces the deleted tests/e2e/test_thinking_no_scroll.py — its pins asserted the reversed phase-21 behavior, so both phase-21 files are deleted in this commit): user scroll restored on the frozen 4s-hesitation tail (wheel is 1:1; click+Home keyboard — the plain div is not keyboard-focusable by design, tabindex is test scaffolding; the literal drag holds the user's position — headless Chromium's overlay scrollbars are not grabbable by synthetic mouse events, documented in the suite), follow-while-pinned at the 2nd-to-last and last chunk (±1px) with the last chunk's text inside the visible rectangle, no re-pin over ≥5 mid-stream chunks after a half-window scroll-up, re-pin on the next chunk after returning to the bottom, the CSS contract, plus the phase-11 (long answer: page scrolls, bubble overflow untouched) and phase-17 (restored collapsed block with full text) regressions. - tests/unit/test_chat_persistence.py: the CSS pin flips with the contract (auto in, hidden out — owner direction 2026-08-27). - tests/unit/test_frontend_scroll.py: the "page-level band constant is gone" pin now excludes the phase-43 window-level THINKING_NEAR_BOTTOM_PX (a different band — the window's, not the page's). E2E (isolation): test_thinking_scroll 7/7 (twice); regressions test_thinking_display 5/5, test_chat_persistence 4/4, test_no_reply_autoscroll 5/5, test_smoke 3/3; unit+integration 725 passed, app/ coverage 99% (unchanged — frontend-only phase); ruff + pyright clean.
This commit is contained in:
@@ -40,10 +40,15 @@ def _fn_body(js: str, name: str) -> str:
|
||||
|
||||
def test_phase18_gate_is_gone() -> None:
|
||||
"""The follow-the-bottom machinery (phase 18) is removed by owner
|
||||
direction 2026-08-27: no band constant, no gate function, and no
|
||||
document-scroller measurement left anywhere in app.js."""
|
||||
direction 2026-08-27: no page-level band constant, no gate
|
||||
function, and no document-scroller measurement left anywhere in
|
||||
app.js. (The phase-43 window-level THINKING_NEAR_BOTTOM_PX /
|
||||
isThinkingNearBottom pair is a different band — the thinking
|
||||
window's, not the page's.)"""
|
||||
js = _js()
|
||||
assert "NEAR_BOTTOM_PX" not in js, "the 200px band constant must be gone"
|
||||
assert not re.search(r"(?<![A-Z_])NEAR_BOTTOM_PX\b", js), (
|
||||
"the 200px page-level band constant must be gone"
|
||||
)
|
||||
assert "isNearBottom" not in js, "the pinned-to-bottom gate must be gone"
|
||||
assert "window.scrollY" not in js, (
|
||||
"nothing in app.js measures the page scroll offset anymore"
|
||||
|
||||
Reference in New Issue
Block a user