refactor(chat): share composer, scroll follow and jump control across both chat surfaces
The rectification session rendered its own textarea (no length ceiling, no count), its own rAF scroll follow with a sticky-scroll helper, and its own jump chip styled differently from the main chat's inline Tailwind button, while the main chat re-ran scrollTo on every streamed token and added a smooth scroll on settle. ChatComposer now accepts a controlled value so the rectification surface reuses it without touching the main draft store; useConversationScrollAnchor owns the follow through a ResizeObserver, one frame per content change, for both surfaces; a single JumpToLatestButton replaces both chips; the 720px transcript override is gone. DESIGN.md records the shared composer, the jump control, the reading width and the two shadow tokens. BUG-477 BUG-478 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
eeb82dfdc4
commit
f542c02490
@@ -13,7 +13,7 @@ import {
|
||||
shouldContinueAfterStructuredChoice,
|
||||
userVisibleChoiceLine,
|
||||
} from "../src/lib/rectification-agentic/v9/choice-action.ts";
|
||||
import { isNearBottom, shouldFollowLatest, shouldShowJumpToLatest } from "../src/lib/rectification-sticky-scroll.ts";
|
||||
import { conversationAnchorThreshold, nextAnchorState } from "../src/hooks/use-conversation-scroll-anchor.ts";
|
||||
import { stableFollowupQuestionId } from "../src/lib/rectification-agentic/v9/server-focus.ts";
|
||||
import {
|
||||
spokenCollectFallbackFollowup,
|
||||
@@ -542,17 +542,15 @@ function choiceAccounting(overrides: Parameters<typeof fakeAccounting>[0] = {})
|
||||
}
|
||||
|
||||
test("does not force-follow when the reader has scrolled upward", () => {
|
||||
assert.equal(isNearBottom(1_000, 100, 400), false);
|
||||
assert.equal(shouldFollowLatest(false), false);
|
||||
assert.equal(isNearBottom(1_000, 520, 400), true);
|
||||
assert.equal(shouldFollowLatest(true), true);
|
||||
});
|
||||
|
||||
test("jump-to-latest stays hidden while a choice card still sits in the overlay band", () => {
|
||||
assert.equal(shouldShowJumpToLatest(1_000, 100, 400), true);
|
||||
assert.equal(shouldShowJumpToLatest(1_000, 520, 400), false);
|
||||
assert.equal(shouldShowJumpToLatest(1_000, 500, 400, true), false);
|
||||
assert.equal(shouldShowJumpToLatest(1_000, 200, 400, true), true);
|
||||
// Former locks exercised `rectification-sticky-scroll.ts` (`isNearBottom`, `shouldFollowLatest`,
|
||||
// `shouldShowJumpToLatest` with a 360px choice-card band). That module was the second scroll
|
||||
// implementation (BUG-478); the shared anchor keeps the same 96px near-bottom rule, and the
|
||||
// jump control shows exactly when the reader is not anchored.
|
||||
assert.equal(conversationAnchorThreshold, 96);
|
||||
assert.equal(nextAnchorState(true, 1_000 - 100 - 400, true), false);
|
||||
assert.equal(nextAnchorState(true, 1_000 - 520 - 400, false), true);
|
||||
assert.equal(nextAnchorState(false, 600, false), false);
|
||||
assert.equal(nextAnchorState(false, 40, false), true);
|
||||
});
|
||||
|
||||
test("choice quotes come from the option label, not the assistant question year", () => {
|
||||
@@ -1125,8 +1123,10 @@ test("the public agent route treats structured choice as a non-model command", (
|
||||
assert.match(chat, /send\("read_only", ""\)/);
|
||||
assert.match(chat, /willContinue/);
|
||||
assert.match(chat, /current\.filter\(\(message\) => message\.renderKey !== assistantRenderKey\)/);
|
||||
assert.match(chat, /回到最新/);
|
||||
assert.match(chat, /followTailRef\.current/);
|
||||
// Former locks: `回到最新` and `followTailRef.current` — the rectification-only chip and follow
|
||||
// flag (BUG-478). The shared control and anchor are locked instead.
|
||||
assert.match(chat, /<JumpToLatestButton onClick=\{conversationAnchor\.anchorToLatest\} \/>/);
|
||||
assert.match(chat, /conversationAnchor\.anchored/);
|
||||
});
|
||||
|
||||
test("rectification attempt timeout stays under the agent route budget", () => {
|
||||
|
||||
Reference in New Issue
Block a user