如何正确调整Xterm.js的大小?

时间:2019-03-24 10:04:45

标签: javascript css reactjs xtermjs

tl; dr 我已经创建了一个React包装器,以将一系列日志消息呈现到终端中,但是调整大小给出了一个奇怪的输出(请参见屏幕截图)。 (NPM上有一个React-Wrapper,但在我的用例中不起作用-导致屏幕闪烁)

我正在为Guppy开发一项功能,其中我在终端输出中添加了Xterm.js。 可以在here中找到PR。

由于超链接扫描/解析,我添加了xterm,并且有效。

但是我一直坚持调整大小才能正常工作。如果我在应用程序中启动devServer并等待一些文本,它将以正确的字母宽度显示。 如果减小尺寸,则会得到字母宽度不正确的输出。 如以下屏幕截图所示: Messed output

在未调整大小的状态下,它始终看起来正确,但是在调整大小后,它将显示错误的显示-因此,这会放大和缩小屏幕宽度。

输出应类似于以下屏幕截图(可能带有一些换行): Correct output

我认为这是由Fit addon引起的,或者是我使用调整大小的观察器处理调整大小的方式引起的,但是我不确定。

xterm字母的跨度样式的宽度为NaNpx,如以下屏幕截图所示: CSS with width NaNpx 这是由我使用的媒体查询引起的吗?我还没有看到,也许我必须暂时禁用所有媒体查询,以查看是否是这种行为。

到目前为止,我已经尝试过:

  • this.xterm.fit()包装为setTimeout(func, 0),但没有效果
  • 修改了一些我正在使用的样式,但我没有找到原因。

代码

我正在使用的代码可以在Github branch feature-terminal-links上找到,但是在这里,我想提取为使Xterm与React一起使用而添加的部分:

  1. 我创建了一个样式组件XtermContainer作为div,以便可以添加Xterm样式和自己的样式。以下代码位于render内,并将成为我们的xterm.js容器(innerRef稍后将在ComponentDidMount中用于通过该容器初始化Xterm):
<XtermContainer
    width={width}
    height={height}
    innerRef={node => (this.node = node)}
/>
  1. 使用上面的容器在componentDidMount中初始化xterm:
componentDidMount() {
    Terminal.applyAddon(webLinks);
    Terminal.applyAddon(localLinks);
    Terminal.applyAddon(fit);

    this.xterm = new Terminal({
      convertEol: true,
      fontFamily: `'Fira Mono', monospace`,
      fontSize: 15,
      rendererType: 'dom', // default is canvas
    });

    this.xterm.setOption('theme', {
      background: COLORS.blue[900],
      foreground: COLORS.white,
    });

    this.xterm.open(this.node);
    this.xterm.fit();

    /* ... some addon setup code here (not relevant for the problem) ... */
}
  1. 在还包含终端容器的包装器中添加了react-resize-observer,因此如果大小发生变化,我可以触发this.xterm.fit()(在仓库中有一个setTimeout包装器用于测试) 。
<ResizeObserver onResize={() => this.xterm && this.xterm.fit()} />
  1. 如果组件正在获取新日志,请使用componentDidUpdate(prevProps, prevState)更新终端,并将终端滚动到底部:
componentDidUpdate(prevProps, prevState) {
    if (prevProps.task.logs !== this.state.logs) {
      if (this.state.logs.length === 0) {
        this.xterm.clear();
      }
      for (const log of this.state.logs) {
        /*
        We need to track what we have added to xterm - feels hacky but it's working.
        `this.xterm.clear()` and re-render everything caused screen flicker that's why I decided to not use it.
        Todo: Check if there is a react-xterm wrapper that is not using xterm.clear or 
              create a wrapper component that can render the logs array (with-out flicker).
        */
        if (!this.renderedLogs[log.id]) {
          this.writeln(log.text);
          this.xterm.scrollToBottom();
          this.renderedLogs[log.id] = true;
        }
      }
    }
}

想法,我必须找到原因:

  • 检查ResizeObserver代码。(请参阅下面的更新)
  • 尝试查找为什么xterm css获得NaN宽度。 Xterm.js是否使用容器的样式宽度?如果是,则可能设置不正确。

更新

好的,可能不需要调整大小的obeserver,因为注释掉渲染中的<ResizeObserver/>后,我得到了相同的行为。因此,我认为这是由xterm.js或Guppy中的CSS引起的。

1 个答案:

答案 0 :(得分:1)

我已解决此问题。现在可以在上述功能分支中使用。不确定是否有更好的解决方案,但对我有用。

我想解释一下我如何解决调整大小的问题:

问题是OnlyOn中使用的DevelopmentServerPane组件。它始终呈现两个TerminalOutput组件。一个终端被display: none隐藏,另一终端被display: inline显示-样式更改是通过样式组件内部的媒体查询来处理的。

OnlyOn替换为React-responsive,并使用渲染道具检查mdMin断点后,它工作正常。反应响应式正在从DOM中删除未显示的mediaquery组件,因此在DOM中只能同时有一个终端。

我仍然不知道为什么字母宽度有问题,但是两个实例可能以某种方式发生了冲突。我无法创建最小的复制品。我试图在此Codesandbox中重新创建问题,但是一次只调整了一个终端的大小,所以我没有那里的问题。

解决该问题的代码(上述存储库的简化版本):

import MediaQuery from 'react-responsive';

const BREAKPOINT_SIZES = {
  sm: 900,
};

const BREAKPOINTS = {
  mdMin: `(min-width: ${BREAKPOINT_SIZES.sm + 1}px)`,
};

const DevelopmentServerPane = () => (
  <MediaQuery query={BREAKPOINTS['mdMin']}>
    {matches =>
      matches ? (
        <div>{/* ... render Terminal for matching mdMin and above */}</div>
      ) : (
        <div> {/* ... render Terminal for small screens */}</div>
      )
    }
  </MediaQuery>
);