|
| 1 | +import { CanvasAddon } from '@xterm/addon-canvas' |
| 2 | +import { FitAddon } from '@xterm/addon-fit' |
| 3 | +import { WebglAddon } from '@xterm/addon-webgl' |
1 | 4 | import '@xterm/xterm/css/xterm.css' |
2 | 5 | import { Terminal as XTerm } from '@xterm/xterm' |
3 | 6 | import { useCallback, useEffect, useRef } from 'react' |
@@ -27,12 +30,14 @@ export function useTerminalInstance({ |
27 | 30 | onResize, |
28 | 31 | }: UseTerminalInstanceOptions) { |
29 | 32 | const xtermRef = useRef<XTerm | null>(null) |
| 33 | + const fitAddonRef = useRef<FitAddon | null>(null) |
30 | 34 | const terminalContainerRef = useRef<HTMLDivElement | null>(null) |
31 | 35 | const terminalTranscriptRef = useRef(INITIAL_TERMINAL_TEXT) |
32 | 36 | const terminalSizeRef = useRef({ cols: DEFAULT_COLS, rows: DEFAULT_ROWS }) |
33 | 37 | const decoderRef = useRef(new TextDecoder()) |
34 | 38 |
|
35 | 39 | const resizeTerminal = useCallback(() => { |
| 40 | + fitAddonRef.current?.fit() |
36 | 41 | const nextSize = calculateTerminalSize( |
37 | 42 | terminalContainerRef.current, |
38 | 43 | xtermRef.current |
@@ -99,26 +104,52 @@ export function useTerminalInstance({ |
99 | 104 | theme: TERMINAL_THEME, |
100 | 105 | }) |
101 | 106 |
|
| 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 | + |
102 | 122 | xtermRef.current = terminal |
| 123 | + fitAddonRef.current = fitAddon |
| 124 | + terminal.loadAddon(fitAddon) |
103 | 125 | terminal.open(container) |
104 | | - terminal.write(terminalTranscriptRef.current) |
105 | 126 | const dataSubscription = terminal.onData(onInput) |
| 127 | + terminal.write(terminalTranscriptRef.current, () => { |
| 128 | + terminal.scrollToBottom() |
| 129 | + }) |
106 | 130 |
|
107 | 131 | requestAnimationFrame(() => { |
108 | 132 | resizeTerminal() |
109 | 133 | terminal.focus() |
| 134 | + terminal.scrollToBottom() |
110 | 135 | }) |
111 | 136 | const resizeTimer = window.setTimeout(() => { |
112 | 137 | resizeTerminal() |
| 138 | + terminal.scrollToBottom() |
113 | 139 | }, 100) |
114 | 140 |
|
115 | 141 | return () => { |
116 | 142 | window.clearTimeout(resizeTimer) |
117 | 143 | dataSubscription.dispose() |
| 144 | + rendererAddon?.dispose() |
| 145 | + fitAddon.dispose() |
118 | 146 | terminal.dispose() |
119 | 147 | if (xtermRef.current === terminal) { |
120 | 148 | xtermRef.current = null |
121 | 149 | } |
| 150 | + if (fitAddonRef.current === fitAddon) { |
| 151 | + fitAddonRef.current = null |
| 152 | + } |
122 | 153 | } |
123 | 154 | }, [onInput, resizeTerminal]) |
124 | 155 |
|
|
0 commit comments