refactor: Clean up root-level files in ui-new folder (#2079)
## Summary Successfully cleaned up root level files in `frontend/src/components/ui-new/`: ### Deleted (unused files): - `MockConversationList.tsx` - not imported anywhere - `MockDisplayConversationEntry.tsx` - not imported anywhere ### Moved to `containers/`: - `ConversationList.tsx` → `containers/ConversationListContainer.tsx` - `NewDisplayConversationEntry.tsx` → `containers/NewDisplayConversationEntry.tsx` - `VirtualizedProcessLogs.tsx` → `containers/VirtualizedProcessLogs.tsx` ### Updated imports in: - `views/WorkspacesMain.tsx` - `views/PreviewControls.tsx` - `containers/LogsContentContainer.tsx` - `components/dialogs/scripts/ScriptFixerDialog.tsx` - `containers/ConversationListContainer.tsx` - `containers/NewDisplayConversationEntry.tsx` The root level of `ui-new/` is now clean with no files remaining. All components that use state or side effects were correctly placed in `containers/` to comply with ESLint rules. Both type check (`pnpm run check`) and lint (`pnpm run lint`) pass successfully.
This commit is contained in:
committed by
GitHub
parent
8ba3a50d0b
commit
75beab35d9
@@ -20,7 +20,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select';
|
} from '@/components/ui/select';
|
||||||
import { AutoExpandingTextarea } from '@/components/ui/auto-expanding-textarea';
|
import { AutoExpandingTextarea } from '@/components/ui/auto-expanding-textarea';
|
||||||
import { VirtualizedProcessLogs } from '@/components/ui-new/VirtualizedProcessLogs';
|
import { VirtualizedProcessLogs } from '@/components/ui-new/containers/VirtualizedProcessLogs';
|
||||||
import { RunningDots } from '@/components/ui-new/primitives/RunningDots';
|
import { RunningDots } from '@/components/ui-new/primitives/RunningDots';
|
||||||
import { defineModal } from '@/lib/modals';
|
import { defineModal } from '@/lib/modals';
|
||||||
import { repoApi, attemptsApi } from '@/lib/api';
|
import { repoApi, attemptsApi } from '@/lib/api';
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
import {
|
|
||||||
VirtuosoMessageList,
|
|
||||||
VirtuosoMessageListLicense,
|
|
||||||
VirtuosoMessageListProps,
|
|
||||||
} from '@virtuoso.dev/message-list';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
|
|
||||||
import NewDisplayConversationEntry from './NewDisplayConversationEntry';
|
|
||||||
import { ApprovalFormProvider } from '@/contexts/ApprovalFormContext';
|
|
||||||
import { ExecutionProcessesProvider } from '@/contexts/ExecutionProcessesContext';
|
|
||||||
import { RetryUiProvider } from '@/contexts/RetryUiContext';
|
|
||||||
import type { NormalizedEntry } from 'shared/types';
|
|
||||||
|
|
||||||
// Type for mock data entries
|
|
||||||
export type MockPatchEntry = {
|
|
||||||
type: 'NORMALIZED_ENTRY';
|
|
||||||
content: NormalizedEntry;
|
|
||||||
patchKey: string;
|
|
||||||
executionProcessId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
interface MockConversationListProps {
|
|
||||||
entries: MockPatchEntry[];
|
|
||||||
attemptId?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const INITIAL_TOP_ITEM = { index: 'LAST' as const, align: 'end' as const };
|
|
||||||
|
|
||||||
const ItemContent: VirtuosoMessageListProps<
|
|
||||||
MockPatchEntry,
|
|
||||||
undefined
|
|
||||||
>['ItemContent'] = ({ data }) => {
|
|
||||||
if (data.type === 'NORMALIZED_ENTRY') {
|
|
||||||
return (
|
|
||||||
<NewDisplayConversationEntry
|
|
||||||
expansionKey={data.patchKey}
|
|
||||||
entry={data.content}
|
|
||||||
executionProcessId={data.executionProcessId}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const computeItemKey: VirtuosoMessageListProps<
|
|
||||||
MockPatchEntry,
|
|
||||||
undefined
|
|
||||||
>['computeItemKey'] = ({ data }) => `mock-${data.patchKey}`;
|
|
||||||
|
|
||||||
export function MockConversationList({
|
|
||||||
entries,
|
|
||||||
attemptId,
|
|
||||||
}: MockConversationListProps) {
|
|
||||||
const channelData = useMemo(() => ({ data: entries }), [entries]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ExecutionProcessesProvider attemptId={attemptId}>
|
|
||||||
<RetryUiProvider attemptId={attemptId}>
|
|
||||||
<ApprovalFormProvider>
|
|
||||||
<VirtuosoMessageListLicense
|
|
||||||
licenseKey={import.meta.env.VITE_PUBLIC_REACT_VIRTUOSO_LICENSE_KEY}
|
|
||||||
>
|
|
||||||
<VirtuosoMessageList<MockPatchEntry, undefined>
|
|
||||||
className="h-full scrollbar-none"
|
|
||||||
data={channelData}
|
|
||||||
initialLocation={INITIAL_TOP_ITEM}
|
|
||||||
computeItemKey={computeItemKey}
|
|
||||||
ItemContent={ItemContent}
|
|
||||||
Header={() => <div className="h-2" />}
|
|
||||||
Footer={() => <div className="h-2" />}
|
|
||||||
/>
|
|
||||||
</VirtuosoMessageListLicense>
|
|
||||||
</ApprovalFormProvider>
|
|
||||||
</RetryUiProvider>
|
|
||||||
</ExecutionProcessesProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MockConversationList;
|
|
||||||
@@ -1,461 +0,0 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import WYSIWYGEditor from '@/components/ui/wysiwyg';
|
|
||||||
import {
|
|
||||||
ActionType,
|
|
||||||
NormalizedEntry,
|
|
||||||
type NormalizedEntryType,
|
|
||||||
} from 'shared/types.ts';
|
|
||||||
import FileChangeRenderer from '@/components/NormalizedConversation/FileChangeRenderer';
|
|
||||||
import { useExpandable } from '@/stores/useExpandableStore';
|
|
||||||
import {
|
|
||||||
WarningCircleIcon,
|
|
||||||
RobotIcon,
|
|
||||||
CheckCircleIcon,
|
|
||||||
CheckSquareIcon,
|
|
||||||
CaretDownIcon,
|
|
||||||
PencilSimpleIcon,
|
|
||||||
EyeIcon,
|
|
||||||
GlobeIcon,
|
|
||||||
HammerIcon,
|
|
||||||
PlusIcon,
|
|
||||||
MagnifyingGlassIcon,
|
|
||||||
GearIcon,
|
|
||||||
TerminalIcon,
|
|
||||||
UserIcon,
|
|
||||||
} from '@phosphor-icons/react';
|
|
||||||
import RawLogText from '@/components/common/RawLogText';
|
|
||||||
import { cn } from '@/lib/utils';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
entry: NormalizedEntry;
|
|
||||||
expansionKey: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type FileEditAction = Extract<ActionType, { action: 'file_edit' }>;
|
|
||||||
|
|
||||||
const getEntryIcon = (entryType: NormalizedEntryType) => {
|
|
||||||
const iconClassName = 'size-icon-xs';
|
|
||||||
if (entryType.type === 'user_message' || entryType.type === 'user_feedback') {
|
|
||||||
return <UserIcon className={iconClassName} />;
|
|
||||||
}
|
|
||||||
if (entryType.type === 'assistant_message') {
|
|
||||||
return <RobotIcon className={iconClassName} />;
|
|
||||||
}
|
|
||||||
if (entryType.type === 'system_message') {
|
|
||||||
return <GearIcon className={iconClassName} />;
|
|
||||||
}
|
|
||||||
if (entryType.type === 'error_message') {
|
|
||||||
return <WarningCircleIcon className={iconClassName} />;
|
|
||||||
}
|
|
||||||
if (entryType.type === 'tool_use') {
|
|
||||||
const { action_type, tool_name } = entryType;
|
|
||||||
|
|
||||||
if (
|
|
||||||
action_type.action === 'todo_management' ||
|
|
||||||
(tool_name &&
|
|
||||||
['todowrite', 'todoread', 'todo_write', 'todo_read', 'todo'].includes(
|
|
||||||
tool_name.toLowerCase()
|
|
||||||
))
|
|
||||||
) {
|
|
||||||
return <CheckSquareIcon className={iconClassName} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action_type.action === 'file_read') {
|
|
||||||
return <EyeIcon className={iconClassName} />;
|
|
||||||
} else if (action_type.action === 'file_edit') {
|
|
||||||
return <PencilSimpleIcon className={iconClassName} />;
|
|
||||||
} else if (action_type.action === 'command_run') {
|
|
||||||
return <TerminalIcon className={iconClassName} />;
|
|
||||||
} else if (action_type.action === 'search') {
|
|
||||||
return <MagnifyingGlassIcon className={iconClassName} />;
|
|
||||||
} else if (action_type.action === 'web_fetch') {
|
|
||||||
return <GlobeIcon className={iconClassName} />;
|
|
||||||
} else if (action_type.action === 'task_create') {
|
|
||||||
return <PlusIcon className={iconClassName} />;
|
|
||||||
} else if (action_type.action === 'plan_presentation') {
|
|
||||||
return <CheckSquareIcon className={iconClassName} />;
|
|
||||||
} else if (action_type.action === 'tool') {
|
|
||||||
return <HammerIcon className={iconClassName} />;
|
|
||||||
}
|
|
||||||
return <GearIcon className={iconClassName} />;
|
|
||||||
}
|
|
||||||
return <GearIcon className={iconClassName} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const shouldRenderMarkdown = (entryType: NormalizedEntryType) =>
|
|
||||||
entryType.type === 'assistant_message' ||
|
|
||||||
entryType.type === 'system_message' ||
|
|
||||||
entryType.type === 'tool_use';
|
|
||||||
|
|
||||||
const getContentClassName = (entryType: NormalizedEntryType) => {
|
|
||||||
const base = ' whitespace-pre-wrap break-words';
|
|
||||||
if (
|
|
||||||
entryType.type === 'tool_use' &&
|
|
||||||
entryType.action_type.action === 'command_run'
|
|
||||||
)
|
|
||||||
return `${base} font-mono`;
|
|
||||||
|
|
||||||
if (entryType.type === 'error_message')
|
|
||||||
return `${base} font-mono text-destructive`;
|
|
||||||
|
|
||||||
return base;
|
|
||||||
};
|
|
||||||
|
|
||||||
type CardVariant = 'system' | 'error';
|
|
||||||
|
|
||||||
const MessageCard: React.FC<{
|
|
||||||
children: React.ReactNode;
|
|
||||||
variant: CardVariant;
|
|
||||||
expanded?: boolean;
|
|
||||||
onToggle?: () => void;
|
|
||||||
}> = ({ children, variant, expanded, onToggle }) => {
|
|
||||||
const frameBase =
|
|
||||||
'border px-3 py-2 w-full cursor-pointer bg-[hsl(var(--card))] border-[hsl(var(--border))]';
|
|
||||||
const systemTheme = 'border-400/40 text-zinc-500';
|
|
||||||
const errorTheme =
|
|
||||||
'border-red-400/40 bg-red-50 dark:bg-[hsl(var(--card))] text-[hsl(var(--foreground))]';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`${frameBase} ${
|
|
||||||
variant === 'system' ? systemTheme : errorTheme
|
|
||||||
}`}
|
|
||||||
onClick={onToggle}
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-1.5">
|
|
||||||
<div className="min-w-0 flex-1">{children}</div>
|
|
||||||
{onToggle && (
|
|
||||||
<ExpandChevron
|
|
||||||
expanded={!!expanded}
|
|
||||||
onClick={onToggle}
|
|
||||||
variant={variant}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
type CollapsibleVariant = 'system' | 'error';
|
|
||||||
|
|
||||||
const ExpandChevron: React.FC<{
|
|
||||||
expanded: boolean;
|
|
||||||
onClick: () => void;
|
|
||||||
variant: CollapsibleVariant;
|
|
||||||
}> = ({ expanded, onClick, variant }) => {
|
|
||||||
const color =
|
|
||||||
variant === 'system'
|
|
||||||
? 'text-700 dark:text-300'
|
|
||||||
: 'text-red-700 dark:text-red-300';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CaretDownIcon
|
|
||||||
onClick={onClick}
|
|
||||||
className={`size-icon-base cursor-pointer transition-transform ${color} ${
|
|
||||||
expanded ? '' : '-rotate-90'
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const CollapsibleEntry: React.FC<{
|
|
||||||
content: string;
|
|
||||||
markdown: boolean;
|
|
||||||
expansionKey: string;
|
|
||||||
variant: CollapsibleVariant;
|
|
||||||
contentClassName: string;
|
|
||||||
}> = ({ content, markdown, expansionKey, variant, contentClassName }) => {
|
|
||||||
const multiline = content.includes('\n');
|
|
||||||
const [expanded, toggle] = useExpandable(`entry:${expansionKey}`, false);
|
|
||||||
|
|
||||||
const Inner = (
|
|
||||||
<div className={contentClassName}>
|
|
||||||
{markdown ? (
|
|
||||||
<WYSIWYGEditor
|
|
||||||
value={content}
|
|
||||||
disabled
|
|
||||||
className="whitespace-pre-wrap break-words"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
content
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const firstLine = content.split('\n')[0];
|
|
||||||
const PreviewInner = (
|
|
||||||
<div className={contentClassName}>
|
|
||||||
{markdown ? (
|
|
||||||
<WYSIWYGEditor
|
|
||||||
value={firstLine}
|
|
||||||
disabled
|
|
||||||
className="whitespace-pre-wrap break-words"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
firstLine
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!multiline) {
|
|
||||||
return <MessageCard variant={variant}>{Inner}</MessageCard>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return expanded ? (
|
|
||||||
<MessageCard variant={variant} expanded={expanded} onToggle={toggle}>
|
|
||||||
{Inner}
|
|
||||||
</MessageCard>
|
|
||||||
) : (
|
|
||||||
<MessageCard variant={variant} expanded={expanded} onToggle={toggle}>
|
|
||||||
{PreviewInner}
|
|
||||||
</MessageCard>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const ToolCallCard: React.FC<{
|
|
||||||
entry: NormalizedEntry;
|
|
||||||
expansionKey: string;
|
|
||||||
}> = ({ entry, expansionKey }) => {
|
|
||||||
const { t } = useTranslation('common');
|
|
||||||
|
|
||||||
const entryType =
|
|
||||||
entry.entry_type.type === 'tool_use' ? entry.entry_type : undefined;
|
|
||||||
|
|
||||||
const linkifyUrls = entryType?.tool_name === 'Tool Install Script';
|
|
||||||
const defaultExpanded = linkifyUrls;
|
|
||||||
|
|
||||||
const [expanded, toggle] = useExpandable(
|
|
||||||
`tool-entry:${expansionKey}`,
|
|
||||||
defaultExpanded
|
|
||||||
);
|
|
||||||
|
|
||||||
const actionType = entryType?.action_type;
|
|
||||||
const isCommand = actionType?.action === 'command_run';
|
|
||||||
const isTool = actionType?.action === 'tool';
|
|
||||||
|
|
||||||
const label = isCommand
|
|
||||||
? t('conversation.ran')
|
|
||||||
: entryType?.tool_name || t('conversation.tool');
|
|
||||||
|
|
||||||
const inlineText = entry.content.trim();
|
|
||||||
const isSingleLine = inlineText !== '' && !/\r?\n/.test(inlineText);
|
|
||||||
const showInlineSummary = isSingleLine;
|
|
||||||
|
|
||||||
const commandResult = isCommand ? actionType.result : null;
|
|
||||||
const output = commandResult?.output ?? null;
|
|
||||||
let argsText: string | null = null;
|
|
||||||
if (isCommand) {
|
|
||||||
const fromArgs =
|
|
||||||
typeof actionType.command === 'string' ? actionType.command : '';
|
|
||||||
const fallback = inlineText;
|
|
||||||
argsText = (fromArgs || fallback).trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasArgs = isTool && !!actionType.arguments;
|
|
||||||
const hasResult = isTool && !!actionType.result;
|
|
||||||
|
|
||||||
const hasExpandableDetails = isCommand
|
|
||||||
? Boolean(argsText) || Boolean(output)
|
|
||||||
: hasArgs || hasResult;
|
|
||||||
|
|
||||||
const HeaderWrapper: React.ElementType = hasExpandableDetails
|
|
||||||
? 'button'
|
|
||||||
: 'div';
|
|
||||||
const headerProps = hasExpandableDetails
|
|
||||||
? {
|
|
||||||
onClick: (e: React.MouseEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
toggle();
|
|
||||||
},
|
|
||||||
title: expanded
|
|
||||||
? t('conversation.toolDetailsToggle.hide')
|
|
||||||
: t('conversation.toolDetailsToggle.show'),
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
|
|
||||||
const headerClassName = cn(
|
|
||||||
'w-full flex items-center gap-1.5 text-left text-secondary-foreground'
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderJson = (v: unknown) => (
|
|
||||||
<pre className="whitespace-pre-wrap">{JSON.stringify(v, null, 2)}</pre>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="inline-block w-full flex flex-col gap-4">
|
|
||||||
<HeaderWrapper {...headerProps} className={headerClassName}>
|
|
||||||
<span className="min-w-0 flex items-center gap-1.5">
|
|
||||||
<span>{entryType && getEntryIcon(entryType)}</span>
|
|
||||||
{showInlineSummary ? (
|
|
||||||
<span className="font-mono">{inlineText}</span>
|
|
||||||
) : (
|
|
||||||
<span className="font-mono">{label}</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</HeaderWrapper>
|
|
||||||
|
|
||||||
{expanded && (
|
|
||||||
<div className="max-h-[200px] overflow-y-auto border">
|
|
||||||
{isCommand ? (
|
|
||||||
<>
|
|
||||||
{argsText && (
|
|
||||||
<>
|
|
||||||
<div className="font-normal uppercase bg-background border-b border-dashed px-2 py-1">
|
|
||||||
{t('conversation.args')}
|
|
||||||
</div>
|
|
||||||
<div className="px-2 py-1">{argsText}</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{output && (
|
|
||||||
<>
|
|
||||||
<div className="font-normal uppercase bg-background border-y border-dashed px-2 py-1">
|
|
||||||
{t('conversation.output')}
|
|
||||||
</div>
|
|
||||||
<div className="px-2 py-1">
|
|
||||||
<RawLogText content={output} linkifyUrls={linkifyUrls} />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{isTool && actionType && (
|
|
||||||
<>
|
|
||||||
<div className="font-normal uppercase bg-background border-b border-dashed px-2 py-1">
|
|
||||||
{t('conversation.args')}
|
|
||||||
</div>
|
|
||||||
<div className="px-2 py-1">
|
|
||||||
{renderJson(actionType.arguments)}
|
|
||||||
</div>
|
|
||||||
<div className="font-normal uppercase bg-background border-y border-dashed px-2 py-1">
|
|
||||||
{t('conversation.result')}
|
|
||||||
</div>
|
|
||||||
<div className="px-2 py-1">
|
|
||||||
{actionType.result?.type.type === 'markdown' &&
|
|
||||||
actionType.result.value && (
|
|
||||||
<WYSIWYGEditor
|
|
||||||
value={actionType.result.value?.toString()}
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{actionType.result?.type.type === 'json' &&
|
|
||||||
renderJson(actionType.result.value)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
function MockDisplayConversationEntry({ entry, expansionKey }: Props) {
|
|
||||||
const { t } = useTranslation('common');
|
|
||||||
const entryType = entry.entry_type;
|
|
||||||
const isSystem = entryType.type === 'system_message';
|
|
||||||
const isError = entryType.type === 'error_message';
|
|
||||||
const isToolUse = entryType.type === 'tool_use';
|
|
||||||
const isUserMessage = entryType.type === 'user_message';
|
|
||||||
const isFileEdit = (a: ActionType): a is FileEditAction =>
|
|
||||||
a.action === 'file_edit';
|
|
||||||
|
|
||||||
// User message - simple rendering
|
|
||||||
if (isUserMessage) {
|
|
||||||
return (
|
|
||||||
<div className="px-4 py-2 text-base">
|
|
||||||
<div className="flex items-start gap-2">
|
|
||||||
<UserIcon className="size-icon-base mt-0.5 text-muted-foreground" />
|
|
||||||
<div className="flex-1">
|
|
||||||
<WYSIWYGEditor
|
|
||||||
value={entry.content}
|
|
||||||
disabled
|
|
||||||
className="whitespace-pre-wrap break-words"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tool use rendering
|
|
||||||
if (isToolUse) {
|
|
||||||
const toolEntry = entryType;
|
|
||||||
|
|
||||||
// File edit - use FileChangeRenderer
|
|
||||||
if (isFileEdit(toolEntry.action_type)) {
|
|
||||||
const fileEditAction = toolEntry.action_type as FileEditAction;
|
|
||||||
return (
|
|
||||||
<div className="px-4 py-2 text-base space-y-3">
|
|
||||||
{fileEditAction.changes.map((change, idx) => (
|
|
||||||
<FileChangeRenderer
|
|
||||||
key={idx}
|
|
||||||
path={fileEditAction.path}
|
|
||||||
change={change}
|
|
||||||
expansionKey={`edit:${expansionKey}:${idx}`}
|
|
||||||
defaultExpanded={false}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other tool uses
|
|
||||||
return (
|
|
||||||
<div className="px-4 py-2 text-base space-y-3">
|
|
||||||
<ToolCallCard entry={entry} expansionKey={expansionKey} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// System or error messages - collapsible
|
|
||||||
if (isSystem || isError) {
|
|
||||||
return (
|
|
||||||
<div className="px-4 py-2 text-base">
|
|
||||||
<CollapsibleEntry
|
|
||||||
content={entry.content}
|
|
||||||
markdown={shouldRenderMarkdown(entryType)}
|
|
||||||
expansionKey={expansionKey}
|
|
||||||
variant={isSystem ? 'system' : 'error'}
|
|
||||||
contentClassName={getContentClassName(entryType)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next action - simple completion indicator
|
|
||||||
if (entry.entry_type.type === 'next_action') {
|
|
||||||
return (
|
|
||||||
<div className="px-4 py-2 text-base">
|
|
||||||
<div className="flex items-center gap-2 text-green-600 dark:text-green-400">
|
|
||||||
<CheckCircleIcon className="size-icon-base" />
|
|
||||||
<span>{t('conversation.taskCompleted')}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default: assistant message or other types
|
|
||||||
return (
|
|
||||||
<div className="px-4 py-2 text-base">
|
|
||||||
<div className="flex items-start gap-2">
|
|
||||||
<RobotIcon className="size-icon-base mt-0.5 text-muted-foreground" />
|
|
||||||
<div className={cn('flex-1', getContentClassName(entryType))}>
|
|
||||||
{shouldRenderMarkdown(entryType) ? (
|
|
||||||
<WYSIWYGEditor
|
|
||||||
value={entry.content}
|
|
||||||
disabled
|
|
||||||
className="whitespace-pre-wrap break-words flex flex-col gap-1 font-light"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
entry.content
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MockDisplayConversationEntry;
|
|
||||||
@@ -4,7 +4,7 @@ import { cn } from '@/lib/utils';
|
|||||||
import {
|
import {
|
||||||
VirtualizedProcessLogs,
|
VirtualizedProcessLogs,
|
||||||
type LogEntry,
|
type LogEntry,
|
||||||
} from '../VirtualizedProcessLogs';
|
} from './VirtualizedProcessLogs';
|
||||||
import { useLogStream } from '@/hooks/useLogStream';
|
import { useLogStream } from '@/hooks/useLogStream';
|
||||||
import { useLogsPanel } from '@/contexts/LogsPanelContext';
|
import { useLogsPanel } from '@/contexts/LogsPanelContext';
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ import {
|
|||||||
ChatThinkingMessage,
|
ChatThinkingMessage,
|
||||||
ChatErrorMessage,
|
ChatErrorMessage,
|
||||||
ChatScriptEntry,
|
ChatScriptEntry,
|
||||||
} from './primitives/conversation';
|
} from '../primitives/conversation';
|
||||||
import type { DiffInput } from './primitives/conversation/DiffViewCard';
|
import type { DiffInput } from '../primitives/conversation/DiffViewCard';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
entry: NormalizedEntry;
|
entry: NormalizedEntry;
|
||||||
@@ -2,7 +2,7 @@ import { ArrowSquareOutIcon, SpinnerIcon } from '@phosphor-icons/react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { CollapsibleSectionHeader } from '../primitives/CollapsibleSectionHeader';
|
import { CollapsibleSectionHeader } from '../primitives/CollapsibleSectionHeader';
|
||||||
import { VirtualizedProcessLogs } from '../VirtualizedProcessLogs';
|
import { VirtualizedProcessLogs } from '../containers/VirtualizedProcessLogs';
|
||||||
import { PERSIST_KEYS } from '@/stores/useUiPreferencesStore';
|
import { PERSIST_KEYS } from '@/stores/useUiPreferencesStore';
|
||||||
import { getDevServerWorkingDir } from '@/lib/devServerUtils';
|
import { getDevServerWorkingDir } from '@/lib/devServerUtils';
|
||||||
import type { ExecutionProcess, PatchType } from 'shared/types';
|
import type { ExecutionProcess, PatchType } from 'shared/types';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { Session } from 'shared/types';
|
|||||||
import type { WorkspaceWithSession } from '@/types/attempt';
|
import type { WorkspaceWithSession } from '@/types/attempt';
|
||||||
import { SessionChatBoxContainer } from '@/components/ui-new/containers/SessionChatBoxContainer';
|
import { SessionChatBoxContainer } from '@/components/ui-new/containers/SessionChatBoxContainer';
|
||||||
import { ContextBarContainer } from '@/components/ui-new/containers/ContextBarContainer';
|
import { ContextBarContainer } from '@/components/ui-new/containers/ContextBarContainer';
|
||||||
import { ConversationList } from '../ConversationList';
|
import { ConversationList } from '../containers/ConversationListContainer';
|
||||||
import { EntriesProvider } from '@/contexts/EntriesContext';
|
import { EntriesProvider } from '@/contexts/EntriesContext';
|
||||||
import { MessageEditProvider } from '@/contexts/MessageEditContext';
|
import { MessageEditProvider } from '@/contexts/MessageEditContext';
|
||||||
import { RetryUiProvider } from '@/contexts/RetryUiContext';
|
import { RetryUiProvider } from '@/contexts/RetryUiContext';
|
||||||
|
|||||||
Reference in New Issue
Block a user