1
1
import { makeStyles , useTheme } from "@mui/styles" ;
2
2
import { useMachine } from "@xstate/react" ;
3
- import { Stack } from "components/Stack/Stack" ;
4
3
import { FC , useCallback , useEffect , useRef , useState } from "react" ;
5
4
import { Helmet } from "react-helmet-async" ;
6
5
import { useNavigate , useParams , useSearchParams } from "react-router-dom" ;
@@ -18,45 +17,24 @@ import { terminalMachine } from "xServices/terminal/terminalXService";
18
17
import { useProxy } from "contexts/ProxyContext" ;
19
18
import Box from "@mui/material/Box" ;
20
19
import { useDashboard } from "components/Dashboard/DashboardProvider" ;
21
- import { Region , WorkspaceAgent } from "api/typesGenerated" ;
20
+ import { Region } from "api/typesGenerated" ;
22
21
import { getLatencyColor } from "utils/latency" ;
23
22
import Popover from "@mui/material/Popover" ;
24
23
import { ProxyStatusLatency } from "components/ProxyStatusLatency/ProxyStatusLatency" ;
25
- import TerminalPageAlert , { TerminalPageAlertType } from "./TerminalPageAlert" ;
26
24
import { portForwardURL } from "utils/portForward" ;
25
+ import {
26
+ DisconnectedAlert ,
27
+ ErrorScriptAlert ,
28
+ LoadedScriptsAlert ,
29
+ LoadingScriptsAlert ,
30
+ } from "./TerminalAlerts" ;
27
31
28
32
export const Language = {
29
33
workspaceErrorMessagePrefix : "Unable to fetch workspace: " ,
30
34
workspaceAgentErrorMessagePrefix : "Unable to fetch workspace agent: " ,
31
35
websocketErrorMessagePrefix : "WebSocket failed: " ,
32
36
} ;
33
37
34
- const useTerminalWarning = ( { agent } : { agent ?: WorkspaceAgent } ) => {
35
- const lifecycleState = agent ?. lifecycle_state ;
36
- const [ startupWarning , setStartupWarning ] = useState <
37
- TerminalPageAlertType | undefined
38
- > ( undefined ) ;
39
-
40
- useEffect ( ( ) => {
41
- if ( lifecycleState === "start_error" ) {
42
- setStartupWarning ( "error" ) ;
43
- } else if ( lifecycleState === "starting" ) {
44
- setStartupWarning ( "starting" ) ;
45
- } else {
46
- setStartupWarning ( ( prev ) => {
47
- if ( prev === "starting" ) {
48
- return "success" ;
49
- }
50
- return undefined ;
51
- } ) ;
52
- }
53
- } , [ lifecycleState ] ) ;
54
-
55
- return {
56
- startupWarning,
57
- } ;
58
- } ;
59
-
60
38
type TerminalPageProps = React . PropsWithChildren < {
61
39
renderer : "webgl" | "dom" ;
62
40
} > ;
@@ -110,16 +88,18 @@ const TerminalPage: FC<TerminalPageProps> = ({ renderer }) => {
110
88
workspaceAgent,
111
89
websocketError,
112
90
} = terminalState . context ;
113
- const reloading = useReloading ( isDisconnected ) ;
114
91
const dashboard = useDashboard ( ) ;
115
92
const proxyContext = useProxy ( ) ;
116
93
const selectedProxy = proxyContext . proxy . proxy ;
117
94
const latency = selectedProxy
118
95
? proxyContext . proxyLatencies [ selectedProxy . id ]
119
96
: undefined ;
120
- const { startupWarning } = useTerminalWarning ( {
121
- agent : workspaceAgent ,
122
- } ) ;
97
+
98
+ const lifecycleState = workspaceAgent ?. lifecycle_state ;
99
+ const prevLifecycleState = useRef ( lifecycleState ) ;
100
+ useEffect ( ( ) => {
101
+ prevLifecycleState . current = lifecycleState ;
102
+ } , [ lifecycleState ] ) ;
123
103
124
104
// handleWebLink handles opening of URLs in the terminal!
125
105
const handleWebLink = useCallback (
@@ -326,29 +306,12 @@ const TerminalPage: FC<TerminalPageProps> = ({ renderer }) => {
326
306
: "" }
327
307
</ title >
328
308
</ Helmet >
329
- { /* This overlay makes it more obvious that the terminal is disconnected. */ }
330
- { /* It's nice for situations where Coder restarts, and they are temporarily disconnected. */ }
331
- < div className = { `${ styles . overlay } ${ isDisconnected ? "" : "connected" } ` } >
332
- { reloading . status === "reloading" ? (
333
- < span className = { styles . overlayText } > Reloading...</ span >
334
- ) : (
335
- < Stack spacing = { 0.5 } alignItems = "center" >
336
- < span className = { styles . overlayText } > Disconnected</ span >
337
- < span className = { styles . overlaySubtext } >
338
- Press any key to retry
339
- </ span >
340
- </ Stack >
341
- ) }
342
- </ div >
343
309
< Box display = "flex" flexDirection = "column" height = "100vh" >
344
- { startupWarning && (
345
- < TerminalPageAlert
346
- alertType = { startupWarning }
347
- onDismiss = { ( ) => {
348
- fitAddon ?. fit ( ) ;
349
- } }
350
- />
351
- ) }
310
+ { lifecycleState === "start_error" && < ErrorScriptAlert /> }
311
+ { lifecycleState === "starting" && < LoadingScriptsAlert /> }
312
+ { lifecycleState === "ready" &&
313
+ prevLifecycleState . current === "starting" && < LoadedScriptsAlert /> }
314
+ { isDisconnected && < DisconnectedAlert /> }
352
315
< div
353
316
className = { styles . terminal }
354
317
ref = { xtermRef }
@@ -463,61 +426,6 @@ const BottomBar = ({ proxy, latency }: { proxy: Region; latency?: number }) => {
463
426
) ;
464
427
} ;
465
428
466
- const useReloading = ( isDisconnected : boolean ) => {
467
- const [ status , setStatus ] = useState < "reloading" | "notReloading" > (
468
- "notReloading" ,
469
- ) ;
470
-
471
- // Retry connection on key press when it is disconnected
472
- useEffect ( ( ) => {
473
- if ( ! isDisconnected || status === "reloading" ) {
474
- return ;
475
- }
476
-
477
- // Modifier keys should not trigger a reload.
478
- const ignoredKeys = [
479
- "Alt" ,
480
- "AltGraph" ,
481
- "CapsLock" ,
482
- "Control" ,
483
- "Fn" ,
484
- "FnLock" ,
485
- "Meta" ,
486
- "NumLock" ,
487
- "ScrollLock" ,
488
- "Shift" ,
489
- "Symbol" ,
490
- "SymbolLock" ,
491
- ] ;
492
-
493
- const keyDownHandler = ( event : KeyboardEvent ) => {
494
- // In addition to ignored keys, avoid reloading while modifiers are held
495
- // to cover cases where the terminal unexpectedly tries to reconnect like
496
- // when pressing ctrl+w, ctrl+r, and so on.
497
- if (
498
- ! ignoredKeys . includes ( event . key ) &&
499
- ! event . altKey &&
500
- ! event . ctrlKey &&
501
- ! event . metaKey &&
502
- ! event . shiftKey
503
- ) {
504
- setStatus ( "reloading" ) ;
505
- window . location . reload ( ) ;
506
- }
507
- } ;
508
-
509
- document . addEventListener ( "keydown" , keyDownHandler , true ) ;
510
-
511
- return ( ) => {
512
- document . removeEventListener ( "keydown" , keyDownHandler , true ) ;
513
- } ;
514
- } , [ status , isDisconnected ] ) ;
515
-
516
- return {
517
- status,
518
- } ;
519
- } ;
520
-
521
429
const useStyles = makeStyles ( ( theme ) => ( {
522
430
overlay : {
523
431
position : "absolute" ,
0 commit comments