11import { Files , Info } from 'lucide-react'
2- import React , { useEffect , useState } from 'react'
2+ import React , { useCallback , useState } from 'react'
33
44import { Button } from '@/renderer/src/components/ui/button'
55import { Input } from '@/renderer/src/components/ui/input'
66import {
77 ToggleGroup ,
88 ToggleGroupItem ,
99} from '@/renderer/src/components/ui/toggle-group'
10+ import { useComponentEffect } from '@/renderer/src/hooks/useComponentEffect'
1011import { useUpdateSettings } from '@/renderer/src/hooks/useUpdateSettings'
1112import { useAppSelector } from '@/renderer/src/redux/hooks'
1213import { TERMINAL_APP_IDS , TERMINAL_APP_UI_LABELS } from '@/shared/constants'
@@ -70,10 +71,13 @@ export const General = React.memo(function General(): React.ReactElement {
7071 settings . customTerminalAppName ?? '' ,
7172 )
7273
73- const handleDefaultTabChange = ( nextValue : string ) : void => {
74- if ( nextValue !== 'files' && nextValue !== 'info' ) return
75- updateSettings ( { defaultSkillTab : nextValue } )
76- }
74+ const handleDefaultTabChange = useCallback (
75+ ( nextValue : string ) : void => {
76+ if ( nextValue !== 'files' && nextValue !== 'info' ) return
77+ updateSettings ( { defaultSkillTab : nextValue } )
78+ } ,
79+ [ updateSettings ] ,
80+ )
7781
7882 const handlePreferredTerminalChange = (
7983 e : React . ChangeEvent < HTMLSelectElement > ,
@@ -94,12 +98,19 @@ export const General = React.memo(function General(): React.ReactElement {
9498 * schema so the renderer never asks main to write a value that the
9599 * schema would reject.
96100 */
97- const handleCustomNameBlur = ( ) : void => {
101+ const handleCustomNameChange = useCallback (
102+ ( e : React . ChangeEvent < HTMLInputElement > ) : void => {
103+ setCustomNameDraft ( e . target . value )
104+ } ,
105+ [ ] ,
106+ )
107+
108+ const handleCustomNameBlur = useCallback ( ( ) : void => {
98109 const trimmed = customNameDraft . trim ( )
99110 if ( trimmed === '' ) return
100111 if ( trimmed === settings . customTerminalAppName ) return
101112 updateSettings ( { customTerminalAppName : trimmed } )
102- }
113+ } , [ customNameDraft , settings . customTerminalAppName , updateSettings ] )
103114
104115 const isCustom = settings . preferredTerminal === 'custom'
105116 const isDraftBlank = customNameDraft . trim ( ) === ''
@@ -118,7 +129,7 @@ export const General = React.memo(function General(): React.ReactElement {
118129 const [ isMainWindowAvailable , setIsMainWindowAvailable ] =
119130 useState < boolean > ( true )
120131
121- useEffect ( ( ) => {
132+ useComponentEffect ( ( ) => {
122133 let cancelled = false
123134 // Attach `.catch` explicitly — `void promise.then(...)` only suppresses
124135 // TypeScript's "promise not handled" hint, it does NOT silence a real
@@ -147,20 +158,28 @@ export const General = React.memo(function General(): React.ReactElement {
147158 * flag so the button's disabled state + inline message kick in instead
148159 * of failing silently. Disk write only happens on a real bounds value.
149160 */
150- const handleSaveCurrentSize = async ( ) : Promise < void > => {
151- const bounds = await window . electron . window . getMainBounds ( )
152- if ( bounds === null ) {
161+ const handleSaveCurrentSize = useCallback ( async ( ) : Promise < void > => {
162+ try {
163+ const bounds = await window . electron . window . getMainBounds ( )
164+ if ( bounds === null ) {
165+ setIsMainWindowAvailable ( false )
166+ return
167+ }
168+ updateSettings ( { windowSize : bounds } )
169+ } catch {
153170 setIsMainWindowAvailable ( false )
154- return
155171 }
156- updateSettings ( { windowSize : bounds } )
157- }
172+ } , [ updateSettings ] )
158173
159- const handleResetWindowSize = ( ) : void => {
174+ const handleSaveCurrentSizeClick = useCallback ( ( ) : void => {
175+ void handleSaveCurrentSize ( )
176+ } , [ handleSaveCurrentSize ] )
177+
178+ const handleResetWindowSize = useCallback ( ( ) : void => {
160179 // `undefined` removes the persisted size; on next launch the main
161180 // window falls back to the default 1200×800 + maximize() behavior.
162181 updateSettings ( { windowSize : undefined } )
163- }
182+ } , [ updateSettings ] )
164183
165184 const persistedWindowSize = settings . windowSize
166185 const hasCustomWindowSize = persistedWindowSize !== undefined
@@ -224,7 +243,7 @@ export const General = React.memo(function General(): React.ReactElement {
224243 < Input
225244 type = "text"
226245 value = { customNameDraft }
227- onChange = { ( e ) => setCustomNameDraft ( e . target . value ) }
246+ onChange = { handleCustomNameChange }
228247 onBlur = { handleCustomNameBlur }
229248 placeholder = "e.g. Hyper"
230249 maxLength = { CUSTOM_APP_NAME_MAX_LENGTH }
@@ -273,9 +292,7 @@ export const General = React.memo(function General(): React.ReactElement {
273292 type = "button"
274293 variant = "outline"
275294 size = "sm"
276- onClick = { ( ) => {
277- void handleSaveCurrentSize ( )
278- } }
295+ onClick = { handleSaveCurrentSizeClick }
279296 disabled = { ! isMainWindowAvailable }
280297 >
281298 Use current window size
0 commit comments