Session switch: land at the bottom instantly with the question dock open

- Chat now remounts per session (key={chat.id}): fresh StickToBottom,
  fresh DockOverlay (no cross-session morph from the previous shell
  height), fresh useChat.
- Conversation initial="instant" (was "smooth"): the mount scroll to
  bottom is a jump, not a slide.
- DockOverlay skips the pending-question scroll/expand effect for the
  session's INITIAL pending question (dock starts expanded; the initial
  scroll positions us) — previously it fired a second smooth
  scrollToBottom right after the library's initial scroll (the visible
  'pause, then the quiz pops and pushes everything up').
- The dock-variant fade/rise no longer plays for the session's opening
  variant; reserved for mid-session transitions.
Verified live: client-side switch settles at d=1px from the bottom within
one frame of content swap, dock open from the first frame, both
directions (quiz session, plain session).
This commit is contained in:
Amos
2026-08-22 06:45:32 +03:00
parent 939e1e2e5e
commit d1d28d6bb4
2 changed files with 14 additions and 8 deletions
+13 -8
View File
@@ -95,7 +95,7 @@ export function Chat({
</header>
<div className="relative min-h-0 flex-1">
<Conversation className="h-full">
<Conversation className="h-full" initial="instant">
<ConversationContent className="min-h-full gap-0 p-0">
<div className={`${COLUMN} flex flex-1 flex-col gap-4 pt-4`}>
{messages.length === 0 && !busy && (
@@ -208,14 +208,16 @@ function DockOverlay({
const [expanded, setExpanded] = useState(true);
const pendingId = pending?.toolCallId ?? null;
const initialPendingRef = useRef(pendingId);
// New question: open expanded if the learner is following at the bottom
// (scroll to reveal it); if they're reading history, arrive as the slim bar.
// New question MID-SESSION: open expanded if the learner is following at
// the bottom (scroll to reveal it); reading history → arrive as the bar.
// On session mount the dock simply starts expanded at the bottom (the
// library's instant initial scroll positions us) — no scroll, no morph.
useEffect(() => {
if (pendingId) {
setExpanded(isAtBottom);
if (isAtBottom) void scrollToBottom();
}
if (!pendingId || pendingId === initialPendingRef.current) return;
setExpanded(isAtBottom);
if (isAtBottom) void scrollToBottom();
// eslint-disable-next-line react-hooks/exhaustive-deps -- fire only when a NEW question lands
}, [pendingId]);
@@ -258,6 +260,9 @@ function DockOverlay({
};
const mode = pending ? `question:${pending.toolCallId}` : "chat";
// The opening variant of a session appears instantly — the fade/rise is
// reserved for mid-session transitions.
const initialModeRef = useRef(mode);
// Height morph: animate the shell from its previous height to its new one
// whenever the variant (chat ↔ question ↔ collapsed bar) changes. Typing
@@ -298,7 +303,7 @@ function DockOverlay({
<div className="bg-background pb-4">
<div className={COLUMN}>
<div ref={shellRef} className="pointer-events-auto overflow-hidden rounded-2xl border bg-card shadow-lg">
<div key={mode} className="dock-variant">
<div key={mode} className={mode === initialModeRef.current ? undefined : "dock-variant"}>
{pending ? (
<QuestionDock
toolName={pending.tool as "quiz" | "ask_user_question"}