feat(acp): inline thinking display, plan dedup, and processing indicator fixes#1953
feat(acp): inline thinking display, plan dedup, and processing indicator fixes#1953
Conversation
…tor fixes (#1952) - Display thinking content inline in conversation flow via MessageThinking - Buffer thinking content and persist to DB with timer-based flush - Deduplicate plan messages per turn with stable msg_id, keep at end of list - Intercept "Compacting..." messages as tips instead of text - Show processing indicator only from init to first content token - Fix auto-scroll followOutput to only check userScrolledRef - Add turnFinished guard to prevent auto-recover after finish signal - Set status to finished in AcpAgentManager onSignalEvent finish handler - Remove leaked dispatch imports and debug logging - Add thinking/plan merge support in composeMessageWithIndex Closes #1952
12654a8 to
5cf7194
Compare
Show meaningful tool details (file paths, commands) instead of generic kind labels like "read". Add extractKeyParam helper and update display layout to separate operation name from parameters.
5cf7194 to
0c4fb90
Compare
Replace blacklist (shouldIgnoreStreamMessage) with whitelist (isGeneratingStreamMessage) for sidebar generating detection. Only content/start/thought/thinking/tool_group/acp_tool_call/ acp_permission/plan messages trigger the spinner. Internal signals like slash_commands_updated no longer cause false positives.
…y layout - Increase Virtuoso increaseViewportBy from 200 to 800 to pre-render more items offscreen, preventing scroll position corrections when variable-height items are measured on-the-fly during upward scroll - Replace ThoughtDisplay translateY(36px) + pb-40px hack with proper layout: top-only border-radius (rd-t-20px), negative margin (mb--20px) and z-index to seamlessly merge with the input box below
…bility - Change Shadow DOM code span font-size from 14px to 13px with 20px line-height - Add text-13px to ToolItemDetail name and description in MessageToolGroupSummary
Add common.viewMoreLines key to all 6 locales (en-US, zh-CN, zh-TW, ja-JP, ko-KR, tr-TR) and use t() in CodeBlock component.
Code Review:feat(acp): inline thinking display, plan dedup, and processing indicator fixes (#1953)变更概述本 PR 将 ACP thinking 内容从隐藏的 ThoughtDisplay 改为内联到对话流中显示(类似 Claude.ai),包含流式累积和 DB 持久化。同时修复了 plan 消息重复问题(通过 turn 级别的 stable msg_id)、处理指示器的时机问题(仅在 init→首个 content token 期间显示)、auto-scroll 的 方案评估结论:✅ 方案合理 方案正确解决了 PR 描述的多个问题:thinking 内联显示通过新的 问题清单🟠 HIGH — AcpConnection.ts 死代码块文件: 问题代码: {
const updateType = (message.params?.update as Record<string, unknown>)?.sessionUpdate;
}问题说明:这是一个裸块(bare block),内部声明了 修复建议: 删除第 738–740 行整个块。 🟡 MEDIUM — useAutoScroll.ts 残留 console.debug 调试日志文件: 问题代码: console.debug(
'[AutoScroll] followOutput:',
result,
'isAtBottom:',
_isAtBottom,
'userScrolled:',
userScrolledRef.current
);console.debug('[AutoScroll] atBottomStateChange:', atBottom, 'userScrolled:', userScrolledRef.current);console.debug('[AutoScroll] guard blocked upward scroll, delta:', delta, 'guardAge:', timeSinceGuard, 'ms');console.debug('[AutoScroll] upward scroll detected, delta:', delta, 'setting userScrolled=true');问题说明:生产代码中残留了 4 处 修复建议: 移除所有 4 处 🟡 MEDIUM — CodeBlock expandedStates 模块级 Map 内存泄漏文件: 问题代码: const expandedStates = new Map<string, boolean>();
function getBlockFingerprint(language: string, firstLine: string): string {
return `${language}:${firstLine}`;
}问题说明:模块级 修复建议: 可以考虑使用 🔵 LOW — extractAndStripThinkTags 当前未被调用文件: 问题说明:新增的 🔵 LOW — MessageThinking 中 formatDuration 和 formatElapsedTime 逻辑重复文件: 问题代码: const formatDuration = (ms: number): string => {
const seconds = Math.floor(ms / 1000);
if (seconds < 60) return `${seconds}s`;
const minutes = Math.floor(seconds / 60);
const remaining = seconds % 60;
return `${minutes}m ${remaining}s`;
};
const formatElapsedTime = (seconds: number): string => {
if (seconds < 60) return `${seconds}s`;
const minutes = Math.floor(seconds / 60);
const remaining = seconds % 60;
return `${minutes}m ${remaining}s`;
};问题说明:两个函数逻辑完全相同,唯一区别是输入单位(ms vs seconds)。可以统一为一个函数。 修复建议: const formatSeconds = (seconds: number): string => {
if (seconds < 60) return `${seconds}s`;
const minutes = Math.floor(seconds / 60);
const remaining = seconds % 60;
return `${minutes}m ${remaining}s`;
};
// 使用时:formatSeconds(Math.floor(ms / 1000)) 和 formatSeconds(elapsedTime)汇总
结论本报告由本地 CONCLUSION: CONDITIONAL |
- Remove dead code block in AcpConnection.ts (unused updateType variable) - Remove all console.debug statements from useAutoScroll.ts - Add LRU-style size cap to CodeBlock expandedStates Map Review follow-up for #1953
PR Fix 验证报告原始 PR: #1953
总结: ✅ 已修复 3 个 | ❌ 未能修复 0 个
|
Convert Gemini thought events to thinking messages that are displayed inline in the conversation and persisted to the database, matching the ACP implementation from PR #1953. - GeminiAgentManager: add emitThinkingMessage/flushThinkingToDb, convert thought events to thinking messages, extract inline <think> tags from content, finalize thinking on finish signal - useGeminiMessage: handle thinking message type, track hasThinkingMessage - GeminiSendBox: hide ThoughtDisplay when thinking messages are shown inline
* feat(gemini): inline thinking display in conversation flow Convert Gemini thought events to thinking messages that are displayed inline in the conversation and persisted to the database, matching the ACP implementation from PR #1953. - GeminiAgentManager: add emitThinkingMessage/flushThinkingToDb, convert thought events to thinking messages, extract inline <think> tags from content, finalize thinking on finish signal - useGeminiMessage: handle thinking message type, track hasThinkingMessage - GeminiSendBox: hide ThoughtDisplay when thinking messages are shown inline * test(gemini): add unit tests for emitThinkingMessage and flushThinkingToDb - Cover content accumulation across multiple thought chunks - Verify thinkingMsgId reuse within a single turn - Assert DB flush timer is created on first chunk and cleared on done - Confirm flushThinkingToDb returns early when thinkingMsgId is null - Validate addOrUpdateMessage receives correct TMessage shape Review follow-up for #2013 --------- Co-authored-by: zk <[email protected]> Co-authored-by: zynx <>
* feat(gemini): inline thinking display in conversation flow Convert Gemini thought events to thinking messages that are displayed inline in the conversation and persisted to the database, matching the ACP implementation from PR #1953. - GeminiAgentManager: add emitThinkingMessage/flushThinkingToDb, convert thought events to thinking messages, extract inline <think> tags from content, finalize thinking on finish signal - useGeminiMessage: handle thinking message type, track hasThinkingMessage - GeminiSendBox: hide ThoughtDisplay when thinking messages are shown inline * test(gemini): add unit tests for emitThinkingMessage and flushThinkingToDb - Cover content accumulation across multiple thought chunks - Verify thinkingMsgId reuse within a single turn - Assert DB flush timer is created on first chunk and cleared on done - Confirm flushThinkingToDb returns early when thinkingMsgId is null - Validate addOrUpdateMessage receives correct TMessage shape Review follow-up for #2013 --------- Co-authored-by: zk <[email protected]> Co-authored-by: zynx <>
Summary
followOutputnow only checksuserScrolledRef, ignoring staleisAtBottomstateturnFinishedguard to prevent late messages from re-triggering loading state after finishthis.status = 'finished'inonSignalEventfinish handler to fix stuck status on conversation re-entryTest plan
bun run lint— 0 errorsbunx tsc --noEmit— passbunx vitest run— pass (previewFileWatch failure is pre-existing on main)Closes #1952