Skip to content

Commit 5d69b44

Browse files
committed
refactor(ui): improve message visibility filtering and optimize bundle
- Refactor message filtering logic across AgentExecution, ClaudeCodeSession, SessionOutputViewer, and StreamMessage components to better handle visibility of user messages with tool results - Replace inefficient every() loop with for-loop for better performance when checking message content visibility - Add renderedSomething tracking in StreamMessage to prevent rendering empty components - Optimize SessionOutputViewer with useMemo for displayableMessages filtering - Remove unused GitHub API response fields (url, html_url, git_url) in Rust agents command - Add Vite build configuration with manual code splitting for better bundle optimization - Remove TypeScript expect-error comment for process global variable These changes improve UI performance by reducing unnecessary re-renders and bundle size through better code splitting.
1 parent e0a0ddf commit 5d69b44

6 files changed

Lines changed: 176 additions & 88 deletions

File tree

src-tauri/src/commands/agents.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1952,9 +1952,6 @@ struct GitHubApiResponse {
19521952
path: String,
19531953
sha: String,
19541954
size: i64,
1955-
url: String,
1956-
html_url: String,
1957-
git_url: String,
19581955
download_url: Option<String>,
19591956
#[serde(rename = "type")]
19601957
file_type: String,

src/components/AgentExecution.tsx

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -103,45 +103,54 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
103103

104104
// Skip empty user messages
105105
if (message.type === "user" && message.message) {
106+
if (message.isMeta) return false;
107+
106108
const msg = message.message;
107109
if (!msg.content || (Array.isArray(msg.content) && msg.content.length === 0)) {
108110
return false;
109111
}
110112

111-
// Check if this is a user message with only tool results that are already displayed
113+
// Check if user message has visible content by checking its parts
112114
if (Array.isArray(msg.content)) {
113-
const hasOnlyHiddenToolResults = msg.content.every((content: any) => {
114-
if (content.type !== "tool_result") return false;
115-
116-
// Check if this tool result should be hidden
117-
let hasCorrespondingWidget = false;
118-
if (content.tool_use_id) {
119-
// Look for the matching tool_use in previous assistant messages
120-
for (let i = index - 1; i >= 0; i--) {
121-
const prevMsg = messages[i];
122-
if (prevMsg.type === 'assistant' && prevMsg.message?.content && Array.isArray(prevMsg.message.content)) {
123-
const toolUse = prevMsg.message.content.find((c: any) =>
124-
c.type === 'tool_use' && c.id === content.tool_use_id
125-
);
126-
if (toolUse) {
127-
const toolName = toolUse.name?.toLowerCase();
128-
const toolsWithWidgets = [
129-
'task', 'edit', 'multiedit', 'todowrite', 'ls', 'read',
130-
'glob', 'bash', 'write', 'grep'
131-
];
132-
if (toolsWithWidgets.includes(toolName) || toolUse.name?.startsWith('mcp__')) {
133-
hasCorrespondingWidget = true;
115+
let hasVisibleContent = false;
116+
for (const content of msg.content) {
117+
if (content.type === "text") {
118+
hasVisibleContent = true;
119+
break;
120+
} else if (content.type === "tool_result") {
121+
// Check if this tool result will be skipped by a widget
122+
let willBeSkipped = false;
123+
if (content.tool_use_id) {
124+
// Look for the matching tool_use in previous assistant messages
125+
for (let i = index - 1; i >= 0; i--) {
126+
const prevMsg = messages[i];
127+
if (prevMsg.type === 'assistant' && prevMsg.message?.content && Array.isArray(prevMsg.message.content)) {
128+
const toolUse = prevMsg.message.content.find((c: any) =>
129+
c.type === 'tool_use' && c.id === content.tool_use_id
130+
);
131+
if (toolUse) {
132+
const toolName = toolUse.name?.toLowerCase();
133+
const toolsWithWidgets = [
134+
'task', 'edit', 'multiedit', 'todowrite', 'ls', 'read',
135+
'glob', 'bash', 'write', 'grep'
136+
];
137+
if (toolsWithWidgets.includes(toolName) || toolUse.name?.startsWith('mcp__')) {
138+
willBeSkipped = true;
139+
}
140+
break;
134141
}
135-
break;
136142
}
137143
}
138144
}
145+
146+
if (!willBeSkipped) {
147+
hasVisibleContent = true;
148+
break;
149+
}
139150
}
140-
141-
return hasCorrespondingWidget && !content.is_error;
142-
});
151+
}
143152

144-
if (hasOnlyHiddenToolResults) {
153+
if (!hasVisibleContent) {
145154
return false;
146155
}
147156
}

src/components/ClaudeCodeSession.tsx

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -117,52 +117,57 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
117117
return false;
118118
}
119119

120-
// Skip empty user messages
120+
// Skip user messages that only contain tool results that are already displayed
121121
if (message.type === "user" && message.message) {
122+
if (message.isMeta) return false;
123+
122124
const msg = message.message;
123125
if (!msg.content || (Array.isArray(msg.content) && msg.content.length === 0)) {
124126
return false;
125127
}
126-
127-
// Check if this is a user message with only tool results that are already displayed
128+
128129
if (Array.isArray(msg.content)) {
129-
const hasOnlyHiddenToolResults = msg.content.every((content: any) => {
130-
if (content.type !== "tool_result") return false;
131-
132-
// Check if this tool result should be hidden
133-
let hasCorrespondingWidget = false;
134-
if (content.tool_use_id) {
135-
// Look for the matching tool_use in previous assistant messages
136-
for (let i = index - 1; i >= 0; i--) {
137-
const prevMsg = messages[i];
138-
if (prevMsg.type === 'assistant' && prevMsg.message?.content && Array.isArray(prevMsg.message.content)) {
139-
const toolUse = prevMsg.message.content.find((c: any) =>
140-
c.type === 'tool_use' && c.id === content.tool_use_id
141-
);
142-
if (toolUse) {
143-
const toolName = toolUse.name?.toLowerCase();
144-
const toolsWithWidgets = [
145-
'task', 'edit', 'multiedit', 'todowrite', 'ls', 'read',
146-
'glob', 'bash', 'write', 'grep'
147-
];
148-
if (toolsWithWidgets.includes(toolName) || toolUse.name?.startsWith('mcp__')) {
149-
hasCorrespondingWidget = true;
130+
let hasVisibleContent = false;
131+
for (const content of msg.content) {
132+
if (content.type === "text") {
133+
hasVisibleContent = true;
134+
break;
135+
}
136+
if (content.type === "tool_result") {
137+
let willBeSkipped = false;
138+
if (content.tool_use_id) {
139+
// Look for the matching tool_use in previous assistant messages
140+
for (let i = index - 1; i >= 0; i--) {
141+
const prevMsg = messages[i];
142+
if (prevMsg.type === 'assistant' && prevMsg.message?.content && Array.isArray(prevMsg.message.content)) {
143+
const toolUse = prevMsg.message.content.find((c: any) =>
144+
c.type === 'tool_use' && c.id === content.tool_use_id
145+
);
146+
if (toolUse) {
147+
const toolName = toolUse.name?.toLowerCase();
148+
const toolsWithWidgets = [
149+
'task', 'edit', 'multiedit', 'todowrite', 'ls', 'read',
150+
'glob', 'bash', 'write', 'grep'
151+
];
152+
if (toolsWithWidgets.includes(toolName) || toolUse.name?.startsWith('mcp__')) {
153+
willBeSkipped = true;
154+
}
155+
break;
150156
}
151-
break;
152157
}
153158
}
154159
}
160+
if (!willBeSkipped) {
161+
hasVisibleContent = true;
162+
break;
163+
}
155164
}
156-
157-
return hasCorrespondingWidget && !content.is_error;
158-
});
159-
160-
if (hasOnlyHiddenToolResults) {
165+
}
166+
if (!hasVisibleContent) {
161167
return false;
162168
}
163169
}
164170
}
165-
166171
return true;
167172
});
168173
}, [messages]);

src/components/SessionOutputViewer.tsx

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useEffect, useRef } from 'react';
1+
import { useState, useEffect, useRef, useMemo } from 'react';
22
import { motion, AnimatePresence } from 'framer-motion';
33
import { X, Maximize2, Minimize2, Copy, RefreshCw, RotateCcw, ChevronDown } from 'lucide-react';
44
import { Button } from '@/components/ui/button';
@@ -282,6 +282,47 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
282282
loadOutput();
283283
}, [session.id]);
284284

285+
const displayableMessages = useMemo(() => {
286+
return messages.filter((message, index) => {
287+
if (message.isMeta && !message.leafUuid && !message.summary) return false;
288+
289+
if (message.type === "user" && message.message) {
290+
if (message.isMeta) return false;
291+
292+
const msg = message.message;
293+
if (!msg.content || (Array.isArray(msg.content) && msg.content.length === 0)) return false;
294+
295+
if (Array.isArray(msg.content)) {
296+
let hasVisibleContent = false;
297+
for (const content of msg.content) {
298+
if (content.type === "text") { hasVisibleContent = true; break; }
299+
if (content.type === "tool_result") {
300+
let willBeSkipped = false;
301+
if (content.tool_use_id) {
302+
for (let i = index - 1; i >= 0; i--) {
303+
const prevMsg = messages[i];
304+
if (prevMsg.type === 'assistant' && prevMsg.message?.content && Array.isArray(prevMsg.message.content)) {
305+
const toolUse = prevMsg.message.content.find((c: any) => c.type === 'tool_use' && c.id === content.tool_use_id);
306+
if (toolUse) {
307+
const toolName = toolUse.name?.toLowerCase();
308+
const toolsWithWidgets = ['task','edit','multiedit','todowrite','ls','read','glob','bash','write','grep'];
309+
if (toolsWithWidgets.includes(toolName) || toolUse.name?.startsWith('mcp__')) {
310+
willBeSkipped = true;
311+
}
312+
break;
313+
}
314+
}
315+
}
316+
}
317+
if (!willBeSkipped) { hasVisibleContent = true; break; }
318+
}
319+
}
320+
if (!hasVisibleContent) return false;
321+
}
322+
}
323+
return true;
324+
});
325+
}, [messages]);
285326

286327
return (
287328
<>
@@ -432,7 +473,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
432473
) : (
433474
<>
434475
<AnimatePresence>
435-
{messages.map((message: ClaudeStreamMessage, index: number) => (
476+
{displayableMessages.map((message: ClaudeStreamMessage, index: number) => (
436477
<motion.div
437478
key={index}
438479
initial={{ opacity: 0, y: 10 }}
@@ -556,7 +597,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
556597
) : (
557598
<>
558599
<AnimatePresence>
559-
{messages.map((message: ClaudeStreamMessage, index: number) => (
600+
{displayableMessages.map((message: ClaudeStreamMessage, index: number) => (
560601
<motion.div
561602
key={index}
562603
initial={{ opacity: 0, y: 10 }}

0 commit comments

Comments
 (0)