diff --git a/apps/x/apps/renderer/src/App.tsx b/apps/x/apps/renderer/src/App.tsx index 9bf73d36..2b879495 100644 --- a/apps/x/apps/renderer/src/App.tsx +++ b/apps/x/apps/renderer/src/App.tsx @@ -6,7 +6,7 @@ import type { LanguageModelUsage, ToolUIPart } from 'ai'; import './App.css' import z from 'zod'; import { Button } from './components/ui/button'; -import { CheckIcon, LoaderIcon, ArrowUp, PanelLeftIcon, PanelRightIcon, Square, X, ChevronLeftIcon, ChevronRightIcon, SquarePen } from 'lucide-react'; +import { CheckIcon, LoaderIcon, ArrowUp, PanelLeftIcon, PanelRightIcon, Search, Square, X, ChevronLeftIcon, ChevronRightIcon, SquarePen } from 'lucide-react'; import { cn } from '@/lib/utils'; import { MarkdownEditor } from './components/markdown-editor'; import { ChatInputBar } from './components/chat-button'; @@ -14,7 +14,7 @@ import { ChatSidebar } from './components/chat-sidebar'; import { GraphView, type GraphEdge, type GraphNode } from '@/components/graph-view'; import { useDebounce } from './hooks/use-debounce'; import { SidebarContentPanel } from '@/components/sidebar-content'; -import { SidebarSectionProvider, type ActiveSection } from '@/contexts/sidebar-context'; +import { SidebarSectionProvider, useSidebarSection, type ActiveSection } from '@/contexts/sidebar-context'; import { Conversation, ConversationContent, @@ -131,8 +131,8 @@ const TITLEBAR_BUTTON_PX = 32 const TITLEBAR_BUTTON_GAP_PX = 4 const TITLEBAR_HEADER_GAP_PX = 8 const TITLEBAR_TOGGLE_MARGIN_LEFT_PX = 12 -const TITLEBAR_BUTTONS_COLLAPSED = 4 -const TITLEBAR_BUTTON_GAPS_COLLAPSED = 3 +const TITLEBAR_BUTTONS_COLLAPSED = 5 +const TITLEBAR_BUTTON_GAPS_COLLAPSED = 4 const clampNumber = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value)) @@ -487,6 +487,7 @@ function FixedSidebarToggle({ leftInsetPx: number }) { const { toggleSidebar, state } = useSidebar() + const { searchOpen, setSearchOpen } = useSidebarSection() const isCollapsed = state === "collapsed" return (
@@ -510,6 +511,20 @@ function FixedSidebarToggle({ > + {/* Back / Forward navigation */} {isCollapsed && ( <> diff --git a/apps/x/apps/renderer/src/components/sidebar-content.tsx b/apps/x/apps/renderer/src/components/sidebar-content.tsx index 7e99049b..a2e485e3 100644 --- a/apps/x/apps/renderer/src/components/sidebar-content.tsx +++ b/apps/x/apps/renderer/src/components/sidebar-content.tsx @@ -401,8 +401,7 @@ export function SidebarContentPanel({ selectedBackgroundTask, ...props }: SidebarContentPanelProps) { - const { activeSection, setActiveSection } = useSidebarSection() - const [searchOpen, setSearchOpen] = useState(false) + const { activeSection, setActiveSection, searchOpen, setSearchOpen } = useSidebarSection() const [searchQuery, setSearchQuery] = useState("") const searchInputRef = useRef(null) @@ -437,7 +436,7 @@ export function SidebarContentPanel({ {/* Top spacer to clear the traffic lights + fixed toggle row */}
- {/* Tab switcher - centered below the traffic lights row */} + {/* Tab switcher */}
{sectionTabs.map((tab) => ( @@ -456,6 +455,29 @@ export function SidebarContentPanel({ ))}
+ {searchOpen && ( +
+ + setSearchQuery(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Escape") setSearchOpen(false) + }} + placeholder={activeSection === "tasks" ? "Search chats..." : "Search files..."} + className="flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground/60" + /> + {searchQuery && ( + + )} +
+ )} {activeSection === "knowledge" && ( @@ -466,11 +488,6 @@ export function SidebarContentPanel({ onSelectFile={onSelectFile} actions={knowledgeActions} onVoiceNoteCreated={onVoiceNoteCreated} - searchOpen={searchOpen} - setSearchOpen={setSearchOpen} - searchQuery={searchQuery} - setSearchQuery={setSearchQuery} - searchInputRef={searchInputRef} /> )} {activeSection === "tasks" && ( @@ -481,11 +498,6 @@ export function SidebarContentPanel({ actions={tasksActions} backgroundTasks={filteredBackgroundTasks} selectedBackgroundTask={selectedBackgroundTask} - searchOpen={searchOpen} - setSearchOpen={setSearchOpen} - searchQuery={searchQuery} - setSearchQuery={setSearchQuery} - searchInputRef={searchInputRef} /> )} @@ -762,11 +774,6 @@ function KnowledgeSection({ onSelectFile, actions, onVoiceNoteCreated, - searchOpen, - setSearchOpen, - searchQuery, - setSearchQuery, - searchInputRef, }: { tree: TreeNode[] selectedPath: string | null @@ -774,11 +781,6 @@ function KnowledgeSection({ onSelectFile: (path: string, kind: "file" | "dir") => void actions: KnowledgeActions onVoiceNoteCreated?: (path: string) => void - searchOpen: boolean - setSearchOpen: (open: boolean) => void - searchQuery: string - setSearchQuery: (query: string) => void - searchInputRef: React.RefObject }) { const isExpanded = expandedPaths.size > 0 @@ -792,79 +794,38 @@ function KnowledgeSection({ -
-
- {quickActions.map((action) => ( - - - - - {action.label} - - ))} - - +
+ {quickActions.map((action) => ( + - - {isExpanded ? "Collapse All" : "Expand All"} - + {action.label} - - - - - Search - -
- {searchOpen && ( -
- - setSearchQuery(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Escape") setSearchOpen(false) - }} - placeholder="Search files..." - className="flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground/60" - /> - {searchQuery && ( - - )} -
- )} + ))} + + + + + + + {isExpanded ? "Collapse All" : "Expand All"} + +
@@ -1118,11 +1079,6 @@ function TasksSection({ actions, backgroundTasks = [], selectedBackgroundTask, - searchOpen, - setSearchOpen, - searchQuery, - setSearchQuery, - searchInputRef, }: { runs: RunListItem[] currentRunId?: string | null @@ -1130,65 +1086,19 @@ function TasksSection({ actions?: TasksActions backgroundTasks?: BackgroundTaskItem[] selectedBackgroundTask?: string | null - searchOpen: boolean - setSearchOpen: (open: boolean) => void - searchQuery: string - setSearchQuery: (query: string) => void - searchInputRef: React.RefObject }) { return ( - {/* Sticky New Chat button + search */} + {/* Sticky New Chat button - matches Knowledge section height */}
-
- - - - - New chat - - - - - - - - Search - -
- {searchOpen && ( -
- - setSearchQuery(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Escape") setSearchOpen(false) - }} - placeholder="Search chats..." - className="flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground/60" - /> - {searchQuery && ( - - )} -
- )} + + + + + New chat + + +
{/* Background Tasks Section */} diff --git a/apps/x/apps/renderer/src/contexts/sidebar-context.tsx b/apps/x/apps/renderer/src/contexts/sidebar-context.tsx index 61e4bb65..dc3f8984 100644 --- a/apps/x/apps/renderer/src/contexts/sidebar-context.tsx +++ b/apps/x/apps/renderer/src/contexts/sidebar-context.tsx @@ -7,6 +7,8 @@ export type ActiveSection = "knowledge" | "tasks" type SidebarSectionContextProps = { activeSection: ActiveSection setActiveSection: (section: ActiveSection) => void + searchOpen: boolean + setSearchOpen: (open: boolean) => void } const SidebarSectionContext = React.createContext(null) @@ -29,6 +31,7 @@ export function SidebarSectionProvider({ children: React.ReactNode }) { const [activeSection, setActiveSectionState] = React.useState(defaultSection) + const [searchOpen, setSearchOpen] = React.useState(false) const setActiveSection = React.useCallback((section: ActiveSection) => { setActiveSectionState(section) @@ -39,8 +42,10 @@ export function SidebarSectionProvider({ () => ({ activeSection, setActiveSection, + searchOpen, + setSearchOpen, }), - [activeSection, setActiveSection] + [activeSection, setActiveSection, searchOpen] ) return (