11import React , { useState , useEffect } from "react"
2- import { Box , Text , useApp , useStdout } from "ink"
2+ import { Box , Text , useApp , useStdout , useInput } from "ink"
33import { createRequire } from "module"
44import { TmuxService } from "./services/TmuxService.js"
55
@@ -60,6 +60,8 @@ import LoadingIndicator from "./components/indicators/LoadingIndicator.js"
6060import RunningIndicator from "./components/indicators/RunningIndicator.js"
6161import UpdatingIndicator from "./components/indicators/UpdatingIndicator.js"
6262import FooterHelp from "./components/ui/FooterHelp.js"
63+ import TmuxHooksPromptDialog from "./components/dialogs/TmuxHooksPromptDialog.js"
64+ import { PaneEventService } from "./services/PaneEventService.js"
6365
6466const DmuxApp : React . FC < DmuxAppProps > = ( {
6567 panesFile,
@@ -153,6 +155,12 @@ const DmuxApp: React.FC<DmuxAppProps> = ({
153155 const [ toastQueueLength , setToastQueueLength ] = useState ( 0 )
154156 const [ toastQueuePosition , setToastQueuePosition ] = useState < number | null > ( null )
155157
158+ // Tmux hooks prompt state
159+ const [ showHooksPrompt , setShowHooksPrompt ] = useState ( false )
160+ const [ hooksPromptIndex , setHooksPromptIndex ] = useState ( 0 )
161+ // undefined = not yet determined, true = use hooks, false = use polling
162+ const [ useHooks , setUseHooks ] = useState < boolean | undefined > ( undefined )
163+
156164 // Subscribe to StateManager for unread error/warning count and toast updates
157165 useEffect ( ( ) => {
158166 const stateManager = StateManager . getInstance ( )
@@ -178,11 +186,46 @@ const DmuxApp: React.FC<DmuxAppProps> = ({
178186 } , [ ] )
179187
180188 // Panes state and persistence (skipLoading will be updated after actionSystem is initialized)
181- const { panes, setPanes, isLoading, loadPanes, savePanes } = usePanes (
189+ const { panes, setPanes, isLoading, loadPanes, savePanes, eventMode } = usePanes (
182190 panesFile ,
183- false
191+ false ,
192+ sessionName ,
193+ controlPaneId ,
194+ useHooks
184195 )
185196
197+ // Check for tmux hooks preference on startup
198+ useEffect ( ( ) => {
199+ const checkHooksPreference = async ( ) => {
200+ // Check if user already has a preference
201+ const settings = settingsManager . getSettings ( )
202+
203+ if ( settings . useTmuxHooks !== undefined ) {
204+ // User has already decided
205+ setUseHooks ( settings . useTmuxHooks )
206+ return
207+ }
208+
209+ // Check if hooks are already installed (from previous session)
210+ const paneEventService = PaneEventService . getInstance ( )
211+ paneEventService . initialize ( { sessionName, controlPaneId } )
212+
213+ const hooksInstalled = await paneEventService . canUseHooks ( )
214+
215+ if ( hooksInstalled ) {
216+ // Hooks already installed, use them automatically
217+ setUseHooks ( true )
218+ // Save the preference
219+ settingsManager . updateSetting ( 'useTmuxHooks' , true , 'global' )
220+ } else {
221+ // Need to ask user - show prompt
222+ setShowHooksPrompt ( true )
223+ }
224+ }
225+
226+ checkHooksPreference ( )
227+ } , [ sessionName , controlPaneId , settingsManager ] )
228+
186229 // Pane lifecycle manager - handles locking to prevent race conditions
187230 // Replaces the old timeout-based intentionallyClosedPanes Set
188231 const lifecycleManager = React . useMemo ( ( ) => PaneLifecycleManager . getInstance ( ) , [ ] )
@@ -725,6 +768,36 @@ const DmuxApp: React.FC<DmuxAppProps> = ({
725768 } , 100 )
726769 }
727770
771+ // Handle tmux hooks prompt input
772+ useInput (
773+ ( input , key ) => {
774+ if ( ! showHooksPrompt ) return
775+
776+ if ( key . upArrow || input === 'k' ) {
777+ setHooksPromptIndex ( Math . max ( 0 , hooksPromptIndex - 1 ) )
778+ } else if ( key . downArrow || input === 'j' ) {
779+ setHooksPromptIndex ( Math . min ( 1 , hooksPromptIndex + 1 ) )
780+ } else if ( input === 'y' ) {
781+ // Yes - install hooks
782+ setShowHooksPrompt ( false )
783+ setUseHooks ( true )
784+ settingsManager . updateSetting ( 'useTmuxHooks' , true , 'global' )
785+ } else if ( input === 'n' ) {
786+ // No - use polling
787+ setShowHooksPrompt ( false )
788+ setUseHooks ( false )
789+ settingsManager . updateSetting ( 'useTmuxHooks' , false , 'global' )
790+ } else if ( key . return ) {
791+ // Select current option
792+ setShowHooksPrompt ( false )
793+ const selected = hooksPromptIndex === 0
794+ setUseHooks ( selected )
795+ settingsManager . updateSetting ( 'useTmuxHooks' , selected , 'global' )
796+ }
797+ } ,
798+ { isActive : showHooksPrompt }
799+ )
800+
728801 // Input handling - extracted to dedicated hook
729802 useInputHandling ( {
730803 panes,
@@ -735,7 +808,7 @@ const DmuxApp: React.FC<DmuxAppProps> = ({
735808 runningCommand,
736809 isUpdating,
737810 isLoading,
738- ignoreInput,
811+ ignoreInput : ignoreInput || showHooksPrompt , // Block other input when hooks prompt is shown
739812 quitConfirmMode,
740813 setQuitConfirmMode,
741814 showCommandPrompt,
@@ -854,6 +927,11 @@ const DmuxApp: React.FC<DmuxAppProps> = ({
854927 { runningCommand && < RunningIndicator /> }
855928
856929 { isUpdating && < UpdatingIndicator /> }
930+
931+ { /* Tmux hooks prompt - shown on first startup */ }
932+ { showHooksPrompt && (
933+ < TmuxHooksPromptDialog selectedIndex = { hooksPromptIndex } />
934+ ) }
857935 </ Box >
858936
859937 { /* Status messages - only render when present */ }
0 commit comments