akhaliq HF Staff commited on
Commit
41493fc
·
1 Parent(s): 9acce26

update sidebar resizing

Browse files
frontend/src/app/globals.css CHANGED
@@ -126,3 +126,67 @@ select:focus-visible {
126
  animation: fade-in 0.3s ease-in-out;
127
  }
128
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  animation: fade-in 0.3s ease-in-out;
127
  }
128
 
129
+ /* Resize handle styles */
130
+ .resize-handle {
131
+ position: relative;
132
+ width: 4px;
133
+ cursor: col-resize;
134
+ user-select: none;
135
+ background: transparent;
136
+ transition: background-color 0.2s ease, width 0.15s ease;
137
+ flex-shrink: 0;
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: center;
141
+ }
142
+
143
+ /* Clickable area (extends beyond visible width) */
144
+ .resize-handle::before {
145
+ content: '';
146
+ position: absolute;
147
+ inset: 0;
148
+ left: -4px;
149
+ right: -4px;
150
+ }
151
+
152
+ /* Visual indicator (three dots) */
153
+ .resize-handle::after {
154
+ content: '⋮';
155
+ position: relative;
156
+ z-index: 1;
157
+ font-size: 16px;
158
+ color: rgba(134, 134, 139, 0.4);
159
+ transition: color 0.2s ease;
160
+ pointer-events: none;
161
+ line-height: 1;
162
+ }
163
+
164
+ .resize-handle:hover {
165
+ background: rgba(0, 123, 255, 0.2);
166
+ }
167
+
168
+ .resize-handle:hover::after {
169
+ color: rgba(0, 123, 255, 0.8);
170
+ }
171
+
172
+ .resize-handle:active,
173
+ .resize-handle.resizing {
174
+ background: rgba(0, 123, 255, 0.4);
175
+ width: 4px;
176
+ }
177
+
178
+ .resize-handle:active::after,
179
+ .resize-handle.resizing::after {
180
+ color: rgba(0, 123, 255, 1);
181
+ }
182
+
183
+ /* Prevent text selection during resize */
184
+ body.resizing {
185
+ user-select: none !important;
186
+ cursor: col-resize !important;
187
+ }
188
+
189
+ body.resizing * {
190
+ cursor: col-resize !important;
191
+ }
192
+
frontend/src/app/page.tsx CHANGED
@@ -28,6 +28,13 @@ export default function Home() {
28
 
29
  // Mobile view state: 'chat', 'editor', or 'settings' - start on chat for mobile
30
  const [mobileView, setMobileView] = useState<'chat' | 'editor' | 'settings'>('chat');
 
 
 
 
 
 
 
31
 
32
  // Load messages from localStorage on mount (client-side only to avoid hydration issues)
33
  useEffect(() => {
@@ -46,6 +53,26 @@ export default function Home() {
46
  console.error('[localStorage] Failed to parse saved messages:', e);
47
  }
48
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  }
50
  }, []); // Empty deps = run once on mount
51
 
@@ -618,6 +645,63 @@ export default function Home() {
618
  await handleSendMessage(prompt, language, modelId);
619
  };
620
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
  // Show landing page if no messages and showLandingPage is true
