@@ -2,6 +2,7 @@ import {FitAddon} from "@xterm/addon-fit";
22import { ipcRenderer } from "electron" ;
33import { Terminal } from "xterm" ;
44import { PersistedSession , SessionConfig } from "./types" ;
5+ import { isClaudeSessionReady } from "./terminal-utils" ;
56
67interface Session {
78 id : string ;
@@ -12,7 +13,6 @@ interface Session {
1213 config : SessionConfig ;
1314 worktreePath : string ;
1415 hasActivePty : boolean ;
15- hasUnreadActivity : boolean ;
1616}
1717
1818interface McpServer {
@@ -272,7 +272,6 @@ function addSession(persistedSession: PersistedSession, hasActivePty: boolean) {
272272 config : persistedSession . config ,
273273 worktreePath : persistedSession . worktreePath ,
274274 hasActivePty,
275- hasUnreadActivity : false ,
276275 } ;
277276
278277 sessions . set ( persistedSession . id , session ) ;
@@ -481,8 +480,6 @@ function markSessionAsUnread(sessionId: string) {
481480 const session = sessions . get ( sessionId ) ;
482481 if ( ! session ) return ;
483482
484- session . hasUnreadActivity = true ;
485-
486483 // Add unread indicator to tab
487484 const tab = document . getElementById ( `tab-${ sessionId } ` ) ;
488485 if ( tab ) {
@@ -494,8 +491,6 @@ function clearUnreadStatus(sessionId: string) {
494491 const session = sessions . get ( sessionId ) ;
495492 if ( ! session ) return ;
496493
497- session . hasUnreadActivity = false ;
498-
499494 // Remove unread indicator from tab
500495 const tab = document . getElementById ( `tab-${ sessionId } ` ) ;
501496 if ( tab ) {
@@ -537,13 +532,6 @@ function switchToSession(sessionId: string) {
537532 // Clear unread status when switching to this session
538533 clearUnreadStatus ( sessionId ) ;
539534
540- // Clear any pending idle timer for this session (Bug 1 fix)
541- const existingTimer = sessionIdleTimers . get ( sessionId ) ;
542- if ( existingTimer ) {
543- clearTimeout ( existingTimer ) ;
544- sessionIdleTimers . delete ( sessionId ) ;
545- }
546-
547535 // Focus and resize
548536 session . terminal . focus ( ) ;
549537 // Dispatch resize event to trigger terminal resize
@@ -575,13 +563,6 @@ function closeSession(sessionId: string) {
575563 // Update UI indicator
576564 updateSessionState ( sessionId , false ) ;
577565
578- // Clean up idle timer (Bug 2 fix)
579- const existingTimer = sessionIdleTimers . get ( sessionId ) ;
580- if ( existingTimer ) {
581- clearTimeout ( existingTimer ) ;
582- sessionIdleTimers . delete ( sessionId ) ;
583- }
584-
585566 // Close PTY in main process
586567 ipcRenderer . send ( "close-session" , sessionId ) ;
587568
@@ -623,13 +604,6 @@ function deleteSession(sessionId: string) {
623604 // Remove from sessions map
624605 sessions . delete ( sessionId ) ;
625606
626- // Clean up idle timer (Bug 2 fix)
627- const existingTimer = sessionIdleTimers . get ( sessionId ) ;
628- if ( existingTimer ) {
629- clearTimeout ( existingTimer ) ;
630- sessionIdleTimers . delete ( sessionId ) ;
631- }
632-
633607 // Delete in main process (handles worktree removal)
634608 ipcRenderer . send ( "delete-session" , sessionId ) ;
635609
@@ -649,10 +623,6 @@ function deleteSession(sessionId: string) {
649623 }
650624}
651625
652- // Track idle timers per session to detect when output stops (Claude is done)
653- const sessionIdleTimers = new Map < string , NodeJS . Timeout > ( ) ;
654- const IDLE_DELAY_MS = 500 ; // 0.5 seconds of no output = Claude is done
655-
656626// Handle session output
657627ipcRenderer . on ( "session-output" , ( _event , sessionId : string , data : string ) => {
658628 const session = sessions . get ( sessionId ) ;
@@ -664,27 +634,10 @@ ipcRenderer.on("session-output", (_event, sessionId: string, data: string) => {
664634 session . terminal . write ( filteredData ) ;
665635
666636 // Only mark as unread if this is not the active session
667- if ( activeSessionId !== sessionId && session . hasActivePty && ! session . hasUnreadActivity ) {
668- // Only track substantive output (ignore cursor movements, keepalives, etc)
669- // Look for actual text content or common escape sequences that indicate real output
670- const hasSubstantiveOutput = / [ a - z A - Z 0 - 9 ] / . test ( filteredData ) ||
671- filteredData . includes ( '\n' ) ||
672- filteredData . includes ( '\r' ) ;
673-
674- if ( hasSubstantiveOutput ) {
675- // Clear any existing idle timer
676- const existingTimer = sessionIdleTimers . get ( sessionId ) ;
677- if ( existingTimer ) {
678- clearTimeout ( existingTimer ) ;
679- }
680-
681- // Set a new timer - if no output for IDLE_DELAY_MS, mark as unread
682- const timer = setTimeout ( ( ) => {
683- markSessionAsUnread ( sessionId ) ;
684- sessionIdleTimers . delete ( sessionId ) ;
685- } , IDLE_DELAY_MS ) ;
686-
687- sessionIdleTimers . set ( sessionId , timer ) ;
637+ if ( activeSessionId !== sessionId && session . hasActivePty ) {
638+ // Check if Claude session is ready for input
639+ if ( isClaudeSessionReady ( filteredData ) ) {
640+ markSessionAsUnread ( sessionId ) ;
688641 }
689642 }
690643 }
0 commit comments