refactor(vscode): 重构消息排序和展示逻辑

- 移除旧的消息排序改进总结文档
- 重新组织消息渲染逻辑,合并所有类型的消息按时间戳排序
- 优化工具调用处理流程,添加时间戳支持
- 改进会话保存机制,直接使用SessionManager保存检查点
- 重构部分组件以提高可维护性
This commit is contained in:
yiliang114
2025-11-28 22:35:31 +08:00
parent 5ce40085d5
commit 9ae45c01a6
33 changed files with 299 additions and 837 deletions

View File

@@ -62,6 +62,8 @@ export const App: React.FC = () => {
} | null>(null);
const [planEntries, setPlanEntries] = useState<PlanEntry[]>([]);
const messagesEndRef = useRef<HTMLDivElement>(null);
// Scroll container for message list; used to keep the view anchored to the latest content
const messagesContainerRef = useRef<HTMLDivElement>(null);
const inputFieldRef = useRef<HTMLDivElement>(null);
const [showBanner, setShowBanner] = useState(true);
const [editMode, setEditMode] = useState<EditMode>('ask');
@@ -173,6 +175,51 @@ export const App: React.FC = () => {
setInputText,
});
// Auto-scroll handling: keep the view pinned to bottom when new content arrives,
// but don't interrupt the user if they scrolled up.
const prevCountsRef = useRef({ msgLen: 0, inProgLen: 0, doneLen: 0 });
useEffect(() => {
const container = messagesContainerRef.current;
const endEl = messagesEndRef.current;
if (!container || !endEl) {
return;
}
const nearBottom = () => {
const threshold = 64; // px tolerance
return (
container.scrollTop + container.clientHeight >=
container.scrollHeight - threshold
);
};
// Detect whether new items were appended (vs. streaming chunk updates)
const prev = prevCountsRef.current;
const newMsg = messageHandling.messages.length > prev.msgLen;
const newInProg = inProgressToolCalls.length > prev.inProgLen;
const newDone = completedToolCalls.length > prev.doneLen;
prevCountsRef.current = {
msgLen: messageHandling.messages.length,
inProgLen: inProgressToolCalls.length,
doneLen: completedToolCalls.length,
};
// If user is near bottom, or if we just appended a new item, scroll to bottom
if (nearBottom() || newMsg || newInProg || newDone) {
const smooth = newMsg || newInProg || newDone; // avoid smooth on streaming chunks
endEl.scrollIntoView({
behavior: smooth ? 'smooth' : 'auto',
block: 'end',
});
}
}, [
messageHandling.messages,
inProgressToolCalls,
completedToolCalls,
messageHandling.isWaitingForResponse,
messageHandling.loadingMessage,
]);
// Handle permission response
const handlePermissionResponse = useCallback(
(optionId: string) => {
@@ -380,6 +427,7 @@ export const App: React.FC = () => {
/>
<div
ref={messagesContainerRef}
className="flex-1 overflow-y-auto overflow-x-hidden pt-5 pr-5 pl-5 pb-[120px] flex flex-col relative min-w-0 focus:outline-none [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:bg-white/20 [&::-webkit-scrollbar-thumb]:rounded-sm [&::-webkit-scrollbar-thumb:hover]:bg-white/30 [&>*]:flex [&>*]:gap-0 [&>*]:items-start [&>*]:text-left [&>*]:py-2 [&>.message-item]:px-0 [&>*]:flex-col [&>*]:relative [&>*]:animate-[fadeIn_0.2s_ease-in]"
style={{ backgroundColor: 'var(--app-primary-background)' }}
>
@@ -387,23 +435,23 @@ export const App: React.FC = () => {
<EmptyState />
) : (
<>
{/* 创建统一的消息数组,包含所有类型的消息和工具调用 */}
{/* Create unified message array containing all types of messages and tool calls */}
{(() => {
// 普通消息
// Regular messages
const regularMessages = messageHandling.messages.map((msg) => ({
type: 'message' as const,
data: msg,
timestamp: msg.timestamp,
}));
// 进行中的工具调用
// In-progress tool calls
const inProgressTools = inProgressToolCalls.map((toolCall) => ({
type: 'in-progress-tool-call' as const,
data: toolCall,
timestamp: toolCall.timestamp || Date.now(),
}));
// 完成的工具调用
// Completed tool calls
const completedTools = completedToolCalls
.filter(hasToolCallOutput)
.map((toolCall) => ({
@@ -412,7 +460,7 @@ export const App: React.FC = () => {
timestamp: toolCall.timestamp || Date.now(),
}));
// 合并并按时间戳排序,确保消息与工具调用穿插显示
// Merge and sort by timestamp to ensure messages and tool calls are interleaved
const allMessages = [
...regularMessages,
...inProgressTools,
@@ -492,7 +540,7 @@ export const App: React.FC = () => {
});
})()}
{/* 已改为在 useWebViewMessages 中将每次 plan 推送为历史 toolcall避免重复展示最新块 */}
{/* Changed to push each plan as a historical toolcall in useWebViewMessages to avoid duplicate display of the latest block */}
{messageHandling.isWaitingForResponse &&
messageHandling.loadingMessage && (