1
+ import Button from "@material-ui/core/Button"
1
2
import { makeStyles } from "@material-ui/core/styles"
3
+ import WarningIcon from "@material-ui/icons/ErrorOutlineRounded"
4
+ import RefreshOutlined from "@material-ui/icons/RefreshOutlined"
2
5
import { useMachine } from "@xstate/react"
3
6
import { portForwardURL } from "components/PortForwardButton/PortForwardButton"
4
7
import { Stack } from "components/Stack/Stack"
@@ -15,41 +18,14 @@ import { MONOSPACE_FONT_FAMILY } from "../../theme/constants"
15
18
import { pageTitle } from "../../utils/page"
16
19
import { terminalMachine } from "../../xServices/terminal/terminalXService"
17
20
import { useProxy } from "contexts/ProxyContext"
21
+ import { combineClasses } from "utils/combineClasses"
18
22
19
23
export const Language = {
20
24
workspaceErrorMessagePrefix : "Unable to fetch workspace: " ,
21
25
workspaceAgentErrorMessagePrefix : "Unable to fetch workspace agent: " ,
22
26
websocketErrorMessagePrefix : "WebSocket failed: " ,
23
27
}
24
28
25
- const useReloading = ( isDisconnected : boolean ) => {
26
- const [ status , setStatus ] = useState < "reloading" | "notReloading" > (
27
- "notReloading" ,
28
- )
29
-
30
- // Retry connection on key press when it is disconnected
31
- useEffect ( ( ) => {
32
- if ( ! isDisconnected ) {
33
- return
34
- }
35
-
36
- const keyDownHandler = ( ) => {
37
- setStatus ( "reloading" )
38
- window . location . reload ( )
39
- }
40
-
41
- document . addEventListener ( "keydown" , keyDownHandler )
42
-
43
- return ( ) => {
44
- document . removeEventListener ( "keydown" , keyDownHandler )
45
- }
46
- } , [ isDisconnected ] )
47
-
48
- return {
49
- status,
50
- }
51
- }
52
-
53
29
const TerminalPage : FC <
54
30
React . PropsWithChildren < {
55
31
readonly renderer ?: XTerm . RendererType
@@ -102,6 +78,12 @@ const TerminalPage: FC<
102
78
websocketError,
103
79
} = terminalState . context
104
80
const reloading = useReloading ( isDisconnected )
81
+ const shouldDisplayStartupWarning = workspaceAgent
82
+ ? [ "starting" , "starting_timeout" ] . includes ( workspaceAgent . lifecycle_state )
83
+ : false
84
+ const shouldDisplayStartupError = workspaceAgent
85
+ ? workspaceAgent . lifecycle_state === "start_error"
86
+ : false
105
87
106
88
// handleWebLink handles opening of URLs in the terminal!
107
89
const handleWebLink = useCallback (
@@ -316,12 +298,80 @@ const TerminalPage: FC<
316
298
</ Stack >
317
299
) }
318
300
</ div >
301
+ { shouldDisplayStartupError && (
302
+ < div
303
+ className = { combineClasses ( [ styles . alert , styles . alertError ] ) }
304
+ role = "alert"
305
+ >
306
+ < WarningIcon className = { styles . alertIcon } />
307
+ < div >
308
+ < div className = { styles . alertTitle } > Startup script failed</ div >
309
+ < div className = { styles . alertMessage } >
310
+ You can continue using this terminal, but something may be missing
311
+ or not fully set up.
312
+ </ div >
313
+ </ div >
314
+ </ div >
315
+ ) }
316
+ { shouldDisplayStartupWarning && (
317
+ < div className = { styles . alert } role = "alert" >
318
+ < WarningIcon className = { styles . alertIcon } />
319
+ < div >
320
+ < div className = { styles . alertTitle } >
321
+ Startup script is still running
322
+ </ div >
323
+ < div className = { styles . alertMessage } >
324
+ You can continue using this terminal, but something may be missing
325
+ or not fully set up.
326
+ </ div >
327
+ </ div >
328
+ < div className = { styles . alertActions } >
329
+ < Button
330
+ startIcon = { < RefreshOutlined /> }
331
+ size = "small"
332
+ onClick = { ( ) => {
333
+ // By redirecting the user without the session in the URL we
334
+ // create a new one
335
+ window . location . href = window . location . pathname
336
+ } }
337
+ >
338
+ Refresh session
339
+ </ Button >
340
+ </ div >
341
+ </ div >
342
+ ) }
319
343
< div className = { styles . terminal } ref = { xtermRef } data-testid = "terminal" />
320
344
</ >
321
345
)
322
346
}
323
347
324
- export default TerminalPage
348
+ const useReloading = ( isDisconnected : boolean ) => {
349
+ const [ status , setStatus ] = useState < "reloading" | "notReloading" > (
350
+ "notReloading" ,
351
+ )
352
+
353
+ // Retry connection on key press when it is disconnected
354
+ useEffect ( ( ) => {
355
+ if ( ! isDisconnected ) {
356
+ return
357
+ }
358
+
359
+ const keyDownHandler = ( ) => {
360
+ setStatus ( "reloading" )
361
+ window . location . reload ( )
362
+ }
363
+
364
+ document . addEventListener ( "keydown" , keyDownHandler )
365
+
366
+ return ( ) => {
367
+ document . removeEventListener ( "keydown" , keyDownHandler )
368
+ }
369
+ } , [ isDisconnected ] )
370
+
371
+ return {
372
+ status,
373
+ }
374
+ }
325
375
326
376
const useStyles = makeStyles ( ( theme ) => ( {
327
377
overlay : {
@@ -355,6 +405,8 @@ const useStyles = makeStyles((theme) => ({
355
405
width : "100vw" ,
356
406
height : "100vh" ,
357
407
overflow : "hidden" ,
408
+ padding : theme . spacing ( 1 ) ,
409
+ backgroundColor : theme . palette . background . paper ,
358
410
// These styles attempt to mimic the VS Code scrollbar.
359
411
"& .xterm" : {
360
412
padding : 4 ,
@@ -377,4 +429,34 @@ const useStyles = makeStyles((theme) => ({
377
429
backgroundColor : "rgba(255, 255, 255, 0.18)" ,
378
430
} ,
379
431
} ,
432
+ alert : {
433
+ display : "flex" ,
434
+ background : theme . palette . background . paperLight ,
435
+ alignItems : "center" ,
436
+ padding : theme . spacing ( 2 ) ,
437
+ gap : theme . spacing ( 2 ) ,
438
+ borderBottom : `1px solid ${ theme . palette . divider } ` ,
439
+ } ,
440
+ alertIcon : {
441
+ color : theme . palette . warning . light ,
442
+ fontSize : theme . spacing ( 3 ) ,
443
+ } ,
444
+ alertError : {
445
+ "& $alertIcon" : {
446
+ color : theme . palette . error . light ,
447
+ } ,
448
+ } ,
449
+ alertTitle : {
450
+ fontWeight : 600 ,
451
+ color : theme . palette . text . primary ,
452
+ } ,
453
+ alertMessage : {
454
+ fontSize : 14 ,
455
+ color : theme . palette . text . secondary ,
456
+ } ,
457
+ alertActions : {
458
+ marginLeft : "auto" ,
459
+ } ,
380
460
} ) )
461
+
462
+ export default TerminalPage
0 commit comments