@@ -9,14 +9,9 @@ import { parseTerminalServerMessage, resolveTerminalWebSocketUrl } from "./termi
99
1010export type TerminalStatus = "attached" | "connecting" | "error" | "exited"
1111
12- export type TerminalConnectionState = {
13- opened : boolean
14- }
12+ export type TerminalConnectionState = { opened : boolean }
1513
16- type TerminalRuntime = {
17- readonly fitAddon : FitAddon
18- readonly terminal : Terminal
19- }
14+ type TerminalRuntime = { readonly fitAddon : FitAddon ; readonly terminal : Terminal }
2015
2116type TerminalMessageHandlers = {
2217 readonly notifyMessage : ( message : string ) => void
@@ -41,6 +36,33 @@ type TerminalLifecycleArgs = {
4136 readonly setStatus : ( status : TerminalStatus ) => void
4237}
4338
39+ type TerminalSocketListenerArgs = {
40+ readonly connectionRef : { current : TerminalConnectionState }
41+ readonly onClose : ( ) => void
42+ readonly onError : ( ) => void
43+ readonly onMessage : ( payload : string ) => void
44+ readonly onOpen : ( ) => void
45+ readonly socket : WebSocket
46+ }
47+
48+ type TerminalSocketFailureHandlerArgs = {
49+ readonly notifyMessage : ( message : string ) => void
50+ readonly setStatus : ( status : TerminalStatus ) => void
51+ readonly terminal : Terminal
52+ readonly terminalLine : string
53+ readonly uiMessage : string
54+ }
55+
56+ type TerminalSessionSocketArgs = {
57+ readonly connectionRef : { current : TerminalConnectionState }
58+ readonly handlers : TerminalMessageHandlers
59+ readonly notifyMessage : ( message : string ) => void
60+ readonly sendResize : ( ) => void
61+ readonly setStatus : ( status : TerminalStatus ) => void
62+ readonly socket : WebSocket
63+ readonly terminal : Terminal
64+ }
65+
4466const requestSessionClose = ( closePath : string ) : void => {
4567 void Effect . runPromise ( deleteTerminalSessionByPath ( closePath ) . pipe ( Effect . either , Effect . asVoid ) )
4668}
@@ -67,14 +89,7 @@ const createTerminalRuntime = (host: HTMLDivElement): TerminalRuntime => {
6789const createTerminalSocket = (
6890 session : ActiveTerminalSession ,
6991 terminal : Terminal
70- ) : WebSocket =>
71- new WebSocket (
72- resolveTerminalWebSocketUrl (
73- session . websocketPath ,
74- terminal . cols ,
75- terminal . rows
76- )
77- )
92+ ) : WebSocket => new WebSocket ( resolveTerminalWebSocketUrl ( session . websocketPath , terminal . cols , terminal . rows ) )
7893
7994const sendTerminalResize = (
8095 fitAddon : FitAddon ,
@@ -99,9 +114,7 @@ const observeTerminalResize = (
99114 if ( typeof ResizeObserver !== "function" ) {
100115 return null
101116 }
102- const resizeObserver = new ResizeObserver ( ( ) => {
103- onResize ( )
104- } )
117+ const resizeObserver = new ResizeObserver ( onResize )
105118 resizeObserver . observe ( host )
106119 return resizeObserver
107120}
@@ -151,11 +164,7 @@ const handleTerminalServerMessage = (
151164}
152165
153166const attachTerminalSocketListeners = (
154- connectionRef : { current : TerminalConnectionState } ,
155- socket : WebSocket ,
156- onOpen : ( ) => void ,
157- onMessage : ( payload : string ) => void ,
158- onError : ( ) => void
167+ { connectionRef, onClose, onError, onMessage, onOpen, socket } : TerminalSocketListenerArgs
159168) : void => {
160169 socket . addEventListener ( "open" , ( ) => {
161170 connectionRef . current . opened = true
@@ -164,6 +173,11 @@ const attachTerminalSocketListeners = (
164173 socket . addEventListener ( "message" , ( event ) => {
165174 onMessage ( typeof event . data === "string" ? event . data : "" )
166175 } )
176+ socket . addEventListener ( "close" , ( ) => {
177+ if ( ! connectionRef . current . opened ) {
178+ onClose ( )
179+ }
180+ } )
167181 socket . addEventListener ( "error" , onError )
168182}
169183
@@ -192,15 +206,13 @@ const createMessageHandlers = (
192206 terminal
193207} )
194208
195- const createSocketErrorHandler = (
196- notifyMessage : ( message : string ) => void ,
197- setStatus : ( status : TerminalStatus ) => void ,
198- terminal : Terminal
209+ const createSocketFailureHandler = (
210+ { notifyMessage, setStatus, terminal, terminalLine, uiMessage } : TerminalSocketFailureHandlerArgs
199211) =>
200- ( ) => {
201- terminal . writeln ( " \r\n[websocket error]" )
212+ ( ) : void => {
213+ terminal . writeln ( ` \r\n${ terminalLine } ` )
202214 setStatus ( "error" )
203- notifyMessage ( "Terminal websocket error." )
215+ notifyMessage ( uiMessage )
204216}
205217
206218const maybeDeletePendingSession = (
@@ -215,6 +227,33 @@ const maybeDeletePendingSession = (
215227 }
216228}
217229
230+ const attachTerminalSessionSocket = (
231+ { connectionRef, handlers, notifyMessage, sendResize, setStatus, socket, terminal } : TerminalSessionSocketArgs
232+ ) : void => {
233+ attachTerminalSocketListeners ( {
234+ connectionRef,
235+ onClose : createSocketFailureHandler ( {
236+ notifyMessage,
237+ setStatus,
238+ terminal,
239+ terminalLine : "[websocket closed before attach]" ,
240+ uiMessage : "Terminal websocket closed before attach."
241+ } ) ,
242+ onError : createSocketFailureHandler ( {
243+ notifyMessage,
244+ setStatus,
245+ terminal,
246+ terminalLine : "[websocket error]" ,
247+ uiMessage : "Terminal websocket error."
248+ } ) ,
249+ onMessage : ( payload ) => {
250+ handleTerminalServerMessage ( handlers , payload )
251+ } ,
252+ onOpen : sendResize ,
253+ socket
254+ } )
255+ }
256+
218257const mountTerminalSession = (
219258 { connectionRef, hostRef, notifyMessage, session, setStatus } : TerminalLifecycleArgs
220259) : ( ( ) => void ) | undefined => {
@@ -239,15 +278,15 @@ const mountTerminalSession = (
239278 )
240279
241280 globalThis . addEventListener ( "resize" , sendResize )
242- attachTerminalSocketListeners (
281+ attachTerminalSessionSocket ( {
243282 connectionRef,
244- socket ,
283+ handlers,
284+ notifyMessage,
245285 sendResize,
246- ( payload ) => {
247- handleTerminalServerMessage ( handlers , payload )
248- } ,
249- createSocketErrorHandler ( notifyMessage , setStatus , terminal )
250- )
286+ setStatus,
287+ socket,
288+ terminal
289+ } )
251290
252291 return ( ) => {
253292 cleanupTerminalResources ( {
0 commit comments