From f852a4c60e66ecbde4871715db75482e5cb27a04 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 8 Jun 2026 17:14:37 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=E4=BC=98=E5=8C=96=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- src/app/globals.css | 226 ++++++++++++------------ src/app/layout.tsx | 2 +- src/components/layout/AppShell.tsx | 163 ++++++++++++++--- src/components/layout/ChatListPanel.tsx | 64 ++----- src/components/layout/UnifiedTopBar.tsx | 46 ++--- 6 files changed, 289 insertions(+), 214 deletions(-) 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/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/layout/AppShell.tsx b/src/components/layout/AppShell.tsx index 6da3009..968ef82 100644 --- a/src/components/layout/AppShell.tsx +++ b/src/components/layout/AppShell.tsx @@ -2,6 +2,7 @@ import { useState, useEffect, useCallback, useMemo, useRef } from "react"; import { usePathname, useRouter } from "next/navigation"; +import Link from "next/link"; import { TooltipProvider } from "@/components/ui/tooltip"; // NavRail removed — navigation merged into ChatListPanel import { ChatListPanel } from "./ChatListPanel"; @@ -20,8 +21,18 @@ import { useGitStatus } from "@/hooks/useGitStatus"; import { SetupCenter } from '@/components/setup/SetupCenter'; import { Toaster } from '@/components/ui/toast'; import { useNotificationPoll } from '@/hooks/useNotificationPoll'; +import { useClientPlatform } from '@/hooks/useClientPlatform'; import { useGlobalSearchShortcut } from '@/hooks/useGlobalSearchShortcut'; import { GlobalSearchDialog } from './GlobalSearchDialog'; +import { Button } from '@/components/ui/button'; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/components/ui/tooltip'; +import { MagnifyingGlass, Gear } from '@/components/ui/icon'; +import { cn } from '@/lib/utils'; +import { useTranslation } from '@/hooks/useTranslation'; const SPLIT_SESSIONS_KEY = "codepilot:split-sessions"; const SPLIT_ACTIVE_COLUMN_KEY = "codepilot:split-active-column"; @@ -77,7 +88,8 @@ const LG_BREAKPOINT = 1024; export function AppShell({ children }: { children: React.ReactNode }) { const pathname = usePathname(); const router = useRouter(); - + const { t } = useTranslation(); + const { isWindows } = useClientPlatform(); const [chatListOpenRaw, setChatListOpenRaw] = useState(false); const [setupOpen, setSetupOpen] = useState(false); const [setupInitialCard, setSetupInitialCard] = useState<'claude' | 'provider' | 'project' | undefined>(); @@ -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 (