asdf98 commited on
Commit
d1203ac
·
verified ·
1 Parent(s): d58fa0e

cleanup: remove grid toggle from floating toolbar; grid is now controlled from Settings

Browse files
Files changed (1) hide show
  1. src/components/Toolbar.tsx +51 -119
src/components/Toolbar.tsx CHANGED
@@ -1,119 +1,51 @@
1
- import { Pin, Paintbrush2, Droplet, Settings, Save, Upload, Minus, Square, X, MousePointer2, Globe, FolderOpen, ChevronDown, ImageDown, LogOut, FilePlus, GripVertical, Grid3X3 } from 'lucide-react';
2
- import { useAppStore } from '../store';
3
- import { useRef, useState } from 'react';
4
- import { invoke } from '@tauri-apps/api/core';
5
- import { getCurrentWindow } from '@tauri-apps/api/window';
6
-
7
- const appWindow = getCurrentWindow();
8
-
9
- export const Toolbar = () => {
10
- const { images, textNotes, annotations, palettes, zoom, setZoom, pan, setPan, globalDesaturate, setGlobalDesaturate, isAlwaysOnTop, setIsAlwaysOnTop, bgOpacity, setBgOpacity, isClickThrough, setIsClickThrough, isAnnotationMode, setIsAnnotationMode, isSettingsOpen, setIsSettingsOpen, setTextNotes, setImages, setAnnotations, setPalettes, isBrowserOpen, setIsBrowserOpen, isLibraryOpen, setIsLibraryOpen, currentScreen, setCurrentScreen, activeProjectId, setActiveProjectId, boardTitle, setBoardTitle } = useAppStore();
11
- const [isBoardMenuOpen, setIsBoardMenuOpen] = useState(false);
12
- const [showGrid, setShowGrid] = useState(true);
13
- const fileInputRef = useRef<HTMLInputElement>(null);
14
-
15
- const handleExportFile = () => {
16
- const state = JSON.stringify({ textNotes, images, annotations, palettes, zoom, pan, title: boardTitle }, null, 2);
17
- const blob = new Blob([state], { type: 'application/json' });
18
- const url = URL.createObjectURL(blob);
19
- const a = document.createElement('a');
20
- a.href = url; a.download = `${boardTitle.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase() || 'board'}.json`;
21
- document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url);
22
- };
23
-
24
- const handleQuickSave = () => {
25
- if (!activeProjectId) return;
26
- invoke('project_save', { id: activeProjectId, state: JSON.stringify({ textNotes, images, annotations, palettes, zoom, pan, title: boardTitle }), title: boardTitle }).catch(() => {});
27
- };
28
-
29
- const handleLoad = (e: React.ChangeEvent<HTMLInputElement>) => {
30
- const file = e.target.files?.[0];
31
- if (!file) return;
32
- const reader = new FileReader();
33
- reader.onload = (ev) => { try { const d = JSON.parse(ev.target?.result as string); if (d.images) setImages(d.images); if (d.textNotes) setTextNotes(d.textNotes); if (d.annotations) setAnnotations(d.annotations); if (d.palettes) setPalettes(d.palettes); if (d.zoom) setZoom(d.zoom); if (d.pan) setPan(d.pan); if (d.title) setBoardTitle(d.title); } catch {} };
34
- reader.readAsText(file);
35
- if (e.target) e.target.value = '';
36
- };
37
-
38
- const handleCloseProject = () => { setIsBoardMenuOpen(false); handleQuickSave(); setCurrentScreen('hub'); };
39
- const handleNewBoard = async () => { setIsBoardMenuOpen(false); handleQuickSave(); try { const entry = await invoke<any>('project_create', { title: null }); setActiveProjectId(entry.id); setBoardTitle(entry.title); } catch {} setImages([]); setTextNotes([]); setAnnotations([]); setPalettes([]); setZoom(1); setPan({ x: 0, y: 0 }); };
40
- const handleRename = (newTitle: string) => { const title = newTitle.trim() || 'Untitled Board'; setBoardTitle(title); if (activeProjectId) invoke('project_rename', { id: activeProjectId, title }).catch(() => {}); };
41
-
42
- // Emit grid toggle to canvas via custom event
43
- const toggleGrid = () => {
44
- setShowGrid(!showGrid);
45
- window.dispatchEvent(new CustomEvent('muse:toggle-grid', { detail: !showGrid }));
46
- };
47
-
48
- return (
49
- <div className={`absolute top-0 w-full h-16 pointer-events-none group/toolbar ${isBrowserOpen || isSettingsOpen ? 'z-20' : 'z-50'}`}>
50
- <div className="absolute top-4 left-1/2 -translate-x-1/2 bg-[#2A2A2E]/95 backdrop-blur-md border border-[#3A3A3E] shadow-2xl flex items-center h-[38px] rounded-lg pointer-events-auto transition-all duration-300 opacity-0 -translate-y-4 group-hover/toolbar:opacity-100 group-hover/toolbar:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 text-gray-400 select-none">
51
-
52
- {/* Drag handle — grabs the window */}
53
- <div className="flex items-center pl-2 pr-1 h-full cursor-grab active:cursor-grabbing text-white/20 hover:text-white/40 transition-colors" data-tauri-drag-region>
54
- <GripVertical size={12} className="pointer-events-none" />
55
- </div>
56
-
57
- {/* Title + menu */}
58
- <div className="flex items-center pr-2 border-r border-[#3A3A3E] h-full relative">
59
- <div className="text-[#E0E0E0] text-[13px] font-medium outline-none min-w-[30px] max-w-[160px] truncate" contentEditable suppressContentEditableWarning onBlur={(e) => handleRename(e.currentTarget.textContent || '')} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); (e.target as HTMLElement).blur(); } }}>{boardTitle}</div>
60
- <button className="ml-1 text-[#A0A0A0] hover:text-[#E0E0E0] rounded hover:bg-white/5 p-0.5" onClick={() => setIsBoardMenuOpen(!isBoardMenuOpen)}><ChevronDown size={14} /></button>
61
- {isBoardMenuOpen && (<><div className="fixed inset-0 z-40" onClick={() => setIsBoardMenuOpen(false)} /><div className="absolute top-full left-0 mt-2 w-52 bg-[#2A2A2E] border border-[#3A3A3E] rounded-lg shadow-2xl z-50 py-1.5 pointer-events-auto">
62
- <button onClick={handleNewBoard} className="w-full text-left px-3 py-2 text-[12px] text-[#C0C0C0] hover:bg-[#0A84FF] hover:text-white flex items-center gap-2.5"><FilePlus size={14} /> New Board</button>
63
- <button onClick={() => { setIsBoardMenuOpen(false); fileInputRef.current?.click(); }} className="w-full text-left px-3 py-2 text-[12px] text-[#C0C0C0] hover:bg-[#0A84FF] hover:text-white flex items-center gap-2.5"><FolderOpen size={14} /> Open File</button>
64
- <div className="h-px bg-[#3A3A3E] my-1.5 mx-2" />
65
- <button onClick={() => { setIsBoardMenuOpen(false); handleExportFile(); }} className="w-full text-left px-3 py-2 text-[12px] text-[#C0C0C0] hover:bg-[#0A84FF] hover:text-white flex items-center gap-2.5"><ImageDown size={14} /> Export as File</button>
66
- <div className="h-px bg-[#3A3A3E] my-1.5 mx-2" />
67
- <button onClick={handleCloseProject} className="w-full text-left px-3 py-2 text-[12px] text-[#FF453A] hover:bg-[#FF453A] hover:text-white flex items-center gap-2.5"><LogOut size={14} /> Close Project</button>
68
- </div></>)}
69
- </div>
70
-
71
- {/* Save / Open */}
72
- <div className="flex items-center px-1 h-full gap-1">
73
- <button className="w-8 h-8 flex items-center justify-center text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5 rounded-md" onClick={handleQuickSave} title="Save (Ctrl+S)"><Save size={14} /></button>
74
- <button className="w-8 h-8 flex items-center justify-center text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5 rounded-md" onClick={() => fileInputRef.current?.click()} title="Open File"><Upload size={14} /></button>
75
- <input type="file" accept=".json" className="hidden" ref={fileInputRef} onChange={handleLoad} />
76
- </div>
77
- <div className="w-px h-[22px] bg-[#3A3A3E]" />
78
-
79
- {/* Zoom */}
80
- <div className="flex items-center px-2 h-full"><div className="text-[#808080] text-[11px] w-10 text-center font-medium">{Math.round(zoom * 100)}%</div></div>
81
- <div className="w-px h-[22px] bg-[#3A3A3E]" />
82
-
83
- {/* Browser & Library & Grid */}
84
- <div className="flex items-center px-1 h-full gap-1">
85
- <button className={`w-8 h-8 flex items-center justify-center rounded-md transition-colors ${isBrowserOpen ? 'text-[#0A84FF] bg-[#0A84FF]/10' : 'text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5'}`} onClick={() => setIsBrowserOpen(!isBrowserOpen)} title="Browser (B)"><Globe size={14} /></button>
86
- <button className={`w-8 h-8 flex items-center justify-center rounded-md transition-colors ${isLibraryOpen ? 'text-[#0A84FF] bg-[#0A84FF]/10' : 'text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5'}`} onClick={() => setIsLibraryOpen(!isLibraryOpen)} title="Library (L)"><FolderOpen size={14} /></button>
87
- <button className={`w-8 h-8 flex items-center justify-center rounded-md transition-colors ${showGrid ? 'text-white/70 bg-white/10' : 'text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5'}`} onClick={toggleGrid} title="Toggle Grid (G)"><Grid3X3 size={14} /></button>
88
- </div>
89
- <div className="w-px h-[22px] bg-[#3A3A3E]" />
90
-
91
- {/* Tools */}
92
- <div className="flex items-center px-1 h-full gap-1">
93
- <button className={`w-8 h-8 flex items-center justify-center rounded-md ${isAlwaysOnTop ? 'text-[#E0E0E0] bg-white/10' : 'text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5'}`} onClick={() => setIsAlwaysOnTop(!isAlwaysOnTop)} title="Always on Top (T)"><Pin size={14} className={isAlwaysOnTop ? '' : '-rotate-45'} /></button>
94
- {isAlwaysOnTop && <><button className={`w-8 h-8 flex items-center justify-center rounded-md ${isClickThrough ? 'text-[#0A84FF] bg-[#0A84FF]/10' : 'text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5'}`} onClick={() => setIsClickThrough(!isClickThrough)}><MousePointer2 size={14} /></button><div className="flex items-center gap-2 pl-2"><span className="text-[10px] text-[#808080] w-6">{bgOpacity}%</span><input type="range" min="10" max="100" value={bgOpacity} onChange={e => setBgOpacity(Number(e.target.value))} className="w-16 accent-[#0A84FF]" /></div></>}
95
- </div>
96
- <div className="w-px h-[22px] bg-[#3A3A3E]" />
97
-
98
- {/* Annotate & Desaturate */}
99
- <div className="flex items-center px-1 h-full gap-1">
100
- <button className={`w-8 h-8 flex items-center justify-center rounded-md ${isAnnotationMode ? 'text-[#32D74B] bg-[#32D74B]/10' : 'text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5'}`} onClick={() => setIsAnnotationMode(!isAnnotationMode)} title="Annotate (A)"><Paintbrush2 size={14} /></button>
101
- <button className={`w-8 h-8 flex items-center justify-center rounded-md ${globalDesaturate ? 'text-[#FFD60A] bg-[#FFD60A]/10' : 'text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5'}`} onClick={() => setGlobalDesaturate(!globalDesaturate)} title="Grayscale (D)"><Droplet size={14} /></button>
102
- </div>
103
- <div className="w-px h-[22px] bg-[#3A3A3E]" />
104
-
105
- {/* Settings */}
106
- <div className="flex items-center px-1 h-full">
107
- <button className="w-8 h-8 flex items-center justify-center text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5 rounded-md" onClick={() => setIsSettingsOpen(!isSettingsOpen)} title="Settings (Ctrl+,)"><Settings size={14} /></button>
108
- </div>
109
-
110
- {/* Window controls */}
111
- <div className="flex items-center gap-1 pl-2 pr-2 h-full border-l border-[#3A3A3E]">
112
- <button className="w-8 h-8 flex items-center justify-center text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5 rounded-md" onClick={() => appWindow.minimize()}><Minus size={15} /></button>
113
- <button className="w-8 h-8 flex items-center justify-center text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5 rounded-md" onClick={() => appWindow.toggleMaximize()}><Square size={12} /></button>
114
- <button className="w-8 h-8 flex items-center justify-center text-[#A0A0A0] hover:text-[#FF453A] hover:bg-[#FF453A]/10 rounded-md" onClick={() => appWindow.close()}><X size={16} /></button>
115
- </div>
116
- </div>
117
- </div>
118
- );
119
- };
 
