@@ -503,23 +503,49 @@ export function setupIpcHandlers(): void {
503503 fileWatchers . set ( resolvedPath , abortController ) ;
504504
505505 // Use fs.watch if available, otherwise fallback to polling
506+ const IGNORED_DIRS = new Set ( [
507+ 'node_modules' , '.git' , 'dist' , 'build' , '.next' , '.cache' ,
508+ '.turbo' , '.nuxt' , '.output' , '__pycache__' , '.venv' , 'venv' ,
509+ '.expo' , '.parcel-cache' , 'coverage' , '.svelte-kit' ,
510+ ] ) ;
511+
506512 const { watch } = await import ( 'node:fs' ) ;
513+
514+ // Debounce file change notifications to avoid IPC flooding
515+ let pendingChanges = new Map < string , string > ( ) ;
516+ let debounceTimer : ReturnType < typeof setTimeout > | null = null ;
517+
518+ const flushChanges = ( ) => {
519+ const changes = Array . from ( pendingChanges . entries ( ) ) ;
520+ pendingChanges = new Map ( ) ;
521+ debounceTimer = null ;
522+ BrowserWindow . getAllWindows ( ) . forEach ( window => {
523+ for ( const [ fullPath , type ] of changes ) {
524+ window . webContents . send ( 'file:change' , { type, path : fullPath } ) ;
525+ }
526+ } ) ;
527+ } ;
528+
507529 const watcher = watch ( resolvedPath , { recursive : true } , ( eventType , filename ) => {
508530 if ( ! filename ) return ;
509-
531+
532+ // Skip ignored directories
533+ const parts = filename . split ( path . sep ) ;
534+ if ( parts . some ( p => IGNORED_DIRS . has ( p ) ) ) return ;
535+
510536 const fullPath = path . join ( resolvedPath , filename ) ;
511-
512- // Send to all windows
513- BrowserWindow . getAllWindows ( ) . forEach ( window => {
514- window . webContents . send ( 'file:change' , {
515- type : eventType === 'rename' ? 'unlink' : 'change' ,
516- path : fullPath ,
517- } ) ;
518- } ) ;
537+ const type = eventType === 'rename' ? 'unlink' : 'change' ;
538+
539+ pendingChanges . set ( fullPath , type ) ;
540+ if ( ! debounceTimer ) {
541+ debounceTimer = setTimeout ( flushChanges , 300 ) ;
542+ }
519543 } ) ;
520544
521545 // Store watcher reference
522546 abortController . signal . addEventListener ( 'abort' , ( ) => {
547+ if ( debounceTimer ) clearTimeout ( debounceTimer ) ;
548+ pendingChanges . clear ( ) ;
523549 watcher . close ( ) ;
524550 } ) ;
525551
@@ -1253,12 +1279,22 @@ export function setupIpcHandlers(): void {
12531279
12541280 // --- Git IPC Handlers ---
12551281 console . log ( '[IPC:git] Registering git handlers...' ) ;
1282+ const GIT_MANAGER_CACHE_MAX = 10 ;
12561283 const gitManagerCache = new Map < string , GitManager > ( ) ;
12571284 function getGitManager ( cwd : string ) : GitManager {
12581285 let mgr = gitManagerCache . get ( cwd ) ;
1259- if ( ! mgr ) {
1260- mgr = new GitManager ( cwd ) ;
1286+ if ( mgr ) {
1287+ // Move to end (most recently used)
1288+ gitManagerCache . delete ( cwd ) ;
12611289 gitManagerCache . set ( cwd , mgr ) ;
1290+ return mgr ;
1291+ }
1292+ mgr = new GitManager ( cwd ) ;
1293+ gitManagerCache . set ( cwd , mgr ) ;
1294+ // Evict oldest entry if cache exceeds limit
1295+ if ( gitManagerCache . size > GIT_MANAGER_CACHE_MAX ) {
1296+ const oldest = gitManagerCache . keys ( ) . next ( ) . value ;
1297+ if ( oldest ) gitManagerCache . delete ( oldest ) ;
12621298 }
12631299 return mgr ;
12641300 }
0 commit comments