|
1 | | -import { useEffect } from 'react'; |
| 1 | +import { useCallback, useEffect, useRef, useState } from 'react'; |
2 | 2 | import { useLocation, useNavigate, useParams } from 'react-router-dom'; |
3 | 3 | import { useTranslation } from 'react-i18next'; |
4 | 4 |
|
@@ -108,11 +108,60 @@ export default function AppContent() { |
108 | 108 | }; |
109 | 109 | }, [isConnected, sendMessage]); |
110 | 110 |
|
| 111 | + const SIDEBAR_MIN = 220; |
| 112 | + const SIDEBAR_MAX = 480; |
| 113 | + const SIDEBAR_DEFAULT = 288; // w-72 |
| 114 | + const STORAGE_KEY = 'vibelab-sidebar-width'; |
| 115 | + |
| 116 | + const [sidebarWidth, setSidebarWidth] = useState(() => { |
| 117 | + const saved = localStorage.getItem(STORAGE_KEY); |
| 118 | + const parsed = saved ? Number(saved) : NaN; |
| 119 | + return Number.isFinite(parsed) && parsed >= SIDEBAR_MIN && parsed <= SIDEBAR_MAX |
| 120 | + ? parsed |
| 121 | + : SIDEBAR_DEFAULT; |
| 122 | + }); |
| 123 | + |
| 124 | + const isResizing = useRef(false); |
| 125 | + |
| 126 | + const handleResizeStart = useCallback((e: React.MouseEvent) => { |
| 127 | + e.preventDefault(); |
| 128 | + isResizing.current = true; |
| 129 | + document.body.style.cursor = 'col-resize'; |
| 130 | + document.body.style.userSelect = 'none'; |
| 131 | + |
| 132 | + const onMouseMove = (ev: MouseEvent) => { |
| 133 | + if (!isResizing.current) return; |
| 134 | + const newWidth = Math.min(SIDEBAR_MAX, Math.max(SIDEBAR_MIN, ev.clientX)); |
| 135 | + setSidebarWidth(newWidth); |
| 136 | + }; |
| 137 | + |
| 138 | + const onMouseUp = () => { |
| 139 | + isResizing.current = false; |
| 140 | + document.body.style.cursor = ''; |
| 141 | + document.body.style.userSelect = ''; |
| 142 | + document.removeEventListener('mousemove', onMouseMove); |
| 143 | + document.removeEventListener('mouseup', onMouseUp); |
| 144 | + setSidebarWidth((w) => { |
| 145 | + localStorage.setItem(STORAGE_KEY, String(w)); |
| 146 | + return w; |
| 147 | + }); |
| 148 | + }; |
| 149 | + |
| 150 | + document.addEventListener('mousemove', onMouseMove); |
| 151 | + document.addEventListener('mouseup', onMouseUp); |
| 152 | + }, []); |
| 153 | + |
111 | 154 | return ( |
112 | 155 | <div className="fixed inset-0 flex bg-background"> |
113 | 156 | {!isMobile ? ( |
114 | | - <div className="h-full flex-shrink-0 border-r border-border/50"> |
115 | | - <Sidebar {...sidebarSharedProps} /> |
| 157 | + <div className="h-full flex-shrink-0 relative" style={{ width: sidebarWidth }}> |
| 158 | + <div className="h-full border-r border-border/50"> |
| 159 | + <Sidebar {...sidebarSharedProps} /> |
| 160 | + </div> |
| 161 | + <div |
| 162 | + className="absolute top-0 right-0 w-1 h-full cursor-col-resize hover:bg-primary/20 active:bg-primary/30 z-10" |
| 163 | + onMouseDown={handleResizeStart} |
| 164 | + /> |
116 | 165 | </div> |
117 | 166 | ) : ( |
118 | 167 | <div |
|
0 commit comments