@@ -183,22 +183,12 @@ fn terminate_terminal_runtime(runtime: Arc<TerminalRuntime>) {
183183 }
184184 }
185185 // Then close the writer end so no further input can be sent.
186- // For PTY, we drop the sender which signals the writer thread to exit .
187- // For TmuxAttached, we take the writer handle .
186+ // Writer thread will exit when the channel is closed (sender dropped) .
187+ // No explicit action needed; dropping the runtime closes the channel .
188188 match & runtime. io {
189- TerminalIo :: Pty { .. } => {
190- // Writer thread will exit when the channel is closed (sender dropped).
191- // No explicit action needed; dropping the runtime closes the channel.
192- }
193- TerminalIo :: TmuxAttached { writer, .. } => {
194- if let Ok ( mut writer) = writer. lock ( ) {
195- writer. take ( ) ;
196- }
197- }
189+ TerminalIo :: Pty { .. } => { }
198190 #[ cfg( test) ]
199191 TerminalIo :: Mock => { }
200- #[ cfg( not( test) ) ]
201- _ => { }
202192 }
203193}
204194
@@ -215,12 +205,6 @@ pub(crate) enum TerminalBridgeTarget {
215205 cols : Option < u16 > ,
216206 rows : Option < u16 > ,
217207 } ,
218- Tmux {
219- session_name : String ,
220- pane_id : String ,
221- cols : Option < u16 > ,
222- rows : Option < u16 > ,
223- } ,
224208}
225209
226210pub ( crate ) struct TerminalCreateOptions {
@@ -545,176 +529,6 @@ fn create_pty_terminal_runtime(
545529 Ok ( runtime)
546530}
547531
548- fn create_tmux_terminal_runtime (
549- terminal_id : u64 ,
550- workspace_id : & str ,
551- session_name : & str ,
552- pane_id : & str ,
553- cols : Option < u16 > ,
554- rows : Option < u16 > ,
555- options : TerminalCreateOptions ,
556- app : & AppHandle ,
557- _state : State < ' _ , AppState > ,
558- ) -> Result < Arc < TerminalRuntime > , String > {
559- let attach_runtime = crate :: services:: tmux:: attach_tmux_session ( session_name, cols, rows) ?;
560- let reader = attach_runtime
561- . pair
562- . master
563- . try_clone_reader ( )
564- . map_err ( |e| e. to_string ( ) ) ?;
565- let writer = attach_runtime
566- . pair
567- . master
568- . take_writer ( )
569- . map_err ( |e| e. to_string ( ) ) ?;
570-
571- let runtime = Arc :: new ( TerminalRuntime {
572- io : TerminalIo :: TmuxAttached {
573- session_name : session_name. to_string ( ) ,
574- pane_id : pane_id. to_string ( ) ,
575- writer : Mutex :: new ( Some ( writer) ) ,
576- master : Mutex :: new ( attach_runtime. pair . master ) ,
577- } ,
578- output : Mutex :: new ( String :: new ( ) ) ,
579- size : Mutex :: new ( ( 80 , 24 ) ) ,
580- persist_workspace_terminal : options. persist_workspace_terminal ,
581- child : Some ( attach_runtime. child ) ,
582- killer : Some ( attach_runtime. killer ) ,
583- process_id : None ,
584- process_group_leader : None ,
585- } ) ;
586-
587- let app_handle = app. clone ( ) ;
588- let state_handle = app. clone ( ) ;
589- let runtime_out = runtime. clone ( ) ;
590- let workspace_id_out = workspace_id. to_string ( ) ;
591- std:: thread:: spawn ( move || {
592- let mut reader = reader;
593- let mut buf = [ 0u8 ; 4096 ] ;
594- let mut decoder = Utf8StreamDecoder :: new ( ) ;
595- loop {
596- match reader. read ( & mut buf) {
597- Ok ( 0 ) => {
598- let text = decoder. finish ( ) ;
599- let state: State < AppState > = state_handle. state ( ) ;
600- emit_runtime_output (
601- & runtime_out,
602- & app_handle,
603- state,
604- & workspace_id_out,
605- terminal_id,
606- & text,
607- ) ;
608- break ;
609- }
610- Ok ( n) => {
611- let text = decoder. push ( & buf[ ..n] ) ;
612- if text. is_empty ( ) {
613- continue ;
614- }
615- let state: State < AppState > = state_handle. state ( ) ;
616- emit_runtime_output (
617- & runtime_out,
618- & app_handle,
619- state,
620- & workspace_id_out,
621- terminal_id,
622- & text,
623- ) ;
624- }
625- Err ( err) if err. kind ( ) == std:: io:: ErrorKind :: WouldBlock => {
626- // Non-blocking read with no data available: sleep and retry.
627- std:: thread:: sleep ( std:: time:: Duration :: from_millis ( 10 ) ) ;
628- continue ;
629- }
630- Err ( err) => {
631- let text = decoder. finish ( ) ;
632- let state: State < AppState > = state_handle. state ( ) ;
633- if !text. is_empty ( ) {
634- emit_runtime_output (
635- & runtime_out,
636- & app_handle,
637- state,
638- & workspace_id_out,
639- terminal_id,
640- & text,
641- ) ;
642- }
643- let state: State < AppState > = state_handle. state ( ) ;
644- let error_msg = format ! ( "\n [terminal error: read failed: {err}]\n " ) ;
645- emit_runtime_output (
646- & runtime_out,
647- & app_handle,
648- state,
649- & workspace_id_out,
650- terminal_id,
651- & error_msg,
652- ) ;
653- break ;
654- }
655- }
656- }
657- } ) ;
658-
659- let app_handle = app. clone ( ) ;
660- let state_handle = app. clone ( ) ;
661- let runtime_out = runtime. clone ( ) ;
662- let workspace_id_out = workspace_id. to_string ( ) ;
663- let key = terminal_key ( workspace_id, terminal_id) ;
664- std:: thread:: spawn ( move || {
665- let exit_text = match & runtime_out. child {
666- Some ( child) => match child. lock ( ) {
667- Ok ( mut child) => format_terminal_exit_message ( child. wait ( ) ) ,
668- Err ( error) => {
669- format ! ( "\n [terminal exited: failed to lock child handle: {error}]\n " )
670- }
671- } ,
672- None => "\n [terminal exited]\n " . to_string ( ) ,
673- } ;
674- let state: State < AppState > = state_handle. state ( ) ;
675- emit_runtime_output (
676- & runtime_out,
677- & app_handle,
678- state,
679- & workspace_id_out,
680- terminal_id,
681- & exit_text,
682- ) ;
683- let state: State < AppState > = state_handle. state ( ) ;
684- if let Ok ( Some ( ( binding_workspace_id, session_id) ) ) =
685- crate :: services:: session_runtime:: session_runtime_binding_for_terminal (
686- terminal_id,
687- state,
688- )
689- {
690- if binding_workspace_id == workspace_id_out {
691- let _ = crate :: services:: session_runtime:: remove_terminal_runtime_registration (
692- & workspace_id_out,
693- & session_id,
694- state,
695- ) ;
696- }
697- }
698- if runtime_out. persist_workspace_terminal {
699- let _ =
700- set_workspace_terminal_recoverable ( state, & workspace_id_out, terminal_id, false ) ;
701- }
702- sync_bound_terminal_runtime_state (
703- & workspace_id_out,
704- terminal_id,
705- SessionStatus :: Interrupted ,
706- false ,
707- Some ( SessionRuntimeLiveness :: ProviderExited ) ,
708- state,
709- ) ;
710- if let Ok ( mut terms) = state. terminals . lock ( ) {
711- terms. remove ( & key) ;
712- }
713- } ) ;
714-
715- Ok ( runtime)
716- }
717-
718532pub ( crate ) fn create_terminal_runtime (
719533 workspace_id : & str ,
720534 _cwd : & str ,
@@ -727,40 +541,23 @@ pub(crate) fn create_terminal_runtime(
727541) -> Result < TerminalInfo , String > {
728542 let terminal_id = next_terminal_id ( state) ?;
729543 let bridge_target = options. bridge_target . clone ( ) ;
730- let runtime = match bridge_target {
731- TerminalBridgeTarget :: Pty {
732- cwd,
733- target,
734- cols,
735- rows,
736- } => create_pty_terminal_runtime (
737- terminal_id,
738- workspace_id,
739- & cwd,
740- & target,
741- cols,
742- rows,
743- options,
744- app,
745- state,
746- ) ?,
747- TerminalBridgeTarget :: Tmux {
748- session_name,
749- pane_id,
750- cols,
751- rows,
752- } => create_tmux_terminal_runtime (
753- terminal_id,
754- workspace_id,
755- & session_name,
756- & pane_id,
757- cols,
758- rows,
759- options,
760- app,
761- state,
762- ) ?,
763- } ;
544+ let TerminalBridgeTarget :: Pty {
545+ cwd,
546+ target,
547+ cols,
548+ rows,
549+ } = bridge_target;
550+ let runtime = create_pty_terminal_runtime (
551+ terminal_id,
552+ workspace_id,
553+ & cwd,
554+ & target,
555+ cols,
556+ rows,
557+ options,
558+ app,
559+ state,
560+ ) ?;
764561
765562 if runtime. persist_workspace_terminal {
766563 if let Err ( error) = persist_workspace_terminal ( state, workspace_id, terminal_id, "" , true ) {
@@ -873,21 +670,8 @@ pub(crate) fn terminal_write(
873670 }
874671 }
875672 }
876- TerminalIo :: TmuxAttached { writer, .. } => {
877- let mut writer = writer. lock ( ) . map_err ( |e| e. to_string ( ) ) ?;
878- if let Some ( handle) = writer. as_mut ( ) {
879- handle
880- . write_all ( decorated_input. as_bytes ( ) )
881- . map_err ( |e| e. to_string ( ) ) ?;
882- handle. flush ( ) . map_err ( |e| e. to_string ( ) ) ?;
883- } else {
884- return Err ( "terminal_stdin_closed" . to_string ( ) ) ;
885- }
886- }
887673 #[ cfg( test) ]
888674 TerminalIo :: Mock => { }
889- #[ cfg( not( test) ) ]
890- _ => { }
891675 }
892676 #[ cfg( test) ]
893677 state
@@ -944,7 +728,7 @@ pub(crate) fn terminal_resize(
944728 let terms = state. terminals . lock ( ) . map_err ( |e| e. to_string ( ) ) ?;
945729 let runtime = terms. get ( & key) . ok_or ( "terminal_not_found" ) ?. clone ( ) ;
946730 match & runtime. io {
947- TerminalIo :: Pty { master, .. } | TerminalIo :: TmuxAttached { master , .. } => {
731+ TerminalIo :: Pty { master, .. } => {
948732 let master = master. lock ( ) . map_err ( |e| e. to_string ( ) ) ?;
949733 master
950734 . resize ( PtySize {
@@ -957,9 +741,10 @@ pub(crate) fn terminal_resize(
957741 }
958742 #[ cfg( test) ]
959743 TerminalIo :: Mock => Ok ( ( ) ) ,
744+ #[ cfg( not( test) ) ]
960745 _ => {
961746 eprintln ! ( "warning: terminal_resize: unknown TerminalIo variant, skipping" ) ;
962- return Ok ( ( ) ) ;
747+ Ok ( ( ) )
963748 }
964749 }
965750}
@@ -976,14 +761,7 @@ pub(crate) fn terminal_close(
976761 } ;
977762
978763 if let Some ( runtime) = runtime {
979- let tmux_session = match & runtime. io {
980- TerminalIo :: TmuxAttached { session_name, .. } => Some ( session_name. clone ( ) ) ,
981- _ => None ,
982- } ;
983764 terminate_terminal_runtime ( runtime) ;
984- if let Some ( session_name) = tmux_session {
985- let _ = crate :: services:: tmux:: kill_tmux_session ( & session_name) ;
986- }
987765 }
988766 let is_bound_session_terminal =
989767 crate :: services:: session_runtime:: session_runtime_binding_for_terminal ( terminal_id, state) ?
@@ -1026,14 +804,7 @@ pub(crate) fn close_workspace_terminals(workspace_id: &str, state: State<'_, App
1026804 } ;
1027805
1028806 for ( terminal_id, runtime) in runtimes {
1029- let tmux_session = match & runtime. io {
1030- TerminalIo :: TmuxAttached { session_name, .. } => Some ( session_name. clone ( ) ) ,
1031- _ => None ,
1032- } ;
1033807 terminate_terminal_runtime ( runtime) ;
1034- if let Some ( session_name) = tmux_session {
1035- let _ = crate :: services:: tmux:: kill_tmux_session ( & session_name) ;
1036- }
1037808 sync_bound_terminal_runtime_state (
1038809 workspace_id,
1039810 terminal_id,
0 commit comments