From 186dbcde63e5c9e73f2c2ba103105c487fdd8ac4 Mon Sep 17 00:00:00 2001 From: Haileyesus Date: Fri, 27 Mar 2026 14:53:10 +0300 Subject: [PATCH] refactor: make sidebar a global component --- src/App.tsx | 88 +++++++++--- .../refactored/shared/RootLayout.tsx | 15 ++ .../refactored/sidebar/types/index.ts | 1 + .../refactored/sidebar/view/Sidebar.tsx | 60 ++++++++ .../refactored/sidebar/view/SidebarHeader.tsx | 133 ++++++++++++++++++ .../refactored/sidebar/view/SidebarSearch.tsx | 70 +++++++++ 6 files changed, 345 insertions(+), 22 deletions(-) create mode 100644 src/components/refactored/shared/RootLayout.tsx create mode 100644 src/components/refactored/sidebar/types/index.ts create mode 100644 src/components/refactored/sidebar/view/Sidebar.tsx create mode 100644 src/components/refactored/sidebar/view/SidebarHeader.tsx create mode 100644 src/components/refactored/sidebar/view/SidebarSearch.tsx diff --git a/src/App.tsx b/src/App.tsx index bcbda826..2aeec6f4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,37 +1,81 @@ -import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; +import { RouterProvider, createBrowserRouter } from 'react-router-dom'; import { I18nextProvider } from 'react-i18next'; import { ThemeProvider } from './contexts/ThemeContext'; import { AuthProvider, ProtectedRoute } from './components/auth'; -import { TaskMasterProvider } from './contexts/TaskMasterContext'; -import { TasksSettingsProvider } from './contexts/TasksSettingsContext'; -import { WebSocketProvider } from './contexts/WebSocketContext'; -import { PluginsProvider } from './contexts/PluginsContext'; -import AppContent from './components/app/AppContent'; import i18n from './i18n/config.js'; +import { RootLayout } from '@/components/refactored/shared/RootLayout'; + +// Mock page components +const Home = () =>

Home Page

Select a session or create a new project.

; +const SessionContent = () =>

Session View

Chat interface goes here.

