diff --git a/src/components/app/AppContent.tsx b/src/components/app/AppContent.tsx index 8142b223..f2806c6a 100644 --- a/src/components/app/AppContent.tsx +++ b/src/components/app/AppContent.tsx @@ -1,15 +1,13 @@ import { useEffect, useRef } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; - import Sidebar from '../sidebar/view/Sidebar'; import MainContent from '../main-content/view/MainContent'; -import MobileNav from './MobileNav'; - import { useWebSocket } from '../../contexts/WebSocketContext'; import { useDeviceSettings } from '../../hooks/useDeviceSettings'; import { useSessionProtection } from '../../hooks/useSessionProtection'; import { useProjectsState } from '../../hooks/useProjectsState'; +import MobileNav from './MobileNav'; export default function AppContent() { const navigate = useNavigate(); @@ -98,7 +96,7 @@ export default function AppContent() { ) : (
diff --git a/src/components/auth/view/ProtectedRoute.tsx b/src/components/auth/view/ProtectedRoute.tsx index bd76482a..d94dcaf1 100644 --- a/src/components/auth/view/ProtectedRoute.tsx +++ b/src/components/auth/view/ProtectedRoute.tsx @@ -1,9 +1,9 @@ import type { ReactNode } from 'react'; import { IS_PLATFORM } from '../../../constants/config'; import { useAuth } from '../context/AuthContext'; +import Onboarding from '../../onboarding/view/Onboarding'; import AuthLoadingScreen from './AuthLoadingScreen'; import LoginForm from './LoginForm'; -import Onboarding from '../../onboarding/view/Onboarding'; import SetupForm from './SetupForm'; type ProtectedRouteProps = { diff --git a/src/components/auth/view/SetupForm.tsx b/src/components/auth/view/SetupForm.tsx index f4d892d1..a67a8be6 100644 --- a/src/components/auth/view/SetupForm.tsx +++ b/src/components/auth/view/SetupForm.tsx @@ -74,7 +74,7 @@ export default function SetupForm() { title="Welcome to Claude Code UI" description="Set up your account to get started" footerText="This is a single-user system. Only one account can be created." - logo={CloudCLI} + logo={CloudCLI} >
{isSubmitting ? 'Setting up...' : 'Create Account'} diff --git a/src/components/chat/hooks/useChatComposerState.ts b/src/components/chat/hooks/useChatComposerState.ts index 950db891..bdba41d0 100644 --- a/src/components/chat/hooks/useChatComposerState.ts +++ b/src/components/chat/hooks/useChatComposerState.ts @@ -11,9 +11,7 @@ import type { } from 'react'; import { useDropzone } from 'react-dropzone'; import { authenticatedFetch } from '../../../utils/api'; - import { thinkingModes } from '../constants/thinkingModes'; - import { grantClaudeToolPermission } from '../utils/chatPermissions'; import { safeLocalStorage } from '../utils/chatStorage'; import type { @@ -21,10 +19,10 @@ import type { PendingPermissionRequest, PermissionMode, } from '../types/types'; -import { useFileMentions } from './useFileMentions'; -import { type SlashCommand, useSlashCommands } from './useSlashCommands'; import type { Project, ProjectSession, SessionProvider } from '../../../types/app'; import { escapeRegExp } from '../utils/chatFormatting'; +import { useFileMentions } from './useFileMentions'; +import { type SlashCommand, useSlashCommands } from './useSlashCommands'; type PendingViewSession = { sessionId: string | null; diff --git a/src/components/chat/hooks/useChatSessionState.ts b/src/components/chat/hooks/useChatSessionState.ts index c81954a5..71c9a6cb 100644 --- a/src/components/chat/hooks/useChatSessionState.ts +++ b/src/components/chat/hooks/useChatSessionState.ts @@ -1,6 +1,5 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import type { MutableRefObject } from 'react'; - import { api, authenticatedFetch } from '../../../utils/api'; import type { ChatMessage, Provider } from '../types/types'; import type { Project, ProjectSession } from '../../../types/app'; diff --git a/src/components/chat/hooks/useFileMentions.tsx b/src/components/chat/hooks/useFileMentions.tsx index 44450861..c53f4c7b 100644 --- a/src/components/chat/hooks/useFileMentions.tsx +++ b/src/components/chat/hooks/useFileMentions.tsx @@ -161,7 +161,7 @@ export function useFileMentions({ selectedProject, input, setInput, textareaRef fileMentionSet.has(part) ? ( {part} diff --git a/src/components/chat/tools/ToolRenderer.tsx b/src/components/chat/tools/ToolRenderer.tsx index d51456ff..1f1159f5 100644 --- a/src/components/chat/tools/ToolRenderer.tsx +++ b/src/components/chat/tools/ToolRenderer.tsx @@ -1,8 +1,8 @@ import React, { memo, useMemo, useCallback } from 'react'; -import { getToolConfig } from './configs/toolConfigs'; -import { OneLineDisplay, CollapsibleDisplay, ToolDiffViewer, MarkdownContent, FileListContent, TodoListContent, TaskListContent, TextContent, QuestionAnswerContent, SubagentContainer } from './components'; import type { Project } from '../../../types/app'; import type { SubagentChildTool } from '../types/types'; +import { getToolConfig } from './configs/toolConfigs'; +import { OneLineDisplay, CollapsibleDisplay, ToolDiffViewer, MarkdownContent, FileListContent, TodoListContent, TaskListContent, TextContent, QuestionAnswerContent, SubagentContainer } from './components'; type DiffLine = { type: string; @@ -202,7 +202,7 @@ export const ToolRenderer: React.FC = memo(({ const msg = displayConfig.getMessage?.(parsedData) || 'Success'; contentComponent = (
- + {msg} diff --git a/src/components/chat/tools/components/CollapsibleDisplay.tsx b/src/components/chat/tools/components/CollapsibleDisplay.tsx index dd4e2423..1175893e 100644 --- a/src/components/chat/tools/components/CollapsibleDisplay.tsx +++ b/src/components/chat/tools/components/CollapsibleDisplay.tsx @@ -43,7 +43,7 @@ export const CollapsibleDisplay: React.FC = ({ const borderColor = borderColorMap[toolCategory || 'default'] || borderColorMap.default; return ( -
+
= ({ {children} {showRawParameters && rawContent && ( -
- +
+ = ({ raw params -
+            
               {rawContent}
             
diff --git a/src/components/chat/tools/components/CollapsibleSection.tsx b/src/components/chat/tools/components/CollapsibleSection.tsx index 0d6615d4..c19e8e8e 100644 --- a/src/components/chat/tools/components/CollapsibleSection.tsx +++ b/src/components/chat/tools/components/CollapsibleSection.tsx @@ -23,10 +23,10 @@ export const CollapsibleSection: React.FC = ({ className = '' }) => { return ( -
- +
+ = ({ {toolName && ( - {toolName} + {toolName} )} {toolName && ( - / + / )} {onTitleClick ? ( ) : ( - + {title} )} - {action && {action}} + {action && {action}}
{children} diff --git a/src/components/chat/tools/components/ContentRenderers/FileListContent.tsx b/src/components/chat/tools/components/ContentRenderers/FileListContent.tsx index 695b4f2d..035166be 100644 --- a/src/components/chat/tools/components/ContentRenderers/FileListContent.tsx +++ b/src/components/chat/tools/components/ContentRenderers/FileListContent.tsx @@ -23,11 +23,11 @@ export const FileListContent: React.FC = ({ return (
{title && ( -
+
{title}
)} -
+
{files.map((file, index) => { const filePath = typeof file === 'string' ? file : file.path; const fileName = filePath.split('/').pop() || filePath; @@ -39,13 +39,13 @@ export const FileListContent: React.FC = ({ {index < files.length - 1 && ( - , + , )} ); diff --git a/src/components/chat/tools/components/ContentRenderers/QuestionAnswerContent.tsx b/src/components/chat/tools/components/ContentRenderers/QuestionAnswerContent.tsx index 9ff2c2a2..90c43c6d 100644 --- a/src/components/chat/tools/components/ContentRenderers/QuestionAnswerContent.tsx +++ b/src/components/chat/tools/components/ContentRenderers/QuestionAnswerContent.tsx @@ -33,31 +33,31 @@ export const QuestionAnswerContent: React.FC = ({ return (
{isExpanded && ( -
-
+
+
{q.options.map((opt) => { const wasSelected = answerLabels.includes(opt.label); return (
-
{wasSelected && ( - + )}
-
- +
+ {opt.label} {opt.description && ( - {opt.description} @@ -151,22 +151,22 @@ export const QuestionAnswerContent: React.FC = ({ {answerLabels.filter(lbl => !q.options.some(o => o.label === lbl)).map(lbl => (
-
- +
+
-
- {lbl} - (custom) +
+ {lbl} + (custom)
))} {skipped && hasAnyAnswer && ( -
+
No answer provided
)} @@ -178,7 +178,7 @@ export const QuestionAnswerContent: React.FC = ({ })} {!hasAnyAnswer && total === 1 && ( -
+
Skipped
)} diff --git a/src/components/chat/tools/components/ContentRenderers/TaskListContent.tsx b/src/components/chat/tools/components/ContentRenderers/TaskListContent.tsx index 5ae3f71a..c15fc392 100644 --- a/src/components/chat/tools/components/ContentRenderers/TaskListContent.tsx +++ b/src/components/chat/tools/components/ContentRenderers/TaskListContent.tsx @@ -39,7 +39,7 @@ function parseTaskContent(content: string): TaskItem[] { const statusConfig = { completed: { icon: ( - + ), @@ -48,7 +48,7 @@ const statusConfig = { }, in_progress: { icon: ( - + ), @@ -57,7 +57,7 @@ const statusConfig = { }, pending: { icon: ( - + ), @@ -76,7 +76,7 @@ export const TaskListContent: React.FC = ({ content }) => // If we couldn't parse any tasks, fall back to text display if (tasks.length === 0) { return ( -
+      
         {content}
       
); @@ -87,13 +87,13 @@ export const TaskListContent: React.FC = ({ content }) => return (
-
+
{completed}/{total} completed -
+
0 ? (completed / total) * 100 : 0}%` }} />
@@ -104,16 +104,16 @@ export const TaskListContent: React.FC = ({ content }) => return (
{config.icon} - + #{task.id} - + {task.subject} - + {task.status.replace('_', ' ')}
diff --git a/src/components/chat/tools/components/ContentRenderers/TextContent.tsx b/src/components/chat/tools/components/ContentRenderers/TextContent.tsx index 2a5702f1..abe3367e 100644 --- a/src/components/chat/tools/components/ContentRenderers/TextContent.tsx +++ b/src/components/chat/tools/components/ContentRenderers/TextContent.tsx @@ -26,7 +26,7 @@ export const TextContent: React.FC = ({ } return ( -
+      
         {formattedJson}
       
); @@ -34,7 +34,7 @@ export const TextContent: React.FC = ({ if (format === 'code') { return ( -
+      
         {content}
       
); @@ -42,7 +42,7 @@ export const TextContent: React.FC = ({ // Plain text return ( -
+
{content}
); diff --git a/src/components/chat/tools/components/ContentRenderers/TodoList.tsx b/src/components/chat/tools/components/ContentRenderers/TodoList.tsx index ab25cb49..a9e0a403 100644 --- a/src/components/chat/tools/components/ContentRenderers/TodoList.tsx +++ b/src/components/chat/tools/components/ContentRenderers/TodoList.tsx @@ -79,25 +79,25 @@ const TodoRow = memo( const StatusIcon = statusConfig.icon; return ( -
-
+
+
-
-
+
+

{todo.content}

-
+
{todo.priority} {todo.status.replace('_', ' ')} @@ -136,7 +136,7 @@ const TodoList = memo( return (
{isResult && ( -
+
Todo List ({normalizedTodos.length}{' '} {normalizedTodos.length === 1 ? 'item' : 'items'})
diff --git a/src/components/chat/tools/components/InteractiveRenderers/AskUserQuestionPanel.tsx b/src/components/chat/tools/components/InteractiveRenderers/AskUserQuestionPanel.tsx index 71868510..5a75390f 100644 --- a/src/components/chat/tools/components/InteractiveRenderers/AskUserQuestionPanel.tsx +++ b/src/components/chat/tools/components/InteractiveRenderers/AskUserQuestionPanel.tsx @@ -148,32 +148,32 @@ export const AskUserQuestionPanel: React.FC = ({ tabIndex={-1} onKeyDown={handleKeyDown} className={`w-full outline-none transition-all duration-500 ease-out ${ - mounted ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-3' + mounted ? 'translate-y-0 opacity-100' : 'translate-y-3 opacity-0' }`} > -
+
{/* Accent line */} -
+
{/* Header + Question — compact */} -
-
+
+
{/* Question icon */}
-
- +
+
-
+
-
- +
+ Claude needs your input {q.header && ( - + {q.header} )} @@ -181,7 +181,7 @@ export const AskUserQuestionPanel: React.FC = ({ {/* Step counter */} {!isSingle && ( - + {currentStep + 1}/{total} )} @@ -189,7 +189,7 @@ export const AskUserQuestionPanel: React.FC = ({ {/* Progress dots (multi-question) */} {!isSingle && ( -
+
{questions.map((_, i) => (
{/* Options — tight spacing */} -
+
{q.options.map((opt, optIdx) => { const isSelected = selected.has(opt.label); @@ -226,25 +226,25 @@ export const AskUserQuestionPanel: React.FC = ({ key={opt.label} type="button" onClick={() => toggleOption(currentStep, opt.label, multi)} - className={`group w-full text-left flex items-center gap-2.5 px-3 py-2 rounded-lg border transition-all duration-150 ${ + className={`group flex w-full items-center gap-2.5 rounded-lg border px-3 py-2 text-left transition-all duration-150 ${ isSelected - ? 'border-blue-300 dark:border-blue-600 bg-blue-50/80 dark:bg-blue-900/25 ring-1 ring-blue-200/50 dark:ring-blue-700/30' - : 'border-gray-200 dark:border-gray-700/60 hover:border-gray-300 dark:hover:border-gray-600 hover:bg-gray-50/60 dark:hover:bg-gray-750/50' + ? 'border-blue-300 bg-blue-50/80 ring-1 ring-blue-200/50 dark:border-blue-600 dark:bg-blue-900/25 dark:ring-blue-700/30' + : 'dark:hover:bg-gray-750/50 border-gray-200 hover:border-gray-300 hover:bg-gray-50/60 dark:border-gray-700/60 dark:hover:border-gray-600' }`} > {/* Keyboard hint */} - {optIdx + 1} -
+
{opt.label} @@ -262,7 +262,7 @@ export const AskUserQuestionPanel: React.FC = ({ {/* Selection check */} {isSelected && ( - + )} @@ -274,28 +274,28 @@ export const AskUserQuestionPanel: React.FC = ({ Other... {isOtherOn && ( - + )} @@ -320,9 +320,9 @@ export const AskUserQuestionPanel: React.FC = ({ e.stopPropagation(); }} placeholder="Type your answer..." - className="w-full text-[13px] rounded-lg border-0 bg-gray-50 dark:bg-gray-900/60 text-gray-900 dark:text-gray-100 px-3 py-1.5 outline-none ring-1 ring-gray-200 dark:ring-gray-700 focus:ring-2 focus:ring-blue-400 dark:focus:ring-blue-500 placeholder:text-gray-400 dark:placeholder:text-gray-600 transition-shadow duration-200" + className="w-full rounded-lg border-0 bg-gray-50 px-3 py-1.5 text-[13px] text-gray-900 outline-none ring-1 ring-gray-200 transition-shadow duration-200 placeholder:text-gray-400 focus:ring-2 focus:ring-blue-400 dark:bg-gray-900/60 dark:text-gray-100 dark:ring-gray-700 dark:placeholder:text-gray-600 dark:focus:ring-blue-500" /> - + Enter
@@ -332,11 +332,11 @@ export const AskUserQuestionPanel: React.FC = ({
{/* Footer — compact */} -
+
) : ( )}
diff --git a/src/components/chat/tools/components/OneLineDisplay.tsx b/src/components/chat/tools/components/OneLineDisplay.tsx index a73bd22a..a3fcd421 100644 --- a/src/components/chat/tools/components/OneLineDisplay.tsx +++ b/src/components/chat/tools/components/OneLineDisplay.tsx @@ -68,16 +68,16 @@ export const OneLineDisplay: React.FC = ({ const renderCopyButton = () => ( ) : ( - + {filePath} )} - + {badge}
{/* Diff lines */} -
+
{diffLines.map((diffLine, i) => (
{diffLine.type === 'removed' ? '-' : '+'} {diffLine.content} diff --git a/src/components/chat/view/ChatInterface.tsx b/src/components/chat/view/ChatInterface.tsx index eb888be9..90c1921d 100644 --- a/src/components/chat/view/ChatInterface.tsx +++ b/src/components/chat/view/ChatInterface.tsx @@ -1,15 +1,14 @@ import React, { useCallback, useEffect, useRef } from 'react'; -import { QuickSettingsPanel } from '../../quick-settings-panel'; -import { useTasksSettings } from '../../../contexts/TasksSettingsContext'; import { useTranslation } from 'react-i18next'; -import ChatMessagesPane from './subcomponents/ChatMessagesPane'; -import ChatComposer from './subcomponents/ChatComposer'; -import type { ChatInterfaceProps } from '../types/types'; +import { useTasksSettings } from '../../../contexts/TasksSettingsContext'; +import { QuickSettingsPanel } from '../../quick-settings-panel'; +import type { ChatInterfaceProps, Provider } from '../types/types'; import { useChatProviderState } from '../hooks/useChatProviderState'; import { useChatSessionState } from '../hooks/useChatSessionState'; import { useChatRealtimeHandlers } from '../hooks/useChatRealtimeHandlers'; import { useChatComposerState } from '../hooks/useChatComposerState'; -import type { Provider } from '../types/types'; +import ChatMessagesPane from './subcomponents/ChatMessagesPane'; +import ChatComposer from './subcomponents/ChatComposer'; type PendingViewSession = { @@ -259,7 +258,7 @@ function ChatInterface({ : t('messageTypes.claude'); return ( -
+

{t('projectSelection.startChatWithProvider', { @@ -274,7 +273,7 @@ function ChatInterface({ return ( <> -

+
-
-
- +
+
+
{selectedProvider === 'cursor' ? 'Cursor' : selectedProvider === 'codex' ? 'Codex' : selectedProvider === 'gemini' ? 'Gemini' : 'Claude'}
-
+
.
diff --git a/src/components/chat/view/subcomponents/ChatComposer.tsx b/src/components/chat/view/subcomponents/ChatComposer.tsx index 6ac150db..35bf7548 100644 --- a/src/components/chat/view/subcomponents/ChatComposer.tsx +++ b/src/components/chat/view/subcomponents/ChatComposer.tsx @@ -1,9 +1,3 @@ -import CommandMenu from './CommandMenu'; -import ClaudeStatus from './ClaudeStatus'; -import MicButton from '../../../mic-button/view/MicButton'; -import ImageAttachment from './ImageAttachment'; -import PermissionRequestsBanner from './PermissionRequestsBanner'; -import ChatInputControls from './ChatInputControls'; import { useTranslation } from 'react-i18next'; import type { ChangeEvent, @@ -17,7 +11,13 @@ import type { SetStateAction, TouchEvent, } from 'react'; +import MicButton from '../../../mic-button/view/MicButton'; import type { PendingPermissionRequest, PermissionMode, Provider } from '../../types/types'; +import CommandMenu from './CommandMenu'; +import ClaudeStatus from './ClaudeStatus'; +import ImageAttachment from './ImageAttachment'; +import PermissionRequestsBanner from './PermissionRequestsBanner'; +import ChatInputControls from './ChatInputControls'; interface MentionableFile { name: string; @@ -169,7 +169,7 @@ export default function ChatComposer({ : ''; return ( -
+
{!hasQuestionPanel && (
)} -
+
}
- {!hasQuestionPanel && ) => void} className="relative max-w-4xl mx-auto"> + {!hasQuestionPanel && ) => void} className="relative mx-auto max-w-4xl"> {isDragActive && ( -
-
- +
+
+ 0 && ( -
+
{attachedImages.map((file, index) => ( 0 && ( -
+
{filteredFiles.map((file, index) => (
{ event.preventDefault(); @@ -258,8 +258,8 @@ export default function ChatComposer({ onSelectFile(file); }} > -
{file.name}
-
{file.path}
+
{file.name}
+
{file.path}
))}
@@ -277,13 +277,13 @@ export default function ChatComposer({
-