@@ -8,13 +8,23 @@ import { Skeleton } from "@material-ui/lab"
8
8
import { useMachine } from "@xstate/react"
9
9
import { AppLinkSkeleton } from "components/AppLink/AppLinkSkeleton"
10
10
import { Maybe } from "components/Conditionals/Maybe"
11
- import { Line , Logs } from "components/Logs/Logs"
11
+ import { LogLine , logLineHeight } from "components/Logs/Logs"
12
12
import { PortForwardButton } from "components/PortForwardButton/PortForwardButton"
13
13
import { VSCodeDesktopButton } from "components/VSCodeDesktopButton/VSCodeDesktopButton"
14
- import { FC , useEffect , useMemo , useRef , useState } from "react"
14
+ import {
15
+ FC ,
16
+ useCallback ,
17
+ useEffect ,
18
+ useLayoutEffect ,
19
+ useMemo ,
20
+ useRef ,
21
+ useState
22
+ } from "react"
15
23
import { useTranslation } from "react-i18next"
16
24
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
17
25
import { darcula } from "react-syntax-highlighter/dist/cjs/styles/prism"
26
+ import AutoSizer from "react-virtualized-auto-sizer"
27
+ import { FixedSizeList as List , ListOnScrollProps } from "react-window"
18
28
import { workspaceAgentLogsMachine } from "xServices/workspaceAgentLogs/workspaceAgentLogsXService"
19
29
import { Workspace , WorkspaceAgent } from "../../api/typesGenerated"
20
30
import { AppLink } from "../AppLink/AppLink"
@@ -70,24 +80,49 @@ export const AgentRow: FC<AgentRowProps> = ({
70
80
sendLogsEvent ( "FETCH_STARTUP_LOGS" )
71
81
}
72
82
} , [ sendLogsEvent , showStartupLogs ] )
83
+ const logListRef = useRef < List > ( null )
84
+ const logListDivRef = useRef < HTMLDivElement > ( null )
73
85
const startupLogs = useMemo ( ( ) => {
74
- const logs =
75
- logsMachine . context . startupLogs ?. map (
76
- ( log ) : Line => ( {
77
- level : "info" ,
78
- output : log . output ,
79
- time : log . created_at ,
80
- } ) ,
81
- ) || [ ]
86
+ const allLogs = logsMachine . context . startupLogs || [ ]
87
+
88
+ const logs = [ ...allLogs ]
82
89
if ( agent . startup_logs_overflowed ) {
83
90
logs . push ( {
91
+ id : - 1 ,
84
92
level : "error" ,
85
93
output : "Startup logs exceeded the max size of 1MB!" ,
86
94
time : new Date ( ) . toISOString ( ) ,
87
95
} )
88
96
}
89
97
return logs
90
98
} , [ logsMachine . context . startupLogs , agent . startup_logs_overflowed ] )
99
+ const [ bottomOfLogs , setBottomOfLogs ] = useState ( true )
100
+ useLayoutEffect ( ( ) => {
101
+ if ( bottomOfLogs && logListRef . current ) {
102
+ logListRef . current . scrollToItem ( startupLogs . length - 1 , "end" )
103
+ }
104
+ } , [ showStartupLogs , startupLogs , logListRef , bottomOfLogs ] )
105
+ const handleLogScroll = useCallback (
106
+ ( props : ListOnScrollProps ) => {
107
+ if (
108
+ props . scrollOffset === 0 ||
109
+ props . scrollUpdateWasRequested ||
110
+ ! logListDivRef . current
111
+ ) {
112
+ return
113
+ }
114
+ // The parent holds the height of the list!
115
+ const parent = logListDivRef . current . parentElement
116
+ if ( ! parent ) {
117
+ return
118
+ }
119
+ const distanceFromBottom =
120
+ logListDivRef . current . scrollHeight -
121
+ ( props . scrollOffset + parent . clientHeight )
122
+ setBottomOfLogs ( distanceFromBottom < logLineHeight )
123
+ } ,
124
+ [ logListDivRef ] ,
125
+ )
91
126
92
127
return (
93
128
< Stack
@@ -269,8 +304,30 @@ export const AgentRow: FC<AgentRowProps> = ({
269
304
) }
270
305
</ Stack >
271
306
</ Stack >
307
+
272
308
{ showStartupLogs && (
273
- < Logs className = { styles . startupLogs } lineNumbers lines = { startupLogs } />
309
+ < AutoSizer disableHeight >
310
+ { ( { width } ) => (
311
+ < List
312
+ ref = { logListRef }
313
+ innerRef = { logListDivRef }
314
+ height = { 256 }
315
+ itemCount = { startupLogs . length }
316
+ itemSize = { logLineHeight }
317
+ width = { width }
318
+ className = { styles . startupLogs }
319
+ onScroll = { handleLogScroll }
320
+ >
321
+ { ( { index, style } ) => (
322
+ < LogLine
323
+ line = { startupLogs [ index ] }
324
+ number = { index + 1 }
325
+ style = { style }
326
+ />
327
+ ) }
328
+ </ List >
329
+ ) }
330
+ </ AutoSizer >
274
331
) }
275
332
</ Stack >
276
333
)
@@ -312,8 +369,7 @@ const useStyles = makeStyles((theme) => ({
312
369
313
370
startupLogs : {
314
371
maxHeight : 256 ,
315
- display : "flex" ,
316
- flexDirection : "column-reverse" ,
372
+ background : theme . palette . background . default ,
317
373
} ,
318
374
319
375
startupScriptPopover : {
0 commit comments