Xterm.js Fit Addon未提交文本换行异常问题求助
解决xterm.js在可调整大小布局中输入文本溢出消失的问题
在使用xterm.js结合可调整大小的布局(svelte-splitpanes)时,终端窗口缩小后,正在输入的未提交命令会出现溢出消失的问题——已提交的命令能正常换行,但未提交的输入文本会从DOM中彻底消失且无法恢复。当前使用Fit Addon+ResizeObserver处理尺寸更新,技术栈包含SvelteKit、svelte-splitpanes和StackBlitz WebContainer API。
相关组件代码
TerminalOutput组件代码
<script lang="ts"> import { WebContainer } from '@webcontainer/api'; import { onDestroy, onMount } from 'svelte'; import { Terminal } from 'xterm'; import 'xterm/css/xterm.css'; import { FitAddon } from 'xterm-addon-fit'; onMount(async () => { const terminalEl = document.querySelector('.terminal') as HTMLDivElement; const webcontainerInstance = await WebContainer.boot(); const fitAddon = new FitAddon(); const terminal = new Terminal({ convertEol: true }); terminal.loadAddon(fitAddon); terminal.open(terminalEl); fitAddon.fit(); const shellProcess = await webcontainerInstance.spawn('jsh', { terminal: { cols: terminal.cols, rows: terminal.rows } }); shellProcess.output.pipeTo( new WritableStream({ write(data) { terminal.write(data); } }) ); const input = shellProcess.input.getWriter(); terminal.onData((data) => { input.write(data); }); const ro = new ResizeObserver(() => { fitAddon.fit(); }); ro.observe(terminalEl); onDestroy(() => { webcontainerInstance.teardown(); }); }); </script> <div id="container" class="bg-slate-1000"> <div class="terminal bg-slate-1000" /> </div>
主布局组件代码
<script lang="ts"> import { setContext } from 'svelte'; import { writable } from 'svelte/store'; import TerminalOutput from '../TerminalOutput.svelte'; import { Pane, Splitpanes } from 'svelte-splitpanes'; </script> <Splitpanes class="overflow-hidden w-screen" style="height: calc(100vh - 4rem);" theme="no-splitter" > <Pane size={20}> </Pane> <Pane size={80} minSize={30}> <Splitpanes horizontal theme="nosplitter"> <Splitpanes theme="no-splitter" horizontal> <Pane size={65} minSize={30}> </Pane> <Pane size={35}> <TerminalOutput /> </Pane> </Splitpanes> </Splitpanes> </Pane> </Splitpanes>
问题原因与修复方案
原因分析
终端尺寸缩小时,Fit Addon会重新计算cols/rows,但WebContainer的shell进程并未同步更新终端尺寸,导致输入缓冲区的文本无法适配新宽度,最终溢出消失。此外,xterm.js的未提交输入状态在resize时未被正确保留和重绘。
修复后的TerminalOutput组件代码
<script lang="ts"> import { WebContainer } from '@webcontainer/api'; import { onDestroy, onMount } from 'svelte'; import { Terminal } from 'xterm'; import 'xterm/css/xterm.css'; import { FitAddon } from 'xterm-addon-fit'; onMount(async () => { const terminalEl = document.querySelector('.terminal') as HTMLDivElement; const webcontainerInstance = await WebContainer.boot(); const fitAddon = new FitAddon(); let shellProcess: Awaited<ReturnType<typeof webcontainerInstance.spawn>> | null = null; const terminal = new Terminal({ convertEol: true }); terminal.loadAddon(fitAddon); terminal.open(terminalEl); fitAddon.fit(); // 初始化shell进程 shellProcess = await webcontainerInstance.spawn('jsh', { terminal: { cols: terminal.cols, rows: terminal.rows } }); shellProcess.output.pipeTo( new WritableStream({ write(data) { terminal.write(data); } }) ); const input = shellProcess.input.getWriter(); terminal.onData((data) => { input.write(data); }); const ro = new ResizeObserver(() => { const oldCols = terminal.cols; fitAddon.fit(); const newCols = terminal.cols; const newRows = terminal.rows; // 仅当宽度变化时同步shell进程尺寸并刷新终端 if (oldCols !== newCols && shellProcess) { shellProcess.resize({ cols: newCols, rows: newRows }); terminal.refresh(0, terminal.rows - 1); } }); ro.observe(terminalEl); onDestroy(() => { webcontainerInstance.teardown(); input.releaseLock(); ro.disconnect(); terminal.dispose(); }); }); </script> <div id="container" class="bg-slate-1000"> <div class="terminal bg-slate-1000" /> </div>
关键修改点
- 保存
shellProcess引用,在resize时调用resize()同步终端尺寸给WebContainer的shell进程 - 对比新旧终端宽度,仅在宽度变化时执行更新,避免不必要的操作
- 调用
terminal.refresh()强制重绘终端内容,确保未提交的输入文本适配新宽度 - 完善销毁逻辑,释放输入锁、断开ResizeObserver、销毁终端实例,避免内存泄漏
内容的提问来源于stack exchange,提问作者Alien 10
相关产品推荐
相关产品推荐

