1- import { Box } from "@radix-ui/themes" ;
1+ import { ArrowsInSimpleIcon , ArrowsOutSimpleIcon } from "@phosphor-icons/react" ;
2+ import { Box , Flex , IconButton , Tooltip } from "@radix-ui/themes" ;
23import type { Task } from "@shared/types" ;
34import type React from "react" ;
45import { useEffect , useMemo , useState } from "react" ;
@@ -10,12 +11,17 @@ import { useSidebarStore } from "../../../stores/sidebarStore";
1011import { useTabStore } from "../../../stores/tabStore" ;
1112import { SidebarTreeItem } from "./SidebarTreeItem" ;
1213import { useSidebarMenuData } from "./UseSidebarMenuData" ;
13- import { buildTreeLines } from "./Utils" ;
14+ import { buildTreeLines , getAllNodeIds } from "./Utils" ;
1415
1516export const SidebarContent : React . FC = ( ) => {
1617 const { client } = useAuthStore ( ) ;
1718 const { tabs, createTab, setActiveTab, activeTabId } = useTabStore ( ) ;
18- const { expandedNodes : expandedNodesArray , toggleNode } = useSidebarStore ( ) ;
19+ const {
20+ expandedNodes : expandedNodesArray ,
21+ toggleNode,
22+ expandAll,
23+ collapseAll,
24+ } = useSidebarStore ( ) ;
1925 const { setCliMode } = useLayoutStore ( ) ;
2026 const { saveRecording } = useRecordings ( ) ;
2127 const { startRecording, stopRecording } = useAudioRecorder ( saveRecording ) ;
@@ -101,6 +107,20 @@ export const SidebarContent: React.FC = () => {
101107 } ) ;
102108
103109 const treeLines = buildTreeLines ( [ menuData ] , "" , "" , expandedNodes , 0 ) ;
110+ const allNodeIds = useMemo (
111+ ( ) => getAllNodeIds ( [ menuData ] , "" , 0 ) ,
112+ [ menuData ] ,
113+ ) ;
114+ const allExpanded =
115+ allNodeIds . length > 0 && allNodeIds . every ( ( id ) => expandedNodes . has ( id ) ) ;
116+
117+ const handleToggleExpandAll = ( ) => {
118+ if ( allExpanded ) {
119+ collapseAll ( ) ;
120+ } else {
121+ expandAll ( allNodeIds ) ;
122+ }
123+ } ;
104124
105125 return (
106126 < Box
@@ -111,18 +131,45 @@ export const SidebarContent: React.FC = () => {
111131 >
112132 < Box p = "2" >
113133 < div className = "sidebar-tree" >
114- { treeLines . map ( ( line , index ) => (
115- < SidebarTreeItem
116- key = { line . nodeId }
117- line = { line }
118- index = { index }
119- isHovered = { hoveredLineIndex === index }
120- expandedNodes = { expandedNodes }
121- onMouseEnter = { ( ) => setHoveredLineIndex ( index ) }
122- onMouseLeave = { ( ) => setHoveredLineIndex ( null ) }
123- onToggle = { toggleNode }
124- />
125- ) ) }
134+ { treeLines . map ( ( line , index ) => {
135+ const isRoot = index === 0 ;
136+ if ( isRoot ) {
137+ return (
138+ < Flex key = { line . nodeId } align = "center" justify = "between" mb = "1" >
139+ < span style = { { fontWeight : 500 } } > { line . label } </ span >
140+ < Tooltip
141+ content = { allExpanded ? "Collapse all" : "Expand all" }
142+ >
143+ < IconButton
144+ size = "1"
145+ variant = "ghost"
146+ color = "gray"
147+ onClick = { handleToggleExpandAll }
148+ style = { { cursor : "pointer" } }
149+ >
150+ { allExpanded ? (
151+ < ArrowsInSimpleIcon size = { 12 } />
152+ ) : (
153+ < ArrowsOutSimpleIcon size = { 12 } />
154+ ) }
155+ </ IconButton >
156+ </ Tooltip >
157+ </ Flex >
158+ ) ;
159+ }
160+ return (
161+ < SidebarTreeItem
162+ key = { line . nodeId }
163+ line = { line }
164+ index = { index }
165+ isHovered = { hoveredLineIndex === index }
166+ expandedNodes = { expandedNodes }
167+ onMouseEnter = { ( ) => setHoveredLineIndex ( index ) }
168+ onMouseLeave = { ( ) => setHoveredLineIndex ( null ) }
169+ onToggle = { toggleNode }
170+ />
171+ ) ;
172+ } ) }
126173 </ div >
127174 </ Box >
128175 </ Box >
0 commit comments