From bbf91a98ca2b9b7f1d859e2ebbfe1994d9a753f6 Mon Sep 17 00:00:00 2001 From: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com> Date: Wed, 11 Feb 2026 18:51:14 +0800 Subject: [PATCH] refactor(desktop): redesign layout with sidebar navigation - Replace tab navigation with collapsible sidebar - Fix header scroll issue with proper flex container structure - Add TooltipProvider at app root - Simplify onboarding store by removing forceOnboarding flag Co-Authored-By: Claude Opus 4.5 --- apps/desktop/src/renderer/src/App.tsx | 9 +- .../desktop/src/renderer/src/pages/layout.tsx | 144 ++++++++++-------- .../src/renderer/src/stores/onboarding.ts | 12 +- 3 files changed, 90 insertions(+), 75 deletions(-) diff --git a/apps/desktop/src/renderer/src/App.tsx b/apps/desktop/src/renderer/src/App.tsx index bf706081..b0b1f698 100644 --- a/apps/desktop/src/renderer/src/App.tsx +++ b/apps/desktop/src/renderer/src/App.tsx @@ -1,6 +1,7 @@ import { useEffect } from 'react' import { createHashRouter, Navigate, RouterProvider } from 'react-router-dom' import { ThemeProvider } from './components/theme-provider' +import { TooltipProvider } from '@multica/ui/components/ui/tooltip' import Layout from './pages/layout' import HomePage from './pages/home' import ChatPage from './pages/chat' @@ -15,8 +16,7 @@ import { useChannelsStore } from './stores/channels' function OnboardingGuard({ children }: { children: React.ReactNode }) { const completed = useOnboardingStore((s) => s.completed) - const forceOnboarding = useOnboardingStore((s) => s.forceOnboarding) - if (!completed || forceOnboarding) return + if (!completed) return return <>{children} } @@ -48,7 +48,6 @@ const router = createHashRouter([ export default function App() { useEffect(() => { - useOnboardingStore.getState().initForceFlag() // Prefetch global data at app startup useProviderStore.getState().fetch() useChannelsStore.getState().fetch() @@ -56,7 +55,9 @@ export default function App() { return ( - + + + ) } diff --git a/apps/desktop/src/renderer/src/pages/layout.tsx b/apps/desktop/src/renderer/src/pages/layout.tsx index e1d9cb1d..9d0f0c31 100644 --- a/apps/desktop/src/renderer/src/pages/layout.tsx +++ b/apps/desktop/src/renderer/src/pages/layout.tsx @@ -1,90 +1,114 @@ import { Outlet, NavLink, useLocation } from 'react-router-dom' import { Toaster } from '@multica/ui/components/ui/sonner' -import { Button } from '@multica/ui/components/ui/button' import { HugeiconsIcon } from '@hugeicons/react' import { - Settings02Icon, Home01Icon, + Comment01Icon, CodeIcon, PlugIcon, - Comment01Icon, Share08Icon, Time04Icon, } from '@hugeicons/core-free-icons' +import { + Sidebar, + SidebarContent, + SidebarGroup, + SidebarHeader, + SidebarInset, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + SidebarProvider, + SidebarTrigger, + useSidebar, +} from '@multica/ui/components/ui/sidebar' import { cn } from '@multica/ui/lib/utils' +import { ModeToggle } from '../components/mode-toggle' import { DeviceConfirmDialog } from '../components/device-confirm-dialog' -import ChatPage from './chat' -const tabs = [ - { path: '/', label: 'Home', icon: Home01Icon, exact: true }, +const navItems = [ + { path: '/', label: 'Home', icon: Home01Icon }, { path: '/chat', label: 'Chat', icon: Comment01Icon }, { path: '/tools', label: 'Tools', icon: CodeIcon }, { path: '/skills', label: 'Skills', icon: PlugIcon }, { path: '/channels', label: 'Channels', icon: Share08Icon }, - { path: '/crons', label: 'Cron', icon: Time04Icon }, + { path: '/crons', label: 'Crons', icon: Time04Icon }, ] +function MainHeader() { + const { state, isMobile } = useSidebar() + const needsTrafficLightSpace = state === 'collapsed' || isMobile + + return ( +
+ {/* Drag placeholder for traffic lights when sidebar is collapsed */} +
+ + + + {/* Spacer */} +
+ + {/* Right: Theme toggle */} + +
+ ) +} + export default function Layout() { const location = useLocation() return ( -
- {/* Header with drag region for macOS */} -
-
- Multica -
-
- -
-
+
+ + + {/* Traffic light area */} + - {/* Tabs */} - + + - {/* Content */} -
- {/* ChatPage is always mounted (cached), hidden via CSS */} -
- -
+ {/* Main Content */} +
+ +
+ - {/* Other routes render normally via Outlet */} - {location.pathname !== '/chat' && ( -
- -
- )} -
- - + + +
) } diff --git a/apps/desktop/src/renderer/src/stores/onboarding.ts b/apps/desktop/src/renderer/src/stores/onboarding.ts index d627e7fc..3b9cbb17 100644 --- a/apps/desktop/src/renderer/src/stores/onboarding.ts +++ b/apps/desktop/src/renderer/src/stores/onboarding.ts @@ -10,7 +10,6 @@ interface AcknowledgementsState { interface OnboardingStore { completed: boolean - forceOnboarding: boolean currentStep: number acknowledgements: AcknowledgementsState allAcknowledged: boolean @@ -23,14 +22,12 @@ interface OnboardingStore { setProviderConfigured: (configured: boolean) => void setClientConnected: (connected: boolean) => void completeOnboarding: () => void - initForceFlag: () => Promise } export const useOnboardingStore = create()( persist( (set, get) => ({ completed: false, - forceOnboarding: false, currentStep: 0, acknowledgements: { @@ -57,14 +54,7 @@ export const useOnboardingStore = create()( setClientConnected: (connected) => set({ clientConnected: connected }), - completeOnboarding: () => set({ completed: true, forceOnboarding: false, currentStep: 0 }), - - initForceFlag: async () => { - const flags = await window.electronAPI.app.getFlags() - if (flags.forceOnboarding) { - set({ forceOnboarding: true }) - } - }, + completeOnboarding: () => set({ completed: true, currentStep: 0 }), }), { name: 'multica-onboarding',