Files
Jyotisha/frontend/src/components/chat-streaming-transcript.tsx
T
Jesse_ChenandClaude Opus 5.5 ad801a5db7
Independent Staging Quality Gate / publish (push) Canceled after 0s
Independent Staging Quality Gate / validate (push) Canceled after 2m12s
Home and the chat chart no longer re-render on every streamed frame (issue #123, BUG-1472)
Home subscribed to the whole streamingReply object, so each animation frame of
an answer re-ran the page (header, picker, composer, overlays) and the desktop
natal panel under it. ChatStreamingTranscript now subscribes for its session
and feeds ChatTranscript; Home reads one derived boolean for the stop label.
ChatChartSplit is memoized. 100 frames: Home <=2 commits (was >=100), panel 0.

Two source-contract tests now pin the new component instead of the old names.
The onboarding typewriter still ticks at Home level (out of this ticket).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 11:13:55 +08:00

38 lines
1.5 KiB
TypeScript

"use client";
import { ChatTranscript, type ChatTranscriptProps } from "@/components/chat-transcript";
import type { StreamingReply } from "@/lib/home-types";
import { useSessionStore } from "@/stores/session-store";
type StreamingFields = "streamingText" | "streamingActivity" | "streamingThinking" | "streamingSections" | "streamingTimeline" | "streamingResponseKind";
function replyFor(state: { streamingReply: StreamingReply | null }, sessionId: string | undefined) {
const reply = state.streamingReply;
return reply && reply.sessionId === sessionId ? reply : null;
}
/**
* The transcript is the only subscriber to the streaming reply (issue #123).
* The stream commits once per animation frame; when Home subscribed, every
* frame re-rendered the whole page and the desktop chart panel with it.
*/
export function ChatStreamingTranscript(props: Omit<ChatTranscriptProps, StreamingFields>) {
const reply = useSessionStore((state) => replyFor(state, props.sessionId));
return (
<ChatTranscript
{...props}
streamingText={reply?.text ?? ""}
streamingActivity={reply?.activity}
streamingThinking={reply?.thinkingText}
streamingSections={reply?.thinkingSections}
streamingTimeline={reply?.timeline}
streamingResponseKind={reply?.responseKind}
/>
);
}
/** Whether the active reply has text yet; flips at most twice per answer, so Home can read it. */
export function useStreamingReplyHasText(sessionId: string | undefined) {
return useSessionStore((state) => Boolean(replyFor(state, sessionId)?.text));
}