Skip to content

Commit 5bd0d73

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

3 files changed

Lines changed: 48 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: 35 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,55 @@ export function useTerminalInstance({
99104
theme: TERMINAL_THEME,
100105
})
101106

107+
const fitAddon = new FitAddon()
108+
let rendererAddon: WebglAddon | CanvasAddon | undefined
109+
102110
xtermRef.current = terminal
111+
fitAddonRef.current = fitAddon
112+
terminal.loadAddon(fitAddon)
103113
terminal.open(container)
104-
terminal.write(terminalTranscriptRef.current)
114+
115+
try {
116+
rendererAddon = new WebglAddon()
117+
terminal.loadAddon(rendererAddon)
118+
} catch {
119+
rendererAddon?.dispose()
120+
try {
121+
rendererAddon = new CanvasAddon()
122+
terminal.loadAddon(rendererAddon)
123+
} catch {
124+
rendererAddon?.dispose()
125+
rendererAddon = undefined
126+
}
127+
}
128+
105129
const dataSubscription = terminal.onData(onInput)
130+
terminal.write(terminalTranscriptRef.current, () => {
131+
terminal.scrollToBottom()
132+
})
106133

107134
requestAnimationFrame(() => {
108135
resizeTerminal()
109136
terminal.focus()
137+
terminal.scrollToBottom()
110138
})
111139
const resizeTimer = window.setTimeout(() => {
112140
resizeTerminal()
141+
terminal.scrollToBottom()
113142
}, 100)
114143

115144
return () => {
116145
window.clearTimeout(resizeTimer)
117146
dataSubscription.dispose()
147+
rendererAddon?.dispose()
148+
fitAddon.dispose()
118149
terminal.dispose()
119150
if (xtermRef.current === terminal) {
120151
xtermRef.current = null
121152
}
153+
if (fitAddonRef.current === fitAddon) {
154+
fitAddonRef.current = null
155+
}
122156
}
123157
}, [onInput, resizeTerminal])
124158

0 commit comments

Comments
 (0)