Skip to content

Commit dcc4c5a

Browse files
Try xterm renderer addons for terminal
1 parent 00d78bd commit dcc4c5a

3 files changed

Lines changed: 45 additions & 1 deletion

File tree

bun.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@
9494
"@vercel/analytics": "^1.5.0",
9595
"@vercel/otel": "^2.1.2",
9696
"@vercel/speed-insights": "^1.2.0",
97+
"@xterm/addon-canvas": "^0.7.0",
98+
"@xterm/addon-fit": "^0.11.0",
99+
"@xterm/addon-webgl": "^0.19.0",
97100
"@xterm/xterm": "^6.0.0",
98101
"cheerio": "^1.0.0",
99102
"chrono-node": "^2.8.4",

src/features/dashboard/terminal/use-terminal-instance.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import { CanvasAddon } from '@xterm/addon-canvas'
2+
import { FitAddon } from '@xterm/addon-fit'
3+
import { WebglAddon } from '@xterm/addon-webgl'
14
import '@xterm/xterm/css/xterm.css'
25
import { Terminal as XTerm } from '@xterm/xterm'
36
import { useCallback, useEffect, useRef } from 'react'
@@ -27,12 +30,14 @@ export function useTerminalInstance({
2730
onResize,
2831
}: UseTerminalInstanceOptions) {
2932
const xtermRef = useRef<XTerm | null>(null)
33+
const fitAddonRef = useRef<FitAddon | null>(null)
3034
const terminalContainerRef = useRef<HTMLDivElement | null>(null)
3135
const terminalTranscriptRef = useRef(INITIAL_TERMINAL_TEXT)
3236
const terminalSizeRef = useRef({ cols: DEFAULT_COLS, rows: DEFAULT_ROWS })
3337
const decoderRef = useRef(new TextDecoder())
3438

3539
const resizeTerminal = useCallback(() => {
40+
fitAddonRef.current?.fit()
3641
const nextSize = calculateTerminalSize(
3742
terminalContainerRef.current,
3843
xtermRef.current
@@ -99,26 +104,52 @@ export function useTerminalInstance({
99104
theme: TERMINAL_THEME,
100105
})
101106

107+
const fitAddon = new FitAddon()
108+
let rendererAddon: WebglAddon | CanvasAddon | undefined
109+
110+
try {
111+
rendererAddon = new WebglAddon()
112+
terminal.loadAddon(rendererAddon)
113+
} catch {
114+
try {
115+
rendererAddon = new CanvasAddon()
116+
terminal.loadAddon(rendererAddon)
117+
} catch {
118+
rendererAddon = undefined
119+
}
120+
}
121+
102122
xtermRef.current = terminal
123+
fitAddonRef.current = fitAddon
124+
terminal.loadAddon(fitAddon)
103125
terminal.open(container)
104-
terminal.write(terminalTranscriptRef.current)
105126
const dataSubscription = terminal.onData(onInput)
127+
terminal.write(terminalTranscriptRef.current, () => {
128+
terminal.scrollToBottom()
129+
})
106130

107131
requestAnimationFrame(() => {
108132
resizeTerminal()
109133
terminal.focus()
134+
terminal.scrollToBottom()
110135
})
111136
const resizeTimer = window.setTimeout(() => {
112137
resizeTerminal()
138+
terminal.scrollToBottom()
113139
}, 100)
114140

115141
return () => {
116142
window.clearTimeout(resizeTimer)
117143
dataSubscription.dispose()
144+
rendererAddon?.dispose()
145+
fitAddon.dispose()
118146
terminal.dispose()
119147
if (xtermRef.current === terminal) {
120148
xtermRef.current = null
121149
}
150+
if (fitAddonRef.current === fitAddon) {
151+
fitAddonRef.current = null
152+
}
122153
}
123154
}, [onInput, resizeTerminal])
124155

0 commit comments

Comments
 (0)