neuroplast.ioHuman-machine interface research

Bubble Tea

github.com/charmbracelet/bubbletea · MIT

For hotty.neuroplast.io

3 patches on v2.0.10. HOTTY's website is a set of Bubble Tea programs. In the browser they run as WebAssembly in terminals that xterm.js draws, and they place HOTTY surfaces, HTML, over the cells. Bubble Tea doesn't build for the browser, its renderer scrolls the regions of the screen that surfaces sit in, and it writes the commands that place surfaces apart from the frame they belong to. We carry a fix for each, until Bubble Tea has a build for the browser, a way to turn that scrolling off, and frames that take a program's output with them.

The patches, each with its upstream fit:

  1. Programs build for the browser (GOOS=js) — yes
  2. The renderer redraws lines instead of scrolling them — maybe
  3. Queued output goes out with the next frame — yes

Programs build for the browser (GOOS=js)

Upstream fit: yes · patch 0001

What it changes: it adds tty_js.go, which defines initInput, listenForResize, suspendProcess and suspendSupported as no-ops for GOOS=js. Without them Bubble Tea doesn't compile there: they are defined only for Unix and Windows. In a browser there is no tty to put in raw mode, no SIGWINCH and no job control.

Why we carry it: the apps on HOTTY's website, hotty.neuroplast.io, are Bubble Tea programs. Each runs in the browser, compiled to WebAssembly with TinyGo, in a terminal that xterm.js draws. The page wires their input and output, and sends a WindowSizeMsg when the terminal resizes.

For upstream: yes. It's one small file that builds only for GOOS=js, so nothing changes on any other platform, and any Bubble Tea program can then run in a browser terminal.

The renderer redraws lines instead of scrolling them

Upstream fit: maybe · patch 0002

What it changes: it turns off the renderer's scroll optimization (SetScrollOptim) on every platform, not only on Windows. Lines that haven't changed are redrawn, rather than moved by scrolling a region of the screen.

Why we carry it: HOTTY surfaces sit over the cells, in those regions. A terminal may move them with the scrolled lines, or not, while the program places them again anyway: two moves per row scrolled, and jitter. On HOTTY's website, hotty.neuroplast.io, the feed's benchmark measured half the placements, and a quarter of the terminal's time per step, without it.

For upstream: maybe. Programs that place graphics over the cells may want the same, but most programs don't, and for them the optimization saves output. Upstream would want this as an option a program turns on, not as the default.

Queued output goes out with the next frame

Upstream fit: yes · patch 0003

What it changes: on each tick, Bubble Tea wrote the output a program queued (tea.Raw, and its own queries) in one write and the frame in a second, with only the frame inside its synchronized update (mode 2026). With the patch, both go in one write: the queued output first, as before, and inside the frame's synchronized update when the renderer uses them. When the frame hasn't changed, the queued output goes in a synchronized update of its own.

Why we carry it: a hotty program's layout is a tea.Raw of commands that place surfaces, followed by the cells under them. A terminal may draw between the two writes, and xterm-addon-hotty does: it inflates a compressed document asynchronously, so xterm.js yields after each one. On hotty.neuroplast.io's first load, the tour's nine surfaces appeared one by one, about 10 ms apart, over the frame before, for the ~95 ms until the cells came. In one synchronized update, a host holds the commands until the update ends (SPEC §6.3) and shows the surfaces with their cells.

For upstream: yes. Any program that writes with tea.Raw what belongs to a frame has the same tear: images (kitty graphics, sixels) placed over the cells, hyperlinks or marks drawn beside them. Most queued output is a query or a mode change, which loses nothing by going inside a synchronized update. A program that relies on its tea.Raw reaching the terminal before the frame still gets that, in the same write.


neuroplast.io · Modding · github.com/neuroplastio

Starting the terminal…