1
- import { ChevronDown , Cpu , Settings } from 'lucide-react' ;
1
+ import { ChevronDown , Cpu , Minimize2 , Minus , Settings , Square , X } from 'lucide-react' ;
2
2
import React , { useEffect , useState } from 'react' ;
3
3
import { SelectModelDialog } from '../models/SelectModelDialog' ;
4
4
import { AIService , ModelOption } from '../../services/ai-service' ;
5
5
import { SettingsService } from '../../services/settings-service' ;
6
+ import tensorBlockLogo from '/logos/TensorBlock_logo_dark.svg' ;
6
7
7
8
interface TopBarProps {
8
9
onSelectModel : ( model : string , provider : string ) => void ;
@@ -14,6 +15,48 @@ const TopBar: React.FC<TopBarProps> = ({ onSelectModel, onOpenSettingsDialog })
14
15
const [ selectedModel , setSelectedModel ] = useState ( '' ) ;
15
16
const [ selectedModelName , setSelectedModelName ] = useState ( '' ) ;
16
17
const [ selectedProvider , setSelectedProvider ] = useState ( '' ) ;
18
+ const [ isMaximized , setIsMaximized ] = useState ( false ) ;
19
+
20
+ // Check if window is maximized on mount
21
+ useEffect ( ( ) => {
22
+ const checkMaximized = async ( ) => {
23
+ if ( window . electron && window . electron . isMaximized ) {
24
+ const maximized = await window . electron . isMaximized ( ) ;
25
+ setIsMaximized ( maximized ) ;
26
+ }
27
+ } ;
28
+
29
+ checkMaximized ( ) ;
30
+
31
+ window . electron . onWindowMaximizedChange ( ( _event , maximized ) => {
32
+ setIsMaximized ( maximized ) ;
33
+ } ) ;
34
+ } , [ ] ) ;
35
+
36
+ // Window control handlers
37
+ const handleMinimize = ( ) => {
38
+ if ( window . electron && window . electron . minimize ) {
39
+ window . electron . minimize ( ) ;
40
+ }
41
+ } ;
42
+
43
+ const handleMaximize = async ( ) => {
44
+ if ( ! window . electron ) return ;
45
+
46
+ if ( isMaximized && window . electron . unmaximize ) {
47
+ await window . electron . unmaximize ( ) ;
48
+ setIsMaximized ( false ) ;
49
+ } else if ( window . electron . maximize ) {
50
+ await window . electron . maximize ( ) ;
51
+ setIsMaximized ( true ) ;
52
+ }
53
+ } ;
54
+
55
+ const handleClose = ( ) => {
56
+ if ( window . electron && window . electron . closeApp ) {
57
+ window . electron . closeApp ( ) ;
58
+ }
59
+ } ;
17
60
18
61
useEffect ( ( ) => {
19
62
const settingsService = SettingsService . getInstance ( ) ;
@@ -41,30 +84,70 @@ const TopBar: React.FC<TopBarProps> = ({ onSelectModel, onOpenSettingsDialog })
41
84
} ;
42
85
43
86
return (
44
- < div className = "flex items-center justify-center h-16 gap-8 px-6 bg-white border-b border-gray-200" >
45
- < div className = "flex items-center w-1/3 gap-2" >
87
+ < div className = "flex items-center justify-between h-16 bg-white app-region-drag" >
88
+ { /* Logo area */ }
89
+ < div className = "w-[68px] aspect-square flex items-center justify-center h-16" >
90
+ < div className = "flex items-center justify-center w-10 h-10" >
91
+ < img
92
+ src = { tensorBlockLogo }
93
+ alt = "TensorBlock Logo"
94
+ className = "w-8 h-8"
95
+ onError = { ( e ) => {
96
+ const target = e . target as HTMLImageElement ;
97
+ target . src = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="20" height="20" rx="5" ry="5" /><path d="M16 16h.01" /><path d="M8 16h.01" /><path d="M12 8v8" /></svg>' ;
98
+ } }
99
+ />
100
+ </ div >
101
+ </ div >
102
+
103
+ < div className = 'flex items-center justify-center gap-8 w-full' >
104
+ < div className = "flex items-center w-1/3 gap-2" >
105
+ < button
106
+ className = "btn hover:bg-gray-200 w-full bg-gray-100 border-0 rounded-md px-3 py-1.5 text-sm font-medium text-gray-700 flex justify-between app-region-no-drag"
107
+ onClick = { handleOpenModelDialog }
108
+ aria-label = "Select AI model"
109
+ >
110
+ < Cpu className = "w-5 h-5 p-0.5 text-gray-600" />
111
+ < span className = 'text-center truncate max-w-[200px]' > { selectedModelName } </ span >
112
+ < ChevronDown className = "w-5 h-5 text-gray-600" />
113
+ </ button >
114
+ </ div >
115
+ < button className = "flex items-center justify-center gap-2 px-2 py-1 text-sm text-gray-600 border border-gray-200 rounded-md hover:text-gray-900 app-region-no-drag" onClick = { onOpenSettingsDialog } >
116
+ < Settings className = "w-5 h-5 p-0.5" />
117
+ < span className = 'truncate max-w-[200px]' > API</ span >
118
+ </ button >
119
+
120
+ < SelectModelDialog
121
+ isOpen = { isModelDialogOpen }
122
+ onClose = { handleCloseModelDialog }
123
+ onSelectModel = { handleSelectModel }
124
+ currentModelId = { selectedModel }
125
+ currentProviderId = { selectedProvider }
126
+ />
127
+ </ div >
128
+
129
+ < div className = 'flex items-start justify-center gap-1 h-full' >
46
130
< button
47
- className = "btn hover:bg-gray-200 w-full bg-gray-100 border-0 rounded-md px-3 py-1.5 text-sm font-medium text-gray-700 flex justify-between"
48
- onClick = { handleOpenModelDialog }
49
- aria-label = "Select AI model"
131
+ className = 'btn hover:bg-gray-200 bg-transparent border-0 px-3 py-1.5 text-sm font-medium text-gray-600 flex justify-center items-center app-region-no-drag'
132
+ onClick = { handleMinimize }
133
+ >
134
+ < Minus className = 'w-5 h-5' />
135
+ </ button >
136
+
137
+ < button
138
+ className = 'btn hover:bg-gray-200 bg-transparent border-0 px-3 py-1.5 text-sm font-medium text-gray-600 flex justify-center items-center app-region-no-drag'
139
+ onClick = { handleMaximize }
140
+ >
141
+ { isMaximized ? < Minimize2 className = 'w-5 h-5' /> : < Square className = 'w-5 h-5 p-0.5' /> }
142
+ </ button >
143
+
144
+ < button
145
+ className = 'btn hover:bg-red-500 bg-transparent border-0 px-3 py-1.5 text-sm font-medium text-gray-600 hover:text-white flex justify-center items-center app-region-no-drag'
146
+ onClick = { handleClose }
50
147
>
51
- < Cpu className = "w-5 h-5 p-0.5 text-gray-600" />
52
- < span className = 'text-center truncate max-w-[200px]' > { selectedModelName } </ span >
53
- < ChevronDown className = "w-5 h-5 text-gray-600" />
148
+ < X className = 'w-5 h-5' />
54
149
</ button >
55
150
</ div >
56
- < button className = "flex items-center justify-center gap-2 px-2 py-1 text-sm text-gray-600 border border-gray-200 rounded-md hover:text-gray-900" onClick = { onOpenSettingsDialog } >
57
- < Settings className = "w-5 h-5 p-0.5" />
58
- < span className = 'truncate max-w-[200px]' > API</ span >
59
- </ button >
60
-
61
- < SelectModelDialog
62
- isOpen = { isModelDialogOpen }
63
- onClose = { handleCloseModelDialog }
64
- onSelectModel = { handleSelectModel }
65
- currentModelId = { selectedModel }
66
- currentProviderId = { selectedProvider }
67
- />
68
151
</ div >
69
152
) ;
70
153
} ;
0 commit comments