@@ -7,8 +7,21 @@ import {
77 SearchIcon ,
88 SquarePenIcon ,
99} from "lucide-react" ;
10- import { type MouseEvent , type PointerEvent , useCallback , useRef } from "react" ;
10+ import {
11+ type CSSProperties ,
12+ type MouseEvent ,
13+ type PointerEvent ,
14+ useCallback ,
15+ useMemo ,
16+ useRef ,
17+ useState ,
18+ } from "react" ;
1119
20+ import {
21+ ResizableHandle ,
22+ ResizablePanel ,
23+ ResizablePanelGroup ,
24+ } from "@hypr/ui/components/ui/resizable" ;
1225import { cn } from "@hypr/utils" ;
1326
1427import { resolveMainSurfaceChrome } from "./main-surface-chrome" ;
@@ -34,6 +47,11 @@ import { type Tab, uniqueIdfromTab, useTabs } from "~/store/zustand/tabs";
3447
3548const MAIN_AREA_TOP_DRAG_HEIGHT_PX = 48 ;
3649const MAIN_AREA_WINDOW_DRAG_THRESHOLD_PX = 5 ;
50+ const LEFT_SIDEBAR_DEFAULT_SIZE = 18 ;
51+ const LEFT_SIDEBAR_MIN_SIZE = 12 ;
52+ const LEFT_SIDEBAR_MAX_SIZE = 32 ;
53+ const LEFT_SIDEBAR_MIN_WIDTH_PX = 180 ;
54+ const LEFT_SIDEBAR_MAX_WIDTH_PX = 360 ;
3755
3856type MainAreaWindowDragStart = {
3957 pointerId : number ;
@@ -46,6 +64,9 @@ export function ClassicMainBody() {
4664 const { leftsidebar } = useShell ( ) ;
4765 const currentTab = useTabs ( ( state ) => state . currentTab ) ;
4866 const { runEscapeShortcut } = useClassicMainTabsShortcuts ( ) ;
67+ const [ leftSidebarPanelSize , setLeftSidebarPanelSize ] = useState (
68+ LEFT_SIDEBAR_DEFAULT_SIZE ,
69+ ) ;
4970
5071 const isOnboarding = currentTab ?. type === "onboarding" ;
5172 const isChangelog = currentTab ?. type === "changelog" ;
@@ -54,6 +75,7 @@ export function ClassicMainBody() {
5475 hasLeftSurfaceCustomSidebarTab ( currentTab ) ;
5576 const showSidebarTimelineChrome = ! hasCustomSidebar && ! isOnboarding ;
5677 const showSidebarTimeline = showSidebarTimelineChrome && leftsidebar . expanded ;
78+ const showLeftSidebarPanel = leftsidebar . expanded && ! isOnboarding ;
5779 const showLeftSurfaceChromeBack = hasLeftSurfaceCustomSidebar ;
5880 const enableMainAreaTopDrag =
5981 showSidebarTimelineChrome || hasLeftSurfaceCustomSidebar ;
@@ -76,14 +98,38 @@ export function ClassicMainBody() {
7698 const handleOpenNoteDialog = useCallback ( ( ) => {
7799 openNoteDialog . open ( ) ;
78100 } , [ openNoteDialog ] ) ;
101+ const handlePanelLayout = useCallback (
102+ ( sizes : number [ ] ) => {
103+ if ( ! showLeftSidebarPanel ) {
104+ return ;
105+ }
106+
107+ const sidebarSize = sizes [ 0 ] ;
108+ if ( typeof sidebarSize === "number" ) {
109+ setLeftSidebarPanelSize ( sidebarSize ) ;
110+ }
111+ } ,
112+ [ showLeftSidebarPanel ] ,
113+ ) ;
114+ const leftSidebarChromeStyle = useMemo (
115+ ( ) =>
116+ ( {
117+ width : `${ leftSidebarPanelSize } %` ,
118+ minWidth : LEFT_SIDEBAR_MIN_WIDTH_PX ,
119+ maxWidth : LEFT_SIDEBAR_MAX_WIDTH_PX ,
120+ } ) satisfies CSSProperties ,
121+ [ leftSidebarPanelSize ] ,
122+ ) ;
79123
80124 return (
81125 < div className = "relative flex h-full min-w-0 flex-1 flex-col" >
82126 { isOnboarding ? null : showSidebarTimelineChrome ? (
83127 < div
84128 data-tauri-drag-region
129+ data-left-sidebar-chrome
130+ style = { leftSidebarChromeStyle }
85131 className = { cn ( [
86- "absolute top-0 z-40 h-12 w-[200px] " ,
132+ "absolute top-0 z-40 h-12" ,
87133 leftsidebar . expanded ? "left-0" : "left-1" ,
88134 ! leftsidebar . expanded && "pointer-events-none" ,
89135 ] ) }
@@ -105,7 +151,9 @@ export function ClassicMainBody() {
105151 ) : hasLeftSurfaceCustomSidebar ? (
106152 < div
107153 data-tauri-drag-region
108- className = "absolute top-0 left-0 z-40 h-10 w-[200px]"
154+ data-left-sidebar-chrome
155+ style = { leftSidebarChromeStyle }
156+ className = "absolute top-0 left-0 z-40 h-10"
109157 />
110158 ) : (
111159 < div data-tauri-drag-region className = "relative h-10 shrink-0" >
@@ -118,7 +166,9 @@ export function ClassicMainBody() {
118166 { showLeftSurfaceChromeBack ? (
119167 < div
120168 data-tauri-drag-region
121- className = "absolute top-0 left-0 z-50 h-12 w-[200px]"
169+ data-left-sidebar-chrome
170+ style = { leftSidebarChromeStyle }
171+ className = "absolute top-0 left-0 z-50 h-12"
122172 >
123173 < div
124174 data-tauri-drag-region
@@ -133,29 +183,54 @@ export function ClassicMainBody() {
133183 </ div >
134184 </ div >
135185 ) : null }
136- < div className = "flex min-h-0 min-w-0 flex-1 gap-1" >
137- < ClassicMainSidebar />
138- < div
139- className = "min-h-0 min-w-0 flex-1 overflow-auto"
140- onClickCapture = { mainAreaTopDrag . onClickCapture }
141- onPointerCancel = { mainAreaTopDrag . onPointerEnd }
142- onPointerDown = { mainAreaTopDrag . onPointerDown }
143- onPointerMove = { mainAreaTopDrag . onPointerMove }
144- onPointerUp = { mainAreaTopDrag . onPointerEnd }
145- >
146- < GlobalLiveTranscriptAccessory
147- currentTab = { currentTab }
148- surfaceChrome = { mainSurfaceChrome }
186+ < ResizablePanelGroup
187+ autoSaveId = { showLeftSidebarPanel ? "classic-main-sidebar" : undefined }
188+ direction = "horizontal"
189+ className = "min-h-0 flex-1 overflow-hidden"
190+ onLayout = { handlePanelLayout }
191+ >
192+ { showLeftSidebarPanel ? (
193+ < >
194+ < ResizablePanel
195+ defaultSize = { LEFT_SIDEBAR_DEFAULT_SIZE }
196+ minSize = { LEFT_SIDEBAR_MIN_SIZE }
197+ maxSize = { LEFT_SIDEBAR_MAX_SIZE }
198+ className = "min-h-0 overflow-hidden"
199+ style = { {
200+ minWidth : LEFT_SIDEBAR_MIN_WIDTH_PX ,
201+ maxWidth : LEFT_SIDEBAR_MAX_WIDTH_PX ,
202+ } }
203+ >
204+ < ClassicMainSidebar />
205+ </ ResizablePanel >
206+ < ResizableHandle className = "z-10 w-1 !bg-transparent after:w-2" />
207+ </ >
208+ ) : (
209+ < ClassicMainSidebar />
210+ ) }
211+ < ResizablePanel className = "min-h-0 flex-1 overflow-hidden" >
212+ < div
213+ className = "h-full min-h-0 min-w-0 flex-1 overflow-auto"
214+ onClickCapture = { mainAreaTopDrag . onClickCapture }
215+ onPointerCancel = { mainAreaTopDrag . onPointerEnd }
216+ onPointerDown = { mainAreaTopDrag . onPointerDown }
217+ onPointerMove = { mainAreaTopDrag . onPointerMove }
218+ onPointerUp = { mainAreaTopDrag . onPointerEnd }
149219 >
150- { currentTab ? (
151- < ClassicMainTabContent
152- key = { uniqueIdfromTab ( currentTab ) }
153- tab = { currentTab as Tab }
154- />
155- ) : null }
156- </ GlobalLiveTranscriptAccessory >
157- </ div >
158- </ div >
220+ < GlobalLiveTranscriptAccessory
221+ currentTab = { currentTab }
222+ surfaceChrome = { mainSurfaceChrome }
223+ >
224+ { currentTab ? (
225+ < ClassicMainTabContent
226+ key = { uniqueIdfromTab ( currentTab ) }
227+ tab = { currentTab as Tab }
228+ />
229+ ) : null }
230+ </ GlobalLiveTranscriptAccessory >
231+ </ div >
232+ </ ResizablePanel >
233+ </ ResizablePanelGroup >
159234 </ div >
160235 ) ;
161236}
0 commit comments