1
+ import { Pin, Paintbrush2, Droplet, Settings, Save, Upload, Minus, Square, X, MousePointer2, Globe, FolderOpen, ChevronDown, ImageDown, LogOut, FilePlus, GripVertical } from 'lucide-react';
2
+ import { useAppStore } from '../store';
3
+ import { useRef, useState } from 'react';
4
+ import { invoke } from '@tauri-apps/api/core';
5
+ import { getCurrentWindow } from '@tauri-apps/api/window';
6
+
7
+ const appWindow = getCurrentWindow();
8
+
9
+ export const Toolbar = () => {
10
+ const { images, textNotes, annotations, palettes, zoom, setZoom, pan, setPan, globalDesaturate, setGlobalDesaturate, isAlwaysOnTop, setIsAlwaysOnTop, bgOpacity, setBgOpacity, isClickThrough, setIsClickThrough, isAnnotationMode, setIsAnnotationMode, isSettingsOpen, setIsSettingsOpen, setTextNotes, setImages, setAnnotations, setPalettes, isBrowserOpen, setIsBrowserOpen, isLibraryOpen, setIsLibraryOpen, currentScreen, setCurrentScreen, activeProjectId, setActiveProjectId, boardTitle, setBoardTitle } = useAppStore();
11
+ const [isBoardMenuOpen, setIsBoardMenuOpen] = useState(false);
12
+ const fileInputRef = useRef<HTMLInputElement>(null);
13
+
14
+ const handleExportFile = () => {
15
+ const state = JSON.stringify({ textNotes, images, annotations, palettes, zoom, pan, title: boardTitle }, null, 2);
16
+ const blob = new Blob([state], { type: 'application/json' });
17
+ const url = URL.createObjectURL(blob);
18
+ const a = document.createElement('a');
19
+ a.href = url; a.download = `${boardTitle.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase() || 'board'}.json`;
20
+ document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url);
21
+ };
22
+
23
+ const handleQuickSave = () => { if (!activeProjectId) return; invoke('project_save', { id: activeProjectId, state: JSON.stringify({ textNotes, images, annotations, palettes, zoom, pan, title: boardTitle }), title: boardTitle }).catch(() => {}); };
24
+ const handleLoad = (e: React.ChangeEvent<HTMLInputElement>) => { const file = e.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = (ev) => { try { const d = JSON.parse(ev.target?.result as string); if (d.images) setImages(d.images); if (d.textNotes) setTextNotes(d.textNotes); if (d.annotations) setAnnotations(d.annotations); if (d.palettes) setPalettes(d.palettes); if (d.zoom) setZoom(d.zoom); if (d.pan) setPan(d.pan); if (d.title) setBoardTitle(d.title); } catch {} }; reader.readAsText(file); e.target.value = ''; };
25
+ const handleCloseProject = () => { setIsBoardMenuOpen(false); handleQuickSave(); setCurrentScreen('hub'); };
26
+ const handleNewBoard = async () => { setIsBoardMenuOpen(false); handleQuickSave(); try { const entry = await invoke<any>('project_create', { title: null }); setActiveProjectId(entry.id); setBoardTitle(entry.title); } catch {} setImages([]); setTextNotes([]); setAnnotations([]); setPalettes([]); setZoom(1); setPan({ x: 0, y: 0 }); };
27
+ const handleRename = (newTitle: string) => { const title = newTitle.trim() || 'Untitled Board'; setBoardTitle(title); if (activeProjectId) invoke('project_rename', { id: activeProjectId, title }).catch(() => {}); };
28
+
29
+ return <div className={`absolute top-0 w-full h-16 pointer-events-none group/toolbar ${isBrowserOpen || isSettingsOpen ? 'z-20' : 'z-50'}`}>
30
+ <div className="absolute top-4 left-1/2 -translate-x-1/2 bg-[#2A2A2E]/95 backdrop-blur-md border border-[#3A3A3E] shadow-2xl flex items-center h-[38px] rounded-lg pointer-events-auto transition-all duration-300 opacity-0 -translate-y-4 group-hover/toolbar:opacity-100 group-hover/toolbar:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 text-gray-400 select-none">
31
+ <div className="flex items-center pl-2 pr-1 h-full cursor-grab active:cursor-grabbing text-white/20 hover:text-white/40 transition-colors" data-tauri-drag-region><GripVertical size={12} className="pointer-events-none" /></div>
32
+ <div className="flex items-center pr-2 border-r border-[#3A3A3E] h-full relative">
33
+ <div className="text-[#E0E0E0] text-[13px] font-medium outline-none min-w-[30px] max-w-[160px] truncate" contentEditable suppressContentEditableWarning onBlur={(e) => handleRename(e.currentTarget.textContent || '')} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); (e.target as HTMLElement).blur(); } }}>{boardTitle}</div>
34
+ <button className="ml-1 text-[#A0A0A0] hover:text-[#E0E0E0] rounded hover:bg-white/5 p-0.5" onClick={() => setIsBoardMenuOpen(!isBoardMenuOpen)}><ChevronDown size={14} /></button>
35
+ {isBoardMenuOpen && <><div className="fixed inset-0 z-40" onClick={() => setIsBoardMenuOpen(false)} /><div className="absolute top-full left-0 mt-2 w-52 bg-[#2A2A2E] border border-[#3A3A3E] rounded-lg shadow-2xl z-50 py-1.5 pointer-events-auto"><button onClick={handleNewBoard} className="w-full text-left px-3 py-2 text-[12px] text-[#C0C0C0] hover:bg-[#0A84FF] hover:text-white flex items-center gap-2.5"><FilePlus size={14} /> New Board</button><button onClick={() => { setIsBoardMenuOpen(false); fileInputRef.current?.click(); }} className="w-full text-left px-3 py-2 text-[12px] text-[#C0C0C0] hover:bg-[#0A84FF] hover:text-white flex items-center gap-2.5"><FolderOpen size={14} /> Open File</button><div className="h-px bg-[#3A3A3E] my-1.5 mx-2" /><button onClick={() => { setIsBoardMenuOpen(false); handleExportFile(); }} className="w-full text-left px-3 py-2 text-[12px] text-[#C0C0C0] hover:bg-[#0A84FF] hover:text-white flex items-center gap-2.5"><ImageDown size={14} /> Export as File</button><div className="h-px bg-[#3A3A3E] my-1.5 mx-2" /><button onClick={handleCloseProject} className="w-full text-left px-3 py-2 text-[12px] text-[#FF453A] hover:bg-[#FF453A] hover:text-white flex items-center gap-2.5"><LogOut size={14} /> Close Project</button></div></>}
36
+ </div>
37
+ <div className="flex items-center px-1 h-full gap-1"><button className="w-8 h-8 flex items-center justify-center text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5 rounded-md" onClick={handleQuickSave} title="Save (Ctrl+S)"><Save size={14} /></button><button className="w-8 h-8 flex items-center justify-center text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5 rounded-md" onClick={() => fileInputRef.current?.click()} title="Open File"><Upload size={14} /></button><input type="file" accept=".json" className="hidden" ref={fileInputRef} onChange={handleLoad} /></div>
38
+ <div className="w-px h-[22px] bg-[#3A3A3E]" />
39
+ <div className="flex items-center px-2 h-full"><div className="text-[#808080] text-[11px] w-10 text-center font-medium">{Math.round(zoom * 100)}%</div></div>
40
+ <div className="w-px h-[22px] bg-[#3A3A3E]" />
41
+ <div className="flex items-center px-1 h-full gap-1"><button className={`w-8 h-8 flex items-center justify-center rounded-md transition-colors ${isBrowserOpen ? 'text-[#0A84FF] bg-[#0A84FF]/10' : 'text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5'}`} onClick={() => setIsBrowserOpen(!isBrowserOpen)} title="Browser (B)"><Globe size={14} /></button><button className={`w-8 h-8 flex items-center justify-center rounded-md transition-colors ${isLibraryOpen ? 'text-[#0A84FF] bg-[#0A84FF]/10' : 'text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5'}`} onClick={() => setIsLibraryOpen(!isLibraryOpen)} title="Library (L)"><FolderOpen size={14} /></button></div>
42
+ <div className="w-px h-[22px] bg-[#3A3A3E]" />
43
+ <div className="flex items-center px-1 h-full gap-1"><button className={`w-8 h-8 flex items-center justify-center rounded-md ${isAlwaysOnTop ? 'text-[#E0E0E0] bg-white/10' : 'text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5'}`} onClick={() => setIsAlwaysOnTop(!isAlwaysOnTop)} title="Always on Top (T)"><Pin size={14} className={isAlwaysOnTop ? '' : '-rotate-45'} /></button>{isAlwaysOnTop && <><button className={`w-8 h-8 flex items-center justify-center rounded-md ${isClickThrough ? 'text-[#0A84FF] bg-[#0A84FF]/10' : 'text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5'}`} onClick={() => setIsClickThrough(!isClickThrough)}><MousePointer2 size={14} /></button><div className="flex items-center gap-2 pl-2"><span className="text-[10px] text-[#808080] w-6">{bgOpacity}%</span><input type="range" min="10" max="100" value={bgOpacity} onChange={e => setBgOpacity(Number(e.target.value))} className="w-16 accent-[#0A84FF]" /></div></>}</div>
44
+ <div className="w-px h-[22px] bg-[#3A3A3E]" />
45
+ <div className="flex items-center px-1 h-full gap-1"><button className={`w-8 h-8 flex items-center justify-center rounded-md ${isAnnotationMode ? 'text-[#32D74B] bg-[#32D74B]/10' : 'text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5'}`} onClick={() => setIsAnnotationMode(!isAnnotationMode)} title="Annotate (A)"><Paintbrush2 size={14} /></button><button className={`w-8 h-8 flex items-center justify-center rounded-md ${globalDesaturate ? 'text-[#FFD60A] bg-[#FFD60A]/10' : 'text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5'}`} onClick={() => setGlobalDesaturate(!globalDesaturate)} title="Grayscale (D)"><Droplet size={14} /></button></div>
46
+ <div className="w-px h-[22px] bg-[#3A3A3E]" />
47
+ <div className="flex items-center px-1 h-full"><button className="w-8 h-8 flex items-center justify-center text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5 rounded-md" onClick={() => setIsSettingsOpen(!isSettingsOpen)} title="Settings (Ctrl+,)"><Settings size={14} /></button></div>
48
+ <div className="flex items-center gap-1 pl-2 pr-2 h-full border-l border-[#3A3A3E]"><button className="w-8 h-8 flex items-center justify-center text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5 rounded-md" onClick={() => appWindow.minimize()}><Minus size={15} /></button><button className="w-8 h-8 flex items-center justify-center text-[#A0A0A0] hover:text-[#E0E0E0] hover:bg-white/5 rounded-md" onClick={() => appWindow.toggleMaximize()}><Square size={12} /></button><button className="w-8 h-8 flex items-center justify-center text-[#A0A0A0] hover:text-[#FF453A] hover:bg-[#FF453A]/10 rounded-md" onClick={() => appWindow.close()}><X size={16} /></button></div>
49
+ </div>
50
+ </div>;
51
+ };