; + +const router = createBrowserRouter([ + { + path: "/", + element: , // The layout wraps all children + children: [ + { + path: "/", + element: , + }, + { + path: "/sessions/:sessionId", + element: , + }, + ], + }, +]); export default function App() { return ( - - - - - - - - } /> - } /> - - - - - - - + + + ); } + + +// import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; +// import { I18nextProvider } from 'react-i18next'; +// import { ThemeProvider } from './contexts/ThemeContext'; +// import { AuthProvider, ProtectedRoute } from './components/auth'; +// import { TaskMasterProvider } from './contexts/TaskMasterContext'; +// import { TasksSettingsProvider } from './contexts/TasksSettingsContext'; +// import { WebSocketProvider } from './contexts/WebSocketContext'; +// import { PluginsProvider } from './contexts/PluginsContext'; +// import AppContent from './components/app/AppContent'; +// import i18n from './i18n/config.js'; + +// export default function App() { +// return ( +// +// +// +// +// 1 +// +// +// +// +// +// } /> +// } /> +// } /> +// +// +// +// +// +// +// +// +// +// +// ); +// } diff --git a/src/components/refactored/shared/RootLayout.tsx b/src/components/refactored/shared/RootLayout.tsx new file mode 100644 index 00000000..374178d5 --- /dev/null +++ b/src/components/refactored/shared/RootLayout.tsx @@ -0,0 +1,15 @@ + +import { Outlet } from 'react-router-dom'; +import { Sidebar } from '@/components/refactored/sidebar/view/Sidebar'; + + +export function RootLayout() { + return ( +
+ +
+ +
+
+ ); +} diff --git a/src/components/refactored/sidebar/types/index.ts b/src/components/refactored/sidebar/types/index.ts new file mode 100644 index 00000000..b02dac8c --- /dev/null +++ b/src/components/refactored/sidebar/types/index.ts @@ -0,0 +1 @@ +export type SearchMode = 'projects' | 'conversations'; \ No newline at end of file diff --git a/src/components/refactored/sidebar/view/Sidebar.tsx b/src/components/refactored/sidebar/view/Sidebar.tsx new file mode 100644 index 00000000..74e197d7 --- /dev/null +++ b/src/components/refactored/sidebar/view/Sidebar.tsx @@ -0,0 +1,60 @@ +import { useState } from 'react'; +import { PanelRightOpen } from 'lucide-react'; +import { Button } from '@/shared/view/ui'; +import { cn } from '@/lib/utils'; +import SidebarHeader from '@/components/refactored/sidebar/view/SidebarHeader.js'; + + +export function Sidebar() { + const [isCollapsed, setIsCollapsed] = useState(false); + + return ( + <> + {/* Mobile Backdrop Overlay - allows tapping outside to close */} + {!isCollapsed && ( +
setIsCollapsed(true)} + /> + )} + + + + {/* Collapsed view handle - Only show on desktop since mobile hides it completely behind a toggle usually, but let's keep it consistent or standard. + Actually, on mobile, if it's completely hidden, we need a way to open it from the main content. For now we show the small bar if it's flex, + but since we made it fixed, let's keep the small bar fixed too. */} + {isCollapsed && ( + + )} + + ); +} diff --git a/src/components/refactored/sidebar/view/SidebarHeader.tsx b/src/components/refactored/sidebar/view/SidebarHeader.tsx new file mode 100644 index 00000000..99f65fef --- /dev/null +++ b/src/components/refactored/sidebar/view/SidebarHeader.tsx @@ -0,0 +1,133 @@ +import { useState } from 'react'; +import { FolderPlus, Plus, RefreshCw, PanelLeftClose } from 'lucide-react'; +import type { SearchMode } from '../types'; +import { SidebarSearch } from './SidebarSearch'; +import { Button } from '@/shared/view/ui'; +import { cn } from '@/lib/utils'; +import { IS_PLATFORM } from '@/constants/config'; + +type SidebarHeaderProps = { + isCollapsed: boolean; + onToggleCollapse: () => void; +}; + +export default function SidebarHeader({ isCollapsed, onToggleCollapse }: SidebarHeaderProps) { + // UI States declared here to avoid prop drilling as per instructions + const [searchMode, setSearchMode] = useState('projects'); + const [searchFilter, setSearchFilter] = useState(''); + const [isRefreshing, setIsRefreshing] = useState(false); + + const handleRefresh = () => { + setIsRefreshing(true); + setTimeout(() => setIsRefreshing(false), 1000); + }; + + const LogoBlock = () => ( +
+
+ + + +
+

Claude Code UI

+
+ ); + + const LogoWithLink = () => { + if (IS_PLATFORM) { + return ( + + + + ); + } + return ; + }; + + if (isCollapsed) return null; + + return ( +
+ {/* Desktop header */} +
+
+ +
+ + + +
+
+ + +
+ + {/* Desktop divider */} +
+ + {/* Mobile header */} +
+
+ +
+ + +
+
+ + +
+ + {/* Mobile divider */} +
+
+ ); +} diff --git a/src/components/refactored/sidebar/view/SidebarSearch.tsx b/src/components/refactored/sidebar/view/SidebarSearch.tsx new file mode 100644 index 00000000..aac36d5b --- /dev/null +++ b/src/components/refactored/sidebar/view/SidebarSearch.tsx @@ -0,0 +1,70 @@ +import { Folder, MessageSquare, Search, X } from 'lucide-react'; +import { Input } from '@/shared/view/ui'; +import { cn } from '@/lib/utils'; +import { SearchMode } from '@/components/refactored/sidebar/types/index.js'; + + +type SidebarSearchProps = { + searchMode: SearchMode; + onSearchModeChange: (mode: SearchMode) => void; + searchFilter: string; + onSearchFilterChange: (filter: string) => void; +}; + +export function SidebarSearch({ + searchMode, + onSearchModeChange, + searchFilter, + onSearchFilterChange +}: SidebarSearchProps) { + return ( +
+
+ + +
+ + {/* Search bar */} +
+ + onSearchFilterChange(event.target.value)} + className="nav-search-input h-10 rounded-xl border-0 pl-10 pr-9 text-sm transition-all duration-200 placeholder:text-muted-foreground/40 focus-visible:ring-0 focus-visible:ring-offset-0 md:h-9 md:pl-9 md:pr-8" + /> + {searchFilter && ( + + )} +
+
+ ); +}