You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 20:15:08