diff --git a/package.json b/package.json index 8e13847..879a485 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "codewiz", + "name": "wizai", "version": "0.54.0", "private": true, "license": "BUSL-1.1", diff --git a/src/app/chat/[id]/page.tsx b/src/app/chat/[id]/page.tsx index d978a9e..a3f5a71 100644 --- a/src/app/chat/[id]/page.tsx +++ b/src/app/chat/[id]/page.tsx @@ -25,6 +25,7 @@ export default function ChatSessionPage({ params }: ChatSessionPageProps) { const [sessionInfoLoaded, setSessionInfoLoaded] = useState(false); const [sessionPermissionProfile, setSessionPermissionProfile] = useState<'default' | 'full_access'>('default'); const [sessionMode, setSessionMode] = useState<'code' | 'plan'>('code'); + const [sessionAgentMode, setSessionAgentMode] = useState<'claude' | 'agent'>('claude'); const [sessionHasSummary, setSessionHasSummary] = useState(false); const { setWorkingDirectory, setSessionId, setSessionTitle: setPanelSessionTitle, setFileTreeOpen, setGitPanelOpen, setDashboardPanelOpen } = usePanel(); const targetFilePath = searchParams.get('file') || undefined; @@ -39,6 +40,7 @@ export default function ChatSessionPage({ params }: ChatSessionPageProps) { setSessionModel(''); setSessionProviderId(''); setSessionInfoLoaded(false); + setSessionAgentMode('claude'); async function loadSession() { try { @@ -65,6 +67,7 @@ export default function ChatSessionPage({ params }: ChatSessionPageProps) { setSessionProviderId(resolved.providerId); setSessionPermissionProfile(data.session.permission_profile || 'default'); setSessionMode((data.session.mode as 'code' | 'plan') || 'code'); + setSessionAgentMode((data.session.agent_mode as 'claude' | 'agent') || 'claude'); setSessionHasSummary(!!data.session.context_summary); } } catch { @@ -186,7 +189,7 @@ export default function ChatSessionPage({ params }: ChatSessionPageProps) { return (
- +
); } diff --git a/src/app/globals.css b/src/app/globals.css index 3b6507e..610ef07 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -64,126 +64,126 @@ } :root { - --radius: 0.75rem; - --background: oklch(1 0 0); - --foreground: oklch(0.147 0.004 49.25); - --card: oklch(1 0 0); - --card-foreground: oklch(0.147 0.004 49.25); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.147 0.004 49.25); - --primary: oklch(0.546 0.245 262.881); - --primary-foreground: oklch(0.985 0.001 106.423); - --secondary: oklch(0.97 0.001 106.424); - --secondary-foreground: oklch(0.216 0.006 56.043); - --muted: oklch(0.97 0.001 106.424); - --muted-foreground: oklch(0.553 0.013 58.071); - --accent: oklch(0.97 0.001 106.424); - --accent-foreground: oklch(0.216 0.006 56.043); - --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.923 0.003 48.717); - --input: oklch(0.923 0.003 48.717); - --ring: oklch(0.546 0.245 262.881); - --chart-1: oklch(0.546 0.245 262.881); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --sidebar: oklch(0.985 0.001 106.423); - --sidebar-foreground: oklch(0.147 0.004 49.25); - --sidebar-primary: oklch(0.546 0.245 262.881); - --sidebar-primary-foreground: oklch(0.985 0.001 106.423); - --sidebar-accent: oklch(0.97 0.001 106.424); - --sidebar-accent-foreground: oklch(0.216 0.006 56.043); - --sidebar-border: oklch(0.923 0.003 48.717); - --sidebar-ring: oklch(0.546 0.245 262.881); + --radius: 0.5rem; + --background: oklch(0.98 0.002 60); + --foreground: oklch(0.18 0.015 50); + --card: oklch(0.99 0.001 60); + --card-foreground: oklch(0.18 0.015 50); + --popover: oklch(0.99 0.001 60); + --popover-foreground: oklch(0.18 0.015 50); + --primary: oklch(0.65 0.19 55); + --primary-foreground: oklch(0.99 0.001 60); + --secondary: oklch(0.94 0.005 55); + --secondary-foreground: oklch(0.30 0.015 50); + --muted: oklch(0.93 0.005 55); + --muted-foreground: oklch(0.50 0.015 50); + --accent: oklch(0.94 0.005 55); + --accent-foreground: oklch(0.30 0.015 50); + --destructive: oklch(0.58 0.22 25); + --border: oklch(0.88 0.005 55); + --input: oklch(0.92 0.005 55); + --ring: oklch(0.65 0.19 55); + --chart-1: oklch(0.65 0.19 55); + --chart-2: oklch(0.60 0.15 180); + --chart-3: oklch(0.55 0.12 250); + --chart-4: oklch(0.70 0.14 85); + --chart-5: oklch(0.60 0.18 30); + --sidebar: oklch(0.22 0.01 50); + --sidebar-foreground: oklch(0.90 0.005 60); + --sidebar-primary: oklch(0.65 0.19 55); + --sidebar-primary-foreground: oklch(0.99 0.001 60); + --sidebar-accent: oklch(0.30 0.015 50); + --sidebar-accent-foreground: oklch(0.90 0.005 60); + --sidebar-border: oklch(0.32 0.01 50); + --sidebar-ring: oklch(0.65 0.19 55); --user-bubble: oklch(0.22 0.005 250); - --user-bubble-foreground: oklch(0.985 0.001 106.423); + --user-bubble-foreground: oklch(0.95 0.002 60); /* Status semantic tokens */ - --status-success: oklch(0.527 0.154 150.069); - --status-success-foreground: oklch(0.393 0.124 148.697); - --status-success-muted: oklch(0.527 0.154 150.069 / 10%); - --status-success-border: oklch(0.527 0.154 150.069 / 20%); - --status-warning: oklch(0.705 0.213 47.604); - --status-warning-foreground: oklch(0.646 0.222 41.116); - --status-warning-muted: oklch(0.705 0.213 47.604 / 10%); - --status-warning-border: oklch(0.705 0.213 47.604 / 20%); - --status-error: oklch(0.577 0.245 27.325); - --status-error-foreground: oklch(0.577 0.245 27.325); - --status-error-muted: oklch(0.577 0.245 27.325 / 10%); - --status-error-border: oklch(0.577 0.245 27.325 / 20%); - --status-info: oklch(0.546 0.245 262.881); - --status-info-foreground: oklch(0.546 0.245 262.881); - --status-info-muted: oklch(0.546 0.245 262.881 / 10%); - --status-info-border: oklch(0.546 0.245 262.881 / 20%); + --status-success: oklch(0.60 0.18 148); + --status-success-foreground: oklch(0.40 0.12 148); + --status-success-muted: oklch(0.60 0.18 148 / 12%); + --status-success-border: oklch(0.60 0.18 148 / 25%); + --status-warning: oklch(0.72 0.18 80); + --status-warning-foreground: oklch(0.30 0.10 50); + --status-warning-muted: oklch(0.72 0.18 80 / 12%); + --status-warning-border: oklch(0.72 0.18 80 / 25%); + --status-error: oklch(0.60 0.22 25); + --status-error-foreground: oklch(0.99 0.001 60); + --status-error-muted: oklch(0.60 0.22 25 / 12%); + --status-error-border: oklch(0.60 0.22 25 / 25%); + --status-info: oklch(0.60 0.18 250); + --status-info-foreground: oklch(0.99 0.001 60); + --status-info-muted: oklch(0.60 0.18 250 / 12%); + --status-info-border: oklch(0.60 0.18 250 / 25%); /* Terminal / code-block (bash) tokens */ - --terminal-bg: oklch(0.13 0.004 49.25); - --terminal-foreground: oklch(0.87 0.006 106); - --terminal-muted: oklch(0.55 0.01 58); - --terminal-border: oklch(0.30 0.005 49); - --terminal-accent: oklch(0.55 0.17 155); - --terminal-hover-bg: oklch(0.25 0.005 49); - --terminal-gradient-from: oklch(0.13 0.004 49.25); + --terminal-bg: oklch(0.12 0.005 50); + --terminal-foreground: oklch(0.88 0.005 60); + --terminal-muted: oklch(0.55 0.01 55); + --terminal-border: oklch(0.30 0.005 50); + --terminal-accent: oklch(0.65 0.18 55); + --terminal-hover-bg: oklch(0.22 0.005 50); + --terminal-gradient-from: oklch(0.12 0.005 50); } .dark { - --background: oklch(0.147 0.004 49.25); - --foreground: oklch(0.985 0.001 106.423); - --card: oklch(0.216 0.006 56.043); - --card-foreground: oklch(0.985 0.001 106.423); - --popover: oklch(0.216 0.006 56.043); - --popover-foreground: oklch(0.985 0.001 106.423); - --primary: oklch(0.623 0.214 259.815); - --primary-foreground: oklch(0.985 0.001 106.423); - --secondary: oklch(0.268 0.007 34.298); - --secondary-foreground: oklch(0.985 0.001 106.423); - --muted: oklch(0.268 0.007 34.298); - --muted-foreground: oklch(0.709 0.01 56.259); - --accent: oklch(0.268 0.007 34.298); - --accent-foreground: oklch(0.985 0.001 106.423); - --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.623 0.214 259.815); - --chart-1: oklch(0.623 0.214 259.815); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.216 0.006 56.043); - --sidebar-foreground: oklch(0.985 0.001 106.423); - --sidebar-primary: oklch(0.623 0.214 259.815); - --sidebar-primary-foreground: oklch(0.985 0.001 106.423); - --sidebar-accent: oklch(0.268 0.007 34.298); - --sidebar-accent-foreground: oklch(0.985 0.001 106.423); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.623 0.214 259.815); - --user-bubble: oklch(0.90 0.003 106); - --user-bubble-foreground: oklch(0.147 0.004 49.25); + --background: oklch(0.11 0.01 50); + --foreground: oklch(0.92 0.003 60); + --card: oklch(0.16 0.012 50); + --card-foreground: oklch(0.92 0.003 60); + --popover: oklch(0.16 0.012 50); + --popover-foreground: oklch(0.92 0.003 60); + --primary: oklch(0.70 0.18 55); + --primary-foreground: oklch(0.11 0.01 50); + --secondary: oklch(0.22 0.01 50); + --secondary-foreground: oklch(0.88 0.005 60); + --muted: oklch(0.22 0.01 50); + --muted-foreground: oklch(0.60 0.01 55); + --accent: oklch(0.28 0.015 50); + --accent-foreground: oklch(0.92 0.003 60); + --destructive: oklch(0.65 0.22 25); + --border: oklch(0.28 0.01 50); + --input: oklch(0.22 0.01 50); + --ring: oklch(0.70 0.18 55); + --chart-1: oklch(0.70 0.18 55); + --chart-2: oklch(0.62 0.16 180); + --chart-3: oklch(0.58 0.15 250); + --chart-4: oklch(0.68 0.15 85); + --chart-5: oklch(0.65 0.18 30); + --sidebar: oklch(0.11 0.01 50); + --sidebar-foreground: oklch(0.88 0.005 60); + --sidebar-primary: oklch(0.70 0.18 55); + --sidebar-primary-foreground: oklch(0.11 0.01 50); + --sidebar-accent: oklch(0.22 0.012 50); + --sidebar-accent-foreground: oklch(0.88 0.005 60); + --sidebar-border: oklch(0.26 0.01 50); + --sidebar-ring: oklch(0.70 0.18 55); + --user-bubble: oklch(0.70 0.18 220); + --user-bubble-foreground: oklch(0.11 0.01 50); /* Status semantic tokens */ - --status-success: oklch(0.527 0.154 150.069); - --status-success-foreground: oklch(0.448 0.133 148.697); - --status-success-muted: oklch(0.527 0.154 150.069 / 10%); - --status-success-border: oklch(0.527 0.154 150.069 / 20%); - --status-warning: oklch(0.705 0.213 47.604); - --status-warning-foreground: oklch(0.705 0.213 47.604); - --status-warning-muted: oklch(0.705 0.213 47.604 / 10%); - --status-warning-border: oklch(0.705 0.213 47.604 / 20%); - --status-error: oklch(0.704 0.191 22.216); - --status-error-foreground: oklch(0.704 0.191 22.216); - --status-error-muted: oklch(0.704 0.191 22.216 / 10%); - --status-error-border: oklch(0.704 0.191 22.216 / 20%); - --status-info: oklch(0.623 0.214 259.815); - --status-info-foreground: oklch(0.623 0.214 259.815); - --status-info-muted: oklch(0.623 0.214 259.815 / 10%); - --status-info-border: oklch(0.623 0.214 259.815 / 20%); - /* Terminal tokens — dark mode: stronger border contrast against dark page bg */ - --terminal-bg: oklch(0.10 0.002 49); - --terminal-foreground: oklch(0.87 0.006 106); - --terminal-muted: oklch(0.60 0.01 58); - --terminal-border: oklch(0.35 0.006 49); - --terminal-accent: oklch(0.62 0.17 155); - --terminal-hover-bg: oklch(0.22 0.005 49); - --terminal-gradient-from: oklch(0.10 0.002 49); + --status-success: oklch(0.65 0.18 148); + --status-success-foreground: oklch(0.11 0.01 50); + --status-success-muted: oklch(0.65 0.18 148 / 15%); + --status-success-border: oklch(0.65 0.18 148 / 30%); + --status-warning: oklch(0.75 0.18 80); + --status-warning-foreground: oklch(0.11 0.01 50); + --status-warning-muted: oklch(0.75 0.18 80 / 15%); + --status-warning-border: oklch(0.75 0.18 80 / 30%); + --status-error: oklch(0.65 0.22 25); + --status-error-foreground: oklch(0.11 0.01 50); + --status-error-muted: oklch(0.65 0.22 25 / 15%); + --status-error-border: oklch(0.65 0.22 25 / 30%); + --status-info: oklch(0.65 0.18 250); + --status-info-foreground: oklch(0.11 0.01 50); + --status-info-muted: oklch(0.65 0.18 250 / 15%); + --status-info-border: oklch(0.65 0.18 250 / 30%); + /* Terminal tokens — dark mode: warm dark background */ + --terminal-bg: oklch(0.09 0.008 50); + --terminal-foreground: oklch(0.88 0.005 60); + --terminal-muted: oklch(0.55 0.01 55); + --terminal-border: oklch(0.28 0.008 50); + --terminal-accent: oklch(0.70 0.18 55); + --terminal-hover-bg: oklch(0.18 0.008 50); + --terminal-gradient-from: oklch(0.09 0.008 50); } @layer base { diff --git a/src/app/layout.tsx b/src/app/layout.tsx index c2af808..86a1dff 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -20,7 +20,7 @@ const geistMono = Geist_Mono({ }); export const metadata: Metadata = { - title: "CodePilot", + title: "WizAI", description: "A multi-model AI agent desktop client", }; diff --git a/src/components/chat/ChatView.tsx b/src/components/chat/ChatView.tsx index 6ef76dc..c02968a 100644 --- a/src/components/chat/ChatView.tsx +++ b/src/components/chat/ChatView.tsx @@ -59,12 +59,13 @@ interface ChatViewProps { initialPermissionProfile?: 'default' | 'full_access'; initialMode?: 'code' | 'plan'; initialHasSummary?: boolean; + initialAgentMode?: 'claude' | 'agent'; } /** Maximum messages kept in React state. Older messages are trimmed and reloaded on scroll. */ const MAX_MESSAGES_IN_MEMORY = 300; -export function ChatView({ sessionId, initialMessages = [], initialHasMore = false, modelName, providerId, initialPermissionProfile, initialMode, initialHasSummary }: ChatViewProps) { +export function ChatView({ sessionId, initialMessages = [], initialHasMore = false, modelName, providerId, initialPermissionProfile, initialMode, initialHasSummary, initialAgentMode }: ChatViewProps) { const { setStreamingSessionId, workingDirectory, setPendingApprovalSessionId, setDashboardPanelOpen, setFileTreeOpen, setIsAssistantWorkspace } = usePanel(); const { t } = useTranslation(); const router = useRouter(); @@ -127,6 +128,7 @@ export function ChatView({ sessionId, initialMessages = [], initialHasMore = fal }); }, []); const [mode, setMode] = useState(initialMode || 'code'); + const [agentMode, setAgentMode] = useState<'claude' | 'agent'>(initialAgentMode || 'claude'); const [currentModel, setCurrentModel] = useState(() => modelName || (typeof window !== 'undefined' ? localStorage.getItem('codepilot:last-model') : null) || 'sonnet'); const [currentProviderId, setCurrentProviderId] = useState(() => providerId || (typeof window !== 'undefined' ? localStorage.getItem('codepilot:last-provider-id') : null) || ''); const [selectedEffort, setSelectedEffort] = useState(undefined); @@ -235,6 +237,19 @@ export function ChatView({ sessionId, initialMessages = [], initialHasMore = fal const sendMessageRef = useRef<(content: string, files?: FileAttachment[], systemPromptAppend?: string, displayOverride?: string, mentions?: MentionRef[]) => Promise>(undefined); const initMetaRef = useRef<{ tools?: unknown; slash_commands?: unknown; skills?: unknown } | null>(null); + const handleAgentModeChange = useCallback((newAgentMode: 'claude' | 'agent') => { + setAgentMode(newAgentMode); + if (sessionId) { + fetch(`/api/chat/sessions/${sessionId}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ agent_mode: newAgentMode }), + }).then(() => { + window.dispatchEvent(new CustomEvent('session-updated')); + }).catch(() => { /* silent */ }); + } + }, [sessionId]); + const handleModeChange = useCallback((newMode: string) => { setMode(newMode); if (sessionId) { @@ -876,6 +891,28 @@ export function ChatView({ sessionId, initialMessages = [], initialHasMore = fal )} + {/* Agent mode toggle */} +
+ Agent: + + + {agentMode === 'agent' ? 'Agent' : 'Claude'} + +
(); @@ -495,29 +507,86 @@ export function AppShell({ children }: { children: React.ReactNode }) { -
- - - - {chatListOpen && ( - - )} -
- -
-
-
- {isSplitActive ? ( - - ) : ( - {children} - )} -
+
+ {/* ===== TOP NAVIGATION BAR ===== */} +
+ {/* Brand */} +
+
+ + + + + +
+ WizAI +
+ + {/* Main nav */} + + + {/* Spacer */} +
+ + {/* Right side controls */} +
+ + + {isWindows &&
} +
+
+ + {/* ===== MAIN CONTENT AREA ===== */} +
+ + + + {chatListOpen && ( + + )} +
+ +
+
+
+ {isSplitActive ? ( + + ) : ( + {children} + )} +
+
+ {isChatDetailRoute && }
- {isChatDetailRoute && }
@@ -537,3 +606,51 @@ export function AppShell({ children }: { children: React.ReactNode }) { ); } + +// ===== INLINE NAV COMPONENTS ===== + +function NavNavItem({ href, label, isActive }: { href: string; label: string; isActive: boolean }) { + return ( + + + + ); +} + +function NavRailSettingsButton({ pathname }: { pathname: string }) { + const isSettingsActive = pathname === "/settings" || pathname.startsWith("/settings/"); + return ( + + +
+ +
+
+ Settings +
+ ); +} diff --git a/src/components/layout/ChatListPanel.tsx b/src/components/layout/ChatListPanel.tsx index 9c8469e..cc8dca2 100644 --- a/src/components/layout/ChatListPanel.tsx +++ b/src/components/layout/ChatListPanel.tsx @@ -6,14 +6,8 @@ import Link from "next/link"; import { motion, AnimatePresence } from "motion/react"; import { MagnifyingGlass, - FileArrowDown, - Plus, FolderPlus, - Lightning, - Plug, - Terminal, - Image, - WifiHigh, + Plus, Gear, } from "@/components/ui/icon"; import { Button } from "@/components/ui/button"; @@ -419,28 +413,17 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) { if (!open) return null; - const navItems = [ - { href: "/skills", label: t('nav.skills' as TranslationKey), icon: Lightning }, - { href: "/mcp", label: t('nav.mcp' as TranslationKey), icon: Plug }, - { href: "/cli-tools", label: t('nav.cliTools' as TranslationKey), icon: Terminal }, - { href: "/gallery", label: t('nav.gallery' as TranslationKey), icon: Image }, - { href: "/bridge", label: t('nav.bridge' as TranslationKey), icon: WifiHigh }, - ]; - return (