xtermjs / xterm.js

A terminal for the web
https://xtermjs.org/
MIT License
16.99k stars 1.59k forks source link

Height keeps shrinking when using fitAddon.fit() #5058

Closed jpark799 closed 1 month ago

jpark799 commented 1 month ago

The initial fitAddon.fit() works but I am running it again each time a user resizes their window and for some reason, it keeps shrinking in height when that is not expected. When adjusting from the window left to right, the terminal will resize correctly in width but it also shrinks each time in height. I thought there may be some underlying styling issues with my app so I tried this in a fresh create-react-app and i was getting the same behavior

resizing issue

Code:

App.tsx

import { useEffect, useMemo, useRef, useState } from 'react'
import { FitAddon } from '@xterm/addon-fit'
import { Unicode11Addon } from '@xterm/addon-unicode11'
import { WebLinksAddon } from '@xterm/addon-web-links'
import { WebglAddon } from '@xterm/addon-webgl'
import { Terminal } from '@xterm/xterm'
import '@xterm/xterm/css/xterm.css'
import { useWindowSize } from './utils';

function App() {
  const terminalRef = useRef<HTMLDivElement | null>(null)
  const [term, setTerm] = useState<Terminal | null>(null)
  const fitAddon = useMemo(() => new FitAddon(), [])
  const size = useWindowSize()

  useEffect(() => {
    const terminal = new Terminal({
      allowProposedApi: true,
      cursorBlink: true,
    })
    setTerm(terminal)
    return () => {
      if (terminal) {
        terminal.dispose()
      }
    }
  }, [])

  useEffect(() => {
    if (term) {
      if (terminalRef.current) {
        term.open(terminalRef.current)
        setTerm(term)
        term.loadAddon(fitAddon)
        term.loadAddon(new WebLinksAddon())
        term.loadAddon(new WebglAddon())

        term.loadAddon(new Unicode11Addon())
        fitAddon.activate(term)
        fitAddon.fit()
      }
    }

    return () => {
      term?.dispose()
    }
  }, [fitAddon, term, terminalRef])

  useEffect(() => {
    fitAddon.fit()
    console.log(fitAddon.proposeDimensions())
  }, [term, size, fitAddon])

  return (
   <div ref={terminalRef}/>
  );
}

export default App;

utils.tsx

import { useEffect, useState } from 'react'

interface WindowSize {
  width: number
  height: number
}

export const useWindowSize = (): WindowSize => {
  const [windowSize, setWindowSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight,
  })

  const onWindowResize = () => {
    setWindowSize({ width: window.innerWidth, height: window.innerHeight })
  }

  useEffect(() => {
    window.addEventListener('resize', onWindowResize)

    return () => {
      window.removeEventListener('resize', onWindowResize)
    }
  }, [])

  return windowSize
}

Details

jpark799 commented 1 month ago

Nevermind, I figured out the issue. In case anyone else runs into a similar issue, I had to actually give a max width and height as well as 100% for both height and width for the div so something like this:

width: 100%;
height: 100%;
max-height: 24rem
max-width: 48rem

I'm using tailwind so this is what it looked like: <div className="size-full max-h-96 max-w-3xl" ref={terminalRef} />