Как правильно изменить размер Xterm.js? - PullRequest
1 голос
/ 24 марта 2019

tl; dr Я создал оболочку React для рендеринга массива сообщений журнала в терминал, но изменение размера приводит к странным выводам (см. Скриншот).(В NPM есть React-Wrapper, но он не работает для моего варианта использования - мерцание экрана вызывает)

Я работаю над функцией для Guppy, где добавляю Xterm.JS для вывода терминала.PR можно найти здесь .

Я добавил xterm из-за сканирования / анализа гиперссылок, и это работает.

Но я застрял с получением изменения размераРабота.Если я запускаю devServer в приложении и жду некоторого текста, он будет отображаться с правильной шириной буквы.Если я уменьшу размер, я получу вывод с неправильной шириной буквы.Как на следующем скриншоте: Messed output

Он всегда выглядит правильно в неизмененном состоянии, но после изменения размера он получит неправильное отображение - поэтому это будет происходить для увеличения и уменьшения ширины экрана.

Вывод должен выглядеть примерно так, как показано на следующем снимке экрана (возможно, с некоторыми обернутыми строками): Correct output

Я думаю, это вызвано Fit addon или тем, как я работаюизменение размера с помощью наблюдателя изменения размера, но я не уверен.

Стиль span для буквы xterm получает ширину NaNpx, как на следующем снимке экрана: CSS with width NaNpx Это вызвано медиазапросом I 'я использую?Я еще этого не видел, может быть, мне нужно временно отключить все медиазапросы, чтобы увидеть, вызывает ли это поведение.

То, что я пробовал до сих пор:

  • Обернуто this.xterm.fit() в setTimeout(func, 0), но без эффекта
  • Изменены некоторые стили I 'Я использую, но я не нашел причину.

Код

Код, который я использую, может быть найден в 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)}
/>
Init xterm в componentDidMount с контейнером выше:
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) ... */
}
Добавлен Reaction-Resize-Observer внутри оболочки, которая также содержит контейнер терминала, так что я могу вызвать this.xterm.fit(), если размер изменяется (в репо есть оболочка setTimeout длятестирование).
<ResizeObserver onResize={() => this.xterm && this.xterm.fit()} />
Использование 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 использует ширину стиля контейнера?Если да, может быть, это неправильно установлено.

Обновление

ОК, наблюдатель изменения размера, вероятно, не нужен, так как я получаю то же поведение после комментирования<ResizeObserver/> в рендере.Так что я думаю, что это вызвано xterm.js или CSS в Гуппи.

1 Ответ

1 голос
/ 25 апреля 2019

У меня есть решение этой проблемы. Теперь он работает в вышеупомянутой ветке функций. Не уверен, что есть лучшее решение, но оно работает для меня.

Мне нравится объяснять, как я исправил проблему с изменением размера:

Проблема была в компоненте OnlyOn, который использовался в DevelopmentServerPane. Он всегда отображал два TerminalOutput компонента. Один терминал был скрыт с помощью display: none, а другой отображался с display: inline - изменение стиля было обработано с помощью медиа-запроса внутри styleled-компонента.

После замены OnlyOn на React-реагирующий и использования реквизита рендеринга для проверки mdMin точки останова она работала, как и ожидалось. Реагирующее реагирование удаляет неотображаемый компонент медиазапроса из DOM, поэтому одновременно используется только один терминал в 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>
);

...