feat: refactor message filtering to use filterSyntheticParts for improved clarity

This commit is contained in:
Bohdan Triapitsyn
2025-12-27 02:37:29 +02:00
parent 8f9facb561
commit 9241fed442
2 changed files with 41 additions and 11 deletions
+15 -10
View File
@@ -5,7 +5,7 @@ import ChatMessage from './ChatMessage';
import { PermissionCard } from './PermissionCard'; import { PermissionCard } from './PermissionCard';
import type { Permission } from '@/types/permission'; import type { Permission } from '@/types/permission';
import type { AnimationHandlers, ContentChangeReason } from '@/hooks/useChatScrollManager'; import type { AnimationHandlers, ContentChangeReason } from '@/hooks/useChatScrollManager';
import { isFullySyntheticMessage } from '@/lib/messages/synthetic'; import { filterSyntheticParts } from '@/lib/messages/synthetic';
import { useTurnGrouping } from './hooks/useTurnGrouping'; import { useTurnGrouping } from './hooks/useTurnGrouping';
interface MessageListProps { interface MessageListProps {
@@ -40,16 +40,21 @@ const MessageList: React.FC<MessageListProps> = ({
const displayMessages = React.useMemo(() => { const displayMessages = React.useMemo(() => {
const seenIds = new Set<string>(); const seenIds = new Set<string>();
return messages.filter((message) => { return messages
const messageId = message.info?.id; .filter((message) => {
if (typeof messageId === 'string') { const messageId = message.info?.id;
if (seenIds.has(messageId)) { if (typeof messageId === 'string') {
return false; if (seenIds.has(messageId)) {
return false;
}
seenIds.add(messageId);
} }
seenIds.add(messageId); return true;
} })
return !isFullySyntheticMessage(message.parts); .map((message) => ({
}); ...message,
parts: filterSyntheticParts(message.parts),
}));
}, [messages]); }, [messages]);
const { getContextForMessage } = useTurnGrouping(displayMessages); const { getContextForMessage } = useTurnGrouping(displayMessages);
+26 -1
View File
@@ -1,12 +1,16 @@
import type { Part } from "@opencode-ai/sdk"; import type { Part } from "@opencode-ai/sdk";
const isSyntheticPart = (part: Part | undefined): boolean => { export const isSyntheticPart = (part: Part | undefined): boolean => {
if (!part || typeof part !== "object") { if (!part || typeof part !== "object") {
return false; return false;
} }
return Boolean((part as { synthetic?: boolean }).synthetic); return Boolean((part as { synthetic?: boolean }).synthetic);
}; };
/**
* Checks if a message consists entirely of synthetic parts.
* Used for status/completion logic (not display filtering).
*/
export const isFullySyntheticMessage = (parts: Part[] | undefined): boolean => { export const isFullySyntheticMessage = (parts: Part[] | undefined): boolean => {
if (!Array.isArray(parts) || parts.length === 0) { if (!Array.isArray(parts) || parts.length === 0) {
return false; return false;
@@ -14,3 +18,24 @@ export const isFullySyntheticMessage = (parts: Part[] | undefined): boolean => {
return parts.every((part) => isSyntheticPart(part)); return parts.every((part) => isSyntheticPart(part));
}; };
/**
* Filters out synthetic parts from a message, but only if there are
* non-synthetic parts present. If all parts are synthetic, returns
* them as-is so the message can still be displayed.
*/
export const filterSyntheticParts = (parts: Part[] | undefined): Part[] => {
if (!Array.isArray(parts) || parts.length === 0) {
return [];
}
const hasNonSynthetic = parts.some((part) => !isSyntheticPart(part));
// If there are non-synthetic parts, filter out synthetic ones
if (hasNonSynthetic) {
return parts.filter((part) => !isSyntheticPart(part));
}
// If all parts are synthetic, return them all (so message is displayed)
return parts;
};