622
  if (showLandingPage && messages.length === 0) {
623
  return (
@@ -641,13 +725,17 @@ export default function Home() {
641
  {/* Apple-style layout - Responsive */}
642
  <main className="flex-1 flex overflow-hidden relative">
643
  {/* Left Sidebar - Chat Panel (Hidden on mobile, shown when mobileView='chat') */}
644
- <div className={`
645
- ${mobileView === 'chat' ? 'flex' : 'hidden'} md:flex
646
- w-full md:w-80
647
- bg-[#000000] border-r border-[#424245]/30
648
- flex-col
649
- absolute md:relative inset-0 md:inset-auto z-10 md:z-auto
650
- `}>
 
 
 
 
651
  {/* Panel Header */}
652
  <div className="flex items-center px-4 py-3 bg-[#000000] border-b border-[#424245]/30">
653
  <span className="text-sm font-medium text-[#f5f5f7]">Chat</span>
@@ -664,11 +752,20 @@ export default function Home() {
664
  </div>
665
  </div>
666
 
 
 
 
 
 
 
 
667
  {/* Center - Editor Group (Always visible on mobile when mobileView='editor', always visible on desktop) */}
668
  <div className={`
669
  ${mobileView === 'editor' ? 'flex' : 'hidden'} md:flex
670
  flex-1 flex-col bg-[#000000]
671
  absolute md:relative inset-0 md:inset-auto z-10 md:z-auto
 
 
672
  `}>
673
  {/* Tab Bar */}
674
  <div className="flex items-center px-4 h-10 bg-[#1d1d1f] border-b border-[#424245]/30">
@@ -704,15 +801,26 @@ export default function Home() {
704
  </div>
705
  </div>
706
 
 
 
 
 
 
 
 
707
  {/* Right Sidebar - Configuration Panel (Hidden on mobile, shown when mobileView='settings') */}
708
- <div className={`
709
- ${mobileView === 'settings' ? 'flex' : 'hidden'} md:flex
710
- w-full md:w-72
711
- bg-[#000000] border-l border-[#424245]/30
712
- overflow-y-auto
713
- absolute md:relative inset-0 md:inset-auto z-10 md:z-auto
714
- flex-col
715
- `}>
 
 
 
 
716
  <ControlPanel
717
  selectedLanguage={selectedLanguage}
718
  selectedModel={selectedModel}
 
28
 
29
  // Mobile view state: 'chat', 'editor', or 'settings' - start on chat for mobile
30
  const [mobileView, setMobileView] = useState<'chat' | 'editor' | 'settings'>('chat');
31
+
32
+ // Resizable sidebar widths (in pixels)
33
+ const [chatSidebarWidth, setChatSidebarWidth] = useState(320);
34
+ const [settingsSidebarWidth, setSettingsSidebarWidth] = useState(288);
35
+ const [isResizingChat, setIsResizingChat] = useState(false);
36
+ const [isResizingSettings, setIsResizingSettings] = useState(false);
37
+ const [isDesktop, setIsDesktop] = useState(false);
38
 
39
  // Load messages from localStorage on mount (client-side only to avoid hydration issues)
40
  useEffect(() => {
 
53
  console.error('[localStorage] Failed to parse saved messages:', e);
54
  }
55
  }
56
+
57
+ // Load sidebar widths from localStorage
58
+ const savedChatWidth = localStorage.getItem('anycoder_chat_sidebar_width');
59
+ const savedSettingsWidth = localStorage.getItem('anycoder_settings_sidebar_width');
60
+ if (savedChatWidth) {
61
+ setChatSidebarWidth(parseInt(savedChatWidth, 10));
62
+ }
63
+ if (savedSettingsWidth) {
64
+ setSettingsSidebarWidth(parseInt(savedSettingsWidth, 10));
65
+ }
66
+
67
+ // Check if desktop on mount
68
+ const checkDesktop = () => {
69
+ setIsDesktop(window.innerWidth >= 768);
70
+ };
71
+ checkDesktop();
72
+
73
+ // Listen for window resize to update desktop status
74
+ window.addEventListener('resize', checkDesktop);
75
+ return () => window.removeEventListener('resize', checkDesktop);
76
  }
77
  }, []); // Empty deps = run once on mount
78
 
 
645
  await handleSendMessage(prompt, language, modelId);
646
  };
647
 
648
+ // Resize handlers for chat sidebar (desktop only)
649
+ const startResizingChat = () => {
650
+ if (isDesktop) {
651
+ setIsResizingChat(true);
652
+ }
653
+ };
654
+
655
+ const startResizingSettings = () => {
656
+ if (isDesktop) {
657
+ setIsResizingSettings(true);
658
+ }
659
+ };
660
+
661
+ // Handle mouse move for resizing (desktop only)
662
+ useEffect(() => {
663
+ const handleMouseMove = (e: MouseEvent) => {
664
+ if (!isDesktop) return; // Skip on mobile
665
+
666
+ if (isResizingChat) {
667
+ const newWidth = Math.min(Math.max(e.clientX, 250), 600); // Min 250px, max 600px
668
+ setChatSidebarWidth(newWidth);
669
+ }
670
+ if (isResizingSettings) {
671
+ const newWidth = Math.min(Math.max(window.innerWidth - e.clientX, 220), 500); // Min 220px, max 500px
672
+ setSettingsSidebarWidth(newWidth);
673
+ }
674
+ };
675
+
676
+ const handleMouseUp = () => {
677
+ if (isResizingChat) {
678
+ setIsResizingChat(false);
679
+ // Save to localStorage
680
+ localStorage.setItem('anycoder_chat_sidebar_width', chatSidebarWidth.toString());
681
+ document.body.classList.remove('resizing');
682
+ }
683
+ if (isResizingSettings) {
684
+ setIsResizingSettings(false);
685
+ // Save to localStorage
686
+ localStorage.setItem('anycoder_settings_sidebar_width', settingsSidebarWidth.toString());
687
+ document.body.classList.remove('resizing');
688
+ }
689
+ };
690
+
691
+ if (isResizingChat || isResizingSettings) {
692
+ document.addEventListener('mousemove', handleMouseMove);
693
+ document.addEventListener('mouseup', handleMouseUp);
694
+ // Add resizing class to body for cursor and selection styles
695
+ document.body.classList.add('resizing');
696
+ }
697
+
698
+ return () => {
699
+ document.removeEventListener('mousemove', handleMouseMove);
700
+ document.removeEventListener('mouseup', handleMouseUp);
701
+ document.body.classList.remove('resizing');
702
+ };
703
+ }, [isResizingChat, isResizingSettings, chatSidebarWidth, settingsSidebarWidth, isDesktop]);
704
+
705
  // Show landing page if no messages and showLandingPage is true
706
  if (showLandingPage && messages.length === 0) {
707
  return (
 
725
  {/* Apple-style layout - Responsive */}
726
  <main className="flex-1 flex overflow-hidden relative">
727
  {/* Left Sidebar - Chat Panel (Hidden on mobile, shown when mobileView='chat') */}
728
+ <div
729
+ className={`
730
+ ${mobileView === 'chat' ? 'flex' : 'hidden'} md:flex
731
+ w-full
732
+ bg-[#000000] border-r border-[#424245]/30
733
+ flex-col
734
+ absolute md:relative inset-0 md:inset-auto z-10 md:z-auto
735
+ md:flex-shrink-0
736
+ `}
737
+ style={isDesktop ? { width: `${chatSidebarWidth}px` } : undefined}
738
+ >
739
  {/* Panel Header */}
740
  <div className="flex items-center px-4 py-3 bg-[#000000] border-b border-[#424245]/30">
741
  <span className="text-sm font-medium text-[#f5f5f7]">Chat</span>
 
752
  </div>
753
  </div>
754
 
755
+ {/* Resize Handle for Chat Sidebar (Desktop only) */}
756
+ <div
757
+ className={`hidden md:block resize-handle ${isResizingChat ? 'resizing' : ''}`}
758
+ onMouseDown={startResizingChat}
759
+ title="Drag to resize chat panel"
760
+ />
761
+
762
  {/* Center - Editor Group (Always visible on mobile when mobileView='editor', always visible on desktop) */}
763
  <div className={`
764
  ${mobileView === 'editor' ? 'flex' : 'hidden'} md:flex
765
  flex-1 flex-col bg-[#000000]
766
  absolute md:relative inset-0 md:inset-auto z-10 md:z-auto
767
+ md:min-w-0 overflow-hidden
768
+ w-full
769
  `}>
770
  {/* Tab Bar */}
771
  <div className="flex items-center px-4 h-10 bg-[#1d1d1f] border-b border-[#424245]/30">
 
801
  </div>
802
  </div>
803
 
804
+ {/* Resize Handle for Settings Sidebar (Desktop only) */}
805
+ <div
806
+ className={`hidden md:block resize-handle ${isResizingSettings ? 'resizing' : ''}`}
807
+ onMouseDown={startResizingSettings}
808
+ title="Drag to resize settings panel"
809
+ />
810
+
811
  {/* Right Sidebar - Configuration Panel (Hidden on mobile, shown when mobileView='settings') */}
812
+ <div
813
+ className={`
814
+ ${mobileView === 'settings' ? 'flex' : 'hidden'} md:flex
815
+ w-full
816
+ bg-[#000000] border-l border-[#424245]/30
817
+ overflow-y-auto
818
+ absolute md:relative inset-0 md:inset-auto z-10 md:z-auto
819
+ flex-col
820
+ md:flex-shrink-0
821
+ `}
822
+ style={isDesktop ? { width: `${settingsSidebarWidth}px` } : undefined}
823
+ >
824
  <ControlPanel
825
  selectedLanguage={selectedLanguage}
826
  selectedModel={selectedModel}