@@ -226,71 +226,77 @@ const App: React.FC = () => {
226226 } , [ ] ) ;
227227
228228 // Save settings to storage
229- const saveSettings = useCallback ( async ( newSettings : Partial < AppState [ 'preferences' ] > ) => {
230- try {
231- if ( ! window . electronAPI ) {
232- throw new Error ( 'Electron API not available' ) ;
233- }
229+ const saveSettings = useCallback (
230+ async ( newSettings : Partial < AppState [ 'preferences' ] > ) => {
231+ try {
232+ if ( ! window . electronAPI ) {
233+ throw new Error ( 'Electron API not available' ) ;
234+ }
234235
235- await window . electronAPI . saveSettings ( newSettings ) ;
236- } catch ( error ) {
237- console . error ( 'Error saving settings:' , error ) ;
238- addNotification ( {
239- type : 'error' ,
240- title : t ( 'toast.settingsSaveFailed' ) ,
241- message : t ( 'toast.settingsSaveFailedDesc' ) ,
242- } ) ;
243- }
244- } , [ t ] ) ;
236+ await window . electronAPI . saveSettings ( newSettings ) ;
237+ } catch ( error ) {
238+ console . error ( 'Error saving settings:' , error ) ;
239+ addNotification ( {
240+ type : 'error' ,
241+ title : t ( 'toast.settingsSaveFailed' ) ,
242+ message : t ( 'toast.settingsSaveFailedDesc' ) ,
243+ } ) ;
244+ }
245+ } ,
246+ [ t ]
247+ ) ;
245248
246249 // Load usage stats with enhanced error handling.
247250 // `showLoading` controls whether the full LoadingScreen blocks the UI while
248251 // we wait — callers pass false for silent background refreshes so cached
249252 // data stays visible.
250- const loadUsageStats = useCallback ( async ( showLoading = true ) => {
251- try {
252- if ( showLoading ) {
253- setState ( ( prev ) => ( { ...prev , loading : true , error : null } ) ) ;
254- }
253+ const loadUsageStats = useCallback (
254+ async ( showLoading = true ) => {
255+ try {
256+ if ( showLoading ) {
257+ setState ( ( prev ) => ( { ...prev , loading : true , error : null } ) ) ;
258+ }
255259
256- if ( ! window . electronAPI ) {
257- throw new Error ( 'Electron API not available' ) ;
258- }
260+ if ( ! window . electronAPI ) {
261+ throw new Error ( 'Electron API not available' ) ;
262+ }
259263
260- const data = await window . electronAPI . getUsageStats ( ) ;
264+ const data = await window . electronAPI . getUsageStats ( ) ;
265+
266+ setState ( ( prev ) => ( {
267+ ...prev ,
268+ stats : data ,
269+ loading : false ,
270+ isStale : false ,
271+ error : null ,
272+ } ) ) ;
273+
274+ // Add success notification for manual refresh
275+ if ( ! showLoading ) {
276+ addNotification ( {
277+ type : 'success' ,
278+ title : t ( 'toast.dataRefreshed' ) ,
279+ message : t ( 'toast.dataRefreshedDesc' ) ,
280+ } ) ;
281+ }
282+ } catch ( err ) {
283+ const errorMessage = err instanceof Error ? err . message : 'Failed to load usage stats' ;
261284
262- setState ( ( prev ) => ( {
263- ...prev ,
264- stats : data ,
265- loading : false ,
266- isStale : false ,
267- error : null ,
268- } ) ) ;
285+ setState ( ( prev ) => ( {
286+ ...prev ,
287+ error : errorMessage ,
288+ loading : false ,
289+ } ) ) ;
269290
270- // Add success notification for manual refresh
271- if ( ! showLoading ) {
272291 addNotification ( {
273- type : 'success ' ,
274- title : t ( 'toast.dataRefreshed ' ) ,
275- message : t ( 'toast.dataRefreshedDesc' ) ,
292+ type : 'error ' ,
293+ title : t ( 'toast.updateFailed ' ) ,
294+ message : errorMessage ,
276295 } ) ;
277296 }
278- } catch ( err ) {
279- const errorMessage = err instanceof Error ? err . message : 'Failed to load usage stats' ;
280-
281- setState ( ( prev ) => ( {
282- ...prev ,
283- error : errorMessage ,
284- loading : false ,
285- } ) ) ;
286-
287- addNotification ( {
288- type : 'error' ,
289- title : t ( 'toast.updateFailed' ) ,
290- message : errorMessage ,
291- } ) ;
292- }
293- } , [ t ] ) ;
297+ } ,
298+ [ t ]
299+ ) ;
294300
295301 // Force refresh data
296302 const refreshData = useCallback ( async ( ) => {
@@ -637,111 +643,107 @@ const App: React.FC = () => {
637643 // Whole header strip is the drag region. Interactive bits inside
638644 // (action buttons, window controls, nav tabs) individually opt out
639645 // via WebkitAppRegion: no-drag so clicks reach them normally.
640- style = { {
641- background : 'var(--parchment)' ,
642- WebkitAppRegion : 'drag' ,
643- } as React . CSSProperties }
646+ style = {
647+ {
648+ background : 'var(--parchment)' ,
649+ WebkitAppRegion : 'drag' ,
650+ } as React . CSSProperties
651+ }
644652 >
653+ < div
654+ // items-start pulls the right-side controls up to the top of
655+ // the row while the logo + stacked title/tagline fill more
656+ // vertical space on the left. Drag is inherited from <header>.
657+ className = "flex items-start justify-between mb-3"
658+ >
659+ < div className = "flex items-center gap-3" >
660+ { /* TokenWatch brand mark: terracotta disc with serif "T" inside */ }
645661 < div
646- // items-start pulls the right-side controls up to the top of
647- // the row while the logo + stacked title/tagline fill more
648- // vertical space on the left. Drag is inherited from <header>.
649- className = "flex items-start justify-between mb-3"
662+ className = "w-9 h-9 flex-shrink-0 rounded-full flex items-center justify-center"
663+ style = { { background : 'var(--terracotta)' } }
650664 >
651- < div className = "flex items-center gap-3" >
652- { /* TokenWatch brand mark: terracotta disc with serif "T" inside */ }
653- < div
654- className = "w-9 h-9 flex-shrink-0 rounded-full flex items-center justify-center"
655- style = { { background : 'var(--terracotta)' } }
656- >
657- < span
658- className = "font-serif leading-none text-white"
659- style = { { fontSize : '18px' } }
660- >
661- T
662- </ span >
663- </ div >
664- < div >
665- < h1
666- className = "font-serif leading-none"
667- style = { {
668- color : 'var(--claude-black)' ,
669- fontSize : '20px' ,
670- letterSpacing : '-0.005em' ,
671- fontWeight : 500 ,
672- } }
673- >
674- { t ( 'app.title' ) }
675- </ h1 >
676- < p
677- className = "mt-1 text-[11px]"
678- style = { { color : 'var(--claude-stone)' , letterSpacing : '0.02em' } }
679- >
680- { t ( 'app.tagline' ) }
681- </ p >
682- </ div >
683- </ div >
684-
685- < div
686- // -mt-2 pulls the controls up so their top offset matches
687- // the right-side padding (pr-3 = 12px), giving the row a
688- // balanced gutter around the buttons.
689- className = "flex items-center gap-1.5 -mt-2"
690- style = { { WebkitAppRegion : 'no-drag' } as React . CSSProperties }
665+ < span className = "font-serif leading-none text-white" style = { { fontSize : '18px' } } >
666+ T
667+ </ span >
668+ </ div >
669+ < div >
670+ < h1
671+ className = "font-serif leading-none"
672+ style = { {
673+ color : 'var(--claude-black)' ,
674+ fontSize : '20px' ,
675+ letterSpacing : '-0.005em' ,
676+ fontWeight : 500 ,
677+ } }
691678 >
692- < span
693- className = "px-2.5 py-1 rounded-md text-[11px] font-medium"
694- style = { {
695- color : 'var(--claude-olive)' ,
696- background : 'var(--sand)' ,
697- letterSpacing : '0.02em' ,
698- } }
699- >
700- { new Date ( ) . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' } ) }
701- </ span >
702-
703- < IconButton
704- onClick = { refreshData }
705- title = { t ( 'app.refresh' ) }
706- ariaLabel = { t ( 'app.refreshAria' ) }
707- noDrag
708- >
709- < RefreshCw className = "w-4 h-4" strokeWidth = { 1.75 } />
710- </ IconButton >
711-
712- < IconButton
713- onClick = { takeScreenshot }
714- title = { t ( 'app.screenshot' ) }
715- ariaLabel = { t ( 'app.screenshotAria' ) }
716- noDrag
717- >
718- < Camera className = "w-4 h-4" strokeWidth = { 1.75 } />
719- </ IconButton >
720-
721- { state . preferences . standaloneWindow ? (
722- < >
723- { /* Small vertical separator between app actions and
679+ { t ( 'app.title' ) }
680+ </ h1 >
681+ < p
682+ className = "mt-1 text-[11px]"
683+ style = { { color : 'var(--claude-stone)' , letterSpacing : '0.02em' } }
684+ >
685+ { t ( 'app.tagline' ) }
686+ </ p >
687+ </ div >
688+ </ div >
689+
690+ < div
691+ // -mt-2 pulls the controls up so their top offset matches
692+ // the right-side padding (pr-3 = 12px), giving the row a
693+ // balanced gutter around the buttons.
694+ className = "flex items-center gap-1.5 -mt-2"
695+ style = { { WebkitAppRegion : 'no-drag' } as React . CSSProperties }
696+ >
697+ < span
698+ className = "px-2.5 py-1 rounded-md text-[11px] font-medium"
699+ style = { {
700+ color : 'var(--claude-olive)' ,
701+ background : 'var(--sand)' ,
702+ letterSpacing : '0.02em' ,
703+ } }
704+ >
705+ { new Date ( ) . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' } ) }
706+ </ span >
707+
708+ < IconButton
709+ onClick = { refreshData }
710+ title = { t ( 'app.refresh' ) }
711+ ariaLabel = { t ( 'app.refreshAria' ) }
712+ noDrag
713+ >
714+ < RefreshCw className = "w-4 h-4" strokeWidth = { 1.75 } />
715+ </ IconButton >
716+
717+ < IconButton
718+ onClick = { takeScreenshot }
719+ title = { t ( 'app.screenshot' ) }
720+ ariaLabel = { t ( 'app.screenshotAria' ) }
721+ noDrag
722+ >
723+ < Camera className = "w-4 h-4" strokeWidth = { 1.75 } />
724+ </ IconButton >
725+
726+ { state . preferences . standaloneWindow ? (
727+ < >
728+ { /* Small vertical separator between app actions and
724729 window controls so the three Windows-style buttons
725730 read as a distinct group. */ }
726- < span
727- className = "mx-1 h-5"
728- style = { { width : 1 , background : 'var(--cream)' } }
729- />
730- < WindowControls isMaximized = { isMaximized } />
731- </ >
732- ) : (
733- < IconButton
734- onClick = { ( ) => window . electronAPI ?. quitApp ( ) }
735- title = { t ( 'app.quit' ) }
736- ariaLabel = { t ( 'app.quitAria' ) }
737- danger
738- noDrag
739- >
740- < X className = "w-4 h-4" strokeWidth = { 2 } />
741- </ IconButton >
742- ) }
743- </ div >
744- </ div >
731+ < span className = "mx-1 h-5" style = { { width : 1 , background : 'var(--cream)' } } />
732+ < WindowControls isMaximized = { isMaximized } />
733+ </ >
734+ ) : (
735+ < IconButton
736+ onClick = { ( ) => window . electronAPI ?. quitApp ( ) }
737+ title = { t ( 'app.quit' ) }
738+ ariaLabel = { t ( 'app.quitAria' ) }
739+ danger
740+ noDrag
741+ >
742+ < X className = "w-4 h-4" strokeWidth = { 2 } />
743+ </ IconButton >
744+ ) }
745+ </ div >
746+ </ div >
745747
746748 { /* Navigation Tabs */ }
747749 < NavigationTabs currentView = { state . currentView } onNavigate = { navigateTo } />
0 commit comments