From 75beab35d91ad4b236d8f0ae5a0cd73af4a0c528 Mon Sep 17 00:00:00 2001 From: Louis Knight-Webb Date: Thu, 15 Jan 2026 14:53:54 +0000 Subject: [PATCH] refactor: Clean up root-level files in ui-new folder (#2079) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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. --- .../dialogs/scripts/ScriptFixerDialog.tsx | 2 +- .../ui-new/MockConversationList.tsx | 79 --- .../ui-new/MockDisplayConversationEntry.tsx | 461 ------------------ .../ConversationListContainer.tsx} | 0 .../containers/LogsContentContainer.tsx | 2 +- .../NewDisplayConversationEntry.tsx | 4 +- .../VirtualizedProcessLogs.tsx | 0 .../ui-new/views/PreviewControls.tsx | 2 +- .../ui-new/views/WorkspacesMain.tsx | 2 +- 9 files changed, 6 insertions(+), 546 deletions(-) delete mode 100644 frontend/src/components/ui-new/MockConversationList.tsx delete mode 100644 frontend/src/components/ui-new/MockDisplayConversationEntry.tsx rename frontend/src/components/ui-new/{ConversationList.tsx => containers/ConversationListContainer.tsx} (100%) rename frontend/src/components/ui-new/{ => containers}/NewDisplayConversationEntry.tsx (99%) rename frontend/src/components/ui-new/{ => containers}/VirtualizedProcessLogs.tsx (100%) diff --git a/frontend/src/components/dialogs/scripts/ScriptFixerDialog.tsx b/frontend/src/components/dialogs/scripts/ScriptFixerDialog.tsx index 3ef2ab2c..af3aaeb9 100644 --- a/frontend/src/components/dialogs/scripts/ScriptFixerDialog.tsx +++ b/frontend/src/components/dialogs/scripts/ScriptFixerDialog.tsx @@ -20,7 +20,7 @@ import { SelectValue, } from '@/components/ui/select'; 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 { defineModal } from '@/lib/modals'; import { repoApi, attemptsApi } from '@/lib/api'; diff --git a/frontend/src/components/ui-new/MockConversationList.tsx b/frontend/src/components/ui-new/MockConversationList.tsx deleted file mode 100644 index 501df704..00000000 --- a/frontend/src/components/ui-new/MockConversationList.tsx +++ /dev/null @@ -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 ( - - ); - } - 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 ( - - - - - - className="h-full scrollbar-none" - data={channelData} - initialLocation={INITIAL_TOP_ITEM} - computeItemKey={computeItemKey} - ItemContent={ItemContent} - Header={() =>
} - Footer={() =>
} - /> - - - - - ); -} - -export default MockConversationList; diff --git a/frontend/src/components/ui-new/MockDisplayConversationEntry.tsx b/frontend/src/components/ui-new/MockDisplayConversationEntry.tsx deleted file mode 100644 index 476ea835..00000000 --- a/frontend/src/components/ui-new/MockDisplayConversationEntry.tsx +++ /dev/null @@ -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; - -const getEntryIcon = (entryType: NormalizedEntryType) => { - const iconClassName = 'size-icon-xs'; - if (entryType.type === 'user_message' || entryType.type === 'user_feedback') { - return ; - } - if (entryType.type === 'assistant_message') { - return ; - } - if (entryType.type === 'system_message') { - return ; - } - if (entryType.type === 'error_message') { - return ; - } - 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 ; - } - - if (action_type.action === 'file_read') { - return ; - } else if (action_type.action === 'file_edit') { - return ; - } else if (action_type.action === 'command_run') { - return ; - } else if (action_type.action === 'search') { - return ; - } else if (action_type.action === 'web_fetch') { - return ; - } else if (action_type.action === 'task_create') { - return ; - } else if (action_type.action === 'plan_presentation') { - return ; - } else if (action_type.action === 'tool') { - return ; - } - return ; - } - return ; -}; - -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 ( -
-
-
{children}
- {onToggle && ( - - )} -
-
- ); -}; - -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 ( - - ); -}; - -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 = ( -
- {markdown ? ( - - ) : ( - content - )} -
- ); - - const firstLine = content.split('\n')[0]; - const PreviewInner = ( -
- {markdown ? ( - - ) : ( - firstLine - )} -
- ); - - if (!multiline) { - return {Inner}; - } - - return expanded ? ( - - {Inner} - - ) : ( - - {PreviewInner} - - ); -}; - -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) => ( -
{JSON.stringify(v, null, 2)}
- ); - - return ( -
- - - {entryType && getEntryIcon(entryType)} - {showInlineSummary ? ( - {inlineText} - ) : ( - {label} - )} - - - - {expanded && ( -
- {isCommand ? ( - <> - {argsText && ( - <> -
- {t('conversation.args')} -
-
{argsText}
- - )} - - {output && ( - <> -
- {t('conversation.output')} -
-
- -
- - )} - - ) : ( - <> - {isTool && actionType && ( - <> -
- {t('conversation.args')} -
-
- {renderJson(actionType.arguments)} -
-
- {t('conversation.result')} -
-
- {actionType.result?.type.type === 'markdown' && - actionType.result.value && ( - - )} - {actionType.result?.type.type === 'json' && - renderJson(actionType.result.value)} -
- - )} - - )} -
- )} -
- ); -}; - -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 ( -
-
- -
- -
-
-
- ); - } - - // 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 ( -
- {fileEditAction.changes.map((change, idx) => ( - - ))} -
- ); - } - - // Other tool uses - return ( -
- -
- ); - } - - // System or error messages - collapsible - if (isSystem || isError) { - return ( -
- -
- ); - } - - // Next action - simple completion indicator - if (entry.entry_type.type === 'next_action') { - return ( -
-
- - {t('conversation.taskCompleted')} -
-
- ); - } - - // Default: assistant message or other types - return ( -
-
- -
- {shouldRenderMarkdown(entryType) ? ( - - ) : ( - entry.content - )} -
-
-
- ); -} - -export default MockDisplayConversationEntry; diff --git a/frontend/src/components/ui-new/ConversationList.tsx b/frontend/src/components/ui-new/containers/ConversationListContainer.tsx similarity index 100% rename from frontend/src/components/ui-new/ConversationList.tsx rename to frontend/src/components/ui-new/containers/ConversationListContainer.tsx diff --git a/frontend/src/components/ui-new/containers/LogsContentContainer.tsx b/frontend/src/components/ui-new/containers/LogsContentContainer.tsx index b18878ed..bb8b384e 100644 --- a/frontend/src/components/ui-new/containers/LogsContentContainer.tsx +++ b/frontend/src/components/ui-new/containers/LogsContentContainer.tsx @@ -4,7 +4,7 @@ import { cn } from '@/lib/utils'; import { VirtualizedProcessLogs, type LogEntry, -} from '../VirtualizedProcessLogs'; +} from './VirtualizedProcessLogs'; import { useLogStream } from '@/hooks/useLogStream'; import { useLogsPanel } from '@/contexts/LogsPanelContext'; diff --git a/frontend/src/components/ui-new/NewDisplayConversationEntry.tsx b/frontend/src/components/ui-new/containers/NewDisplayConversationEntry.tsx similarity index 99% rename from frontend/src/components/ui-new/NewDisplayConversationEntry.tsx rename to frontend/src/components/ui-new/containers/NewDisplayConversationEntry.tsx index 2af4044c..6bf8b8da 100644 --- a/frontend/src/components/ui-new/NewDisplayConversationEntry.tsx +++ b/frontend/src/components/ui-new/containers/NewDisplayConversationEntry.tsx @@ -36,8 +36,8 @@ import { ChatThinkingMessage, ChatErrorMessage, ChatScriptEntry, -} from './primitives/conversation'; -import type { DiffInput } from './primitives/conversation/DiffViewCard'; +} from '../primitives/conversation'; +import type { DiffInput } from '../primitives/conversation/DiffViewCard'; type Props = { entry: NormalizedEntry; diff --git a/frontend/src/components/ui-new/VirtualizedProcessLogs.tsx b/frontend/src/components/ui-new/containers/VirtualizedProcessLogs.tsx similarity index 100% rename from frontend/src/components/ui-new/VirtualizedProcessLogs.tsx rename to frontend/src/components/ui-new/containers/VirtualizedProcessLogs.tsx diff --git a/frontend/src/components/ui-new/views/PreviewControls.tsx b/frontend/src/components/ui-new/views/PreviewControls.tsx index 7cc8647f..7c2a87da 100644 --- a/frontend/src/components/ui-new/views/PreviewControls.tsx +++ b/frontend/src/components/ui-new/views/PreviewControls.tsx @@ -2,7 +2,7 @@ import { ArrowSquareOutIcon, SpinnerIcon } from '@phosphor-icons/react'; import { useTranslation } from 'react-i18next'; import { cn } from '@/lib/utils'; import { CollapsibleSectionHeader } from '../primitives/CollapsibleSectionHeader'; -import { VirtualizedProcessLogs } from '../VirtualizedProcessLogs'; +import { VirtualizedProcessLogs } from '../containers/VirtualizedProcessLogs'; import { PERSIST_KEYS } from '@/stores/useUiPreferencesStore'; import { getDevServerWorkingDir } from '@/lib/devServerUtils'; import type { ExecutionProcess, PatchType } from 'shared/types'; diff --git a/frontend/src/components/ui-new/views/WorkspacesMain.tsx b/frontend/src/components/ui-new/views/WorkspacesMain.tsx index c1dac0c5..753a66ee 100644 --- a/frontend/src/components/ui-new/views/WorkspacesMain.tsx +++ b/frontend/src/components/ui-new/views/WorkspacesMain.tsx @@ -4,7 +4,7 @@ import type { Session } from 'shared/types'; import type { WorkspaceWithSession } from '@/types/attempt'; import { SessionChatBoxContainer } from '@/components/ui-new/containers/SessionChatBoxContainer'; import { ContextBarContainer } from '@/components/ui-new/containers/ContextBarContainer'; -import { ConversationList } from '../ConversationList'; +import { ConversationList } from '../containers/ConversationListContainer'; import { EntriesProvider } from '@/contexts/EntriesContext'; import { MessageEditProvider } from '@/contexts/MessageEditContext'; import { RetryUiProvider } from '@/contexts/RetryUiContext';