Преглед изворни кода

fix(outline): 固定标准工作流天空色卡片避免来回闪烁

卡片不再跟会话级 isStreaming 卸挂,出现过后即使工具列表短暂清空也保持外壳。

Co-authored-by: Cursor <cursoragent@cursor.com>
darknessomi пре 3 недеља
родитељ
комит
0840c55f2a

+ 9 - 0
src/components/sources/outline-chat-panel.spec.tsx

@@ -474,10 +474,19 @@ describe("OutlineChatPanel controls", () => {
     expect(source).toContain("OutlineStandardWorkflowPanel")
     expect(source).toContain("shouldUseOutlineStandardWorkflowCard")
     expect(source).toContain("useStandardWorkflowCard")
+    expect(source).toContain("msg.multiAgentRun ? null")
+    expect(source).toContain("isRunning={Boolean(msg.isAgentRunning)}")
     expect(source).not.toContain("enableMultiAgent: true")
     expect(source).toContain("enableMultiAgent: !fastMode")
   })
 
+  it("上下文复用重算历史计划时仍带上标准工作流过程标志", () => {
+    expect(source).toContain("summaryInSystem: true")
+    expect(source).toContain("workflowMode: outlineMode")
+    expect(source).toContain("intentPhase: options.intentPhase")
+    expect(source).toContain("enableMultiAgent,")
+  })
+
   it("顶栏会话 chips 保底可见,阶段徽章不和会话列表抢宽度", () => {
     expect(source).toContain("min-w-[72px]")
     expect(source).toContain("topConversations.length > 0")

+ 6 - 3
src/components/sources/outline-chat-panel.tsx

@@ -1082,13 +1082,13 @@ function OutlineAssistantMessage({
         onResume={() => { void onResumeMultiAgent(msg.id) }}
         resumeDisabled={resumeMultiAgentDisabled}
       />
-      {useStandardWorkflowCard ? (
+      {msg.multiAgentRun ? null : useStandardWorkflowCard ? (
         <OutlineStandardWorkflowPanel
           intentPhase={msg.intentPhase}
-          isRunning={messageIsStreaming || Boolean(msg.isAgentRunning)}
+          isRunning={Boolean(msg.isAgentRunning)}
           toolCalls={msg.agentToolCalls}
           thinkingContent={thinking || undefined}
-          thinkingStreaming={messageIsStreaming}
+          thinkingStreaming={Boolean(thinking) && Boolean(msg.isAgentRunning)}
           onConfirmSave={onConfirmToolSave}
           onReject={onRejectTool}
         />
@@ -2188,6 +2188,9 @@ export function OutlineChatPanel({ onClose }: { onClose: () => void }) {
             contextDecision,
             cachedSummary: contextHubResult.sessionSummary || undefined,
             summaryInSystem: true,
+            workflowMode: outlineMode,
+            intentPhase: options.intentPhase,
+            enableMultiAgent,
           });
         }
 

+ 25 - 0
src/components/sources/outline-standard-workflow-panel.spec.tsx

@@ -128,4 +128,29 @@ describe("OutlineStandardWorkflowPanel", () => {
 
     expect(host.textContent).toBe("")
   })
+
+  it("已经出现过的卡片不会因为工具列表被短暂清空而卸掉", async () => {
+    await act(async () => {
+      root.render(
+        <OutlineStandardWorkflowPanel
+          intentPhase="generation"
+          isRunning
+          toolCalls={sampleCalls}
+        />,
+      )
+    })
+    expect(host.textContent).toContain("大纲生成工作流")
+
+    await act(async () => {
+      root.render(
+        <OutlineStandardWorkflowPanel
+          intentPhase="generation"
+          toolCalls={[]}
+        />,
+      )
+    })
+    expect(host.textContent).toContain("大纲生成工作流")
+    expect(host.innerHTML).toContain("bg-sky-50/45")
+    expect(host.textContent).not.toContain("思考 0 段")
+  })
 })

+ 7 - 3
src/components/sources/outline-standard-workflow-panel.tsx

@@ -1,3 +1,4 @@
+import { useRef } from "react"
 import {
   CheckCircle2,
   ChevronRight,
@@ -70,11 +71,14 @@ export function OutlineStandardWorkflowPanel({
 }: OutlineStandardWorkflowPanelProps) {
   const safeToolCalls = toolCalls ?? []
   const toolsRunning = safeToolCalls.some((call) => call.status === "running")
-  const running = isRunning || Boolean(thinkingStreaming) || toolsRunning
+  const running = isRunning || toolsRunning
   const failedCount = safeToolCalls.filter((call) => call.status === "error").length
   const doneCount = safeToolCalls.filter((call) => call.status === "done").length
   const hasContent = safeToolCalls.length > 0 || Boolean(thinkingContent) || running
-  if (!hasContent) return null
+  const hasShownRef = useRef(false)
+  if (hasContent) hasShownRef.current = true
+  // 过程中工具数组/运行状态会短暂清空;一旦出现过就不要卸掉天空色卡片,否则会和左边框时间线来回闪。
+  if (!hasShownRef.current) return null
 
   const status: "running" | "done" | "error" = running
     ? "running"
@@ -123,7 +127,7 @@ export function OutlineStandardWorkflowPanel({
         <AgentToolCallMessage
           toolCalls={safeToolCalls}
           thinkingContent={thinkingContent}
-          thinkingStreaming={thinkingStreaming || (running && safeToolCalls.length === 0)}
+          thinkingStreaming={Boolean(thinkingStreaming) || (running && safeToolCalls.length === 0)}
           chrome="none"
           showEventSummary={false}
           onConfirmSave={onConfirmSave}