story-kit: merge 174_story_constrain_thinking_traces_in_chat_panel

This commit is contained in:
Dave
2026-02-25 09:32:48 +00:00
parent 4b161d7c87
commit 42c40209d2
5 changed files with 204 additions and 68 deletions

View File

@@ -13,6 +13,56 @@ import { StagePanel } from "./StagePanel";
const { useCallback, useEffect, useRef, useState } = React;
/** Fixed-height thinking trace block that auto-scrolls to bottom as text arrives. */
function ThinkingBlock({ text }: { text: string }) {
const scrollRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = scrollRef.current;
if (el) {
el.scrollTop = el.scrollHeight;
}
}, [text]);
return (
<div
data-testid="chat-thinking-block"
ref={scrollRef}
style={{
maxHeight: "96px",
overflowY: "auto",
background: "#161b22",
border: "1px solid #2d333b",
borderRadius: "6px",
padding: "6px 10px",
fontSize: "0.78em",
fontFamily: "monospace",
color: "#6e7681",
fontStyle: "italic",
whiteSpace: "pre-wrap",
wordBreak: "break-word",
lineHeight: "1.4",
marginBottom: "8px",
}}
>
<span
style={{
display: "block",
fontSize: "0.8em",
color: "#444c56",
marginBottom: "4px",
fontStyle: "normal",
letterSpacing: "0.04em",
textTransform: "uppercase",
}}
>
thinking
</span>
{text}
</div>
);
}
const NARROW_BREAKPOINT = 900;
function formatToolActivity(toolName: string): string {
@@ -64,6 +114,7 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
const [availableModels, setAvailableModels] = useState<string[]>([]);
const [claudeModels, setClaudeModels] = useState<string[]>([]);
const [streamingContent, setStreamingContent] = useState("");
const [streamingThinking, setStreamingThinking] = useState("");
const [showApiKeyDialog, setShowApiKeyDialog] = useState(false);
const [apiKeyInput, setApiKeyInput] = useState("");
const [hasAnthropicKey, setHasAnthropicKey] = useState(false);
@@ -208,9 +259,13 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
onToken: (content) => {
setStreamingContent((prev: string) => prev + content);
},
onThinkingToken: (content) => {
setStreamingThinking((prev: string) => prev + content);
},
onUpdate: (history) => {
setMessages(history);
setStreamingContent("");
setStreamingThinking("");
const last = history[history.length - 1];
if (last?.role === "assistant" && !last.tool_calls) {
setLoading(false);
@@ -303,7 +358,8 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
lastScrollTopRef.current = currentScrollTop;
};
const autoScrollKey = messages.length + streamingContent.length;
const autoScrollKey =
messages.length + streamingContent.length + streamingThinking.length;
useEffect(() => {
if (
@@ -351,6 +407,7 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
setStreamingContent("");
}
setStreamingThinking("");
setLoading(false);
setActivityStatus(null);
} catch (e) {
@@ -395,6 +452,7 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
}
setLoading(true);
setStreamingContent("");
setStreamingThinking("");
setActivityStatus(null);
try {
@@ -471,6 +529,7 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
clearMessages();
setStreamingContent("");
setStreamingThinking("");
setLoading(false);
setActivityStatus(null);
setClaudeSessionId(null);
@@ -761,6 +820,9 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
</div>
</div>
))}
{loading && streamingThinking && (
<ThinkingBlock text={streamingThinking} />
)}
{loading && streamingContent && (
<div
style={{
@@ -817,21 +879,23 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
</div>
</div>
)}
{loading && (activityStatus != null || !streamingContent) && (
<div
data-testid="activity-indicator"
style={{
alignSelf: "flex-start",
color: "#888",
fontSize: "0.9em",
marginTop: "10px",
}}
>
<span className="pulse">
{activityStatus ?? "Thinking..."}
</span>
</div>
)}
{loading &&
(activityStatus != null ||
(!streamingContent && !streamingThinking)) && (
<div
data-testid="activity-indicator"
style={{
alignSelf: "flex-start",
color: "#888",
fontSize: "0.9em",
marginTop: "10px",
}}
>
<span className="pulse">
{activityStatus ?? "Thinking..."}
</span>
</div>
)}
<div ref={messagesEndRef} />
</div>
</div>
@@ -1075,7 +1139,7 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
stateVersion={agentStateVersion}
/>
<StagePanel title="Done" items={pipeline.done} />
<StagePanel title="Done" items={pipeline.done ?? []} />
<StagePanel title="To Merge" items={pipeline.merge} />
<StagePanel title="QA" items={pipeline.qa} />
<StagePanel title="Current" items={pipeline.current} />