Skip to content

bubbletui/booba-demo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

booba — Bubbletea v2 in WASM (go-booba edition)

This directory contains a rework of the root-level proof-of-concept using bubbletea v2 and go-booba, which eliminates every hack that was required to make bubbletea v1 compile and run inside a browser.


Contents

booba/
├── main.go          # bubbletea v2 application (multi-textarea split editor)
├── go.mod / go.sum  # module dependencies
├── build.sh         # build script — compiles WASM and stages example/
└── example/
    ├── index.html   # browser entry-point (xterm.js v6)
    ├── main.js      # ES-module JS glue (terminal ↔ WASM bridge)
    ├── booba.wasm   # compiled output (generated by build.sh)
    └── wasm_exec.js # Go runtime WASM support file (generated by build.sh)

Why v2 + go-booba?

v1 pain-points

The original root-level implementation needed three separate patches just to get the code to compile for GOOS=js GOARCH=wasm:

Problem v1 workaround
github.com/atotto/clipboard has no js stubs replace to a local fork with clipboard_js.go
github.com/containerd/console won't compile for js replace to a patched pre-release commit
github.com/charmbracelet/bubbletea has no js stubs for tty/signals replace to a local fork with tty_js.go + signals_js.go

On top of that, the runtime bridge required a busy-wait hack (MinReadBuffer) because bubbletea v1 expected a blocking io.Reader and JavaScript is single-threaded:

// v1 hack — burns CPU on every read
func (b *MinReadBuffer) Read(p []byte) (int, error) {
    for b.buf.Len() == 0 {
        time.Sleep(100 * time.Millisecond)
    }
    return b.buf.Read(p)
}

How v2 + go-booba solves this

go-booba is an official companion library for bubbletea v2 that was built from the ground up to target GOOS=js GOARCH=wasm. It provides:

  1. booba.Run(model) — a drop-in replacement for tea.NewProgram(...).Run() that wires up the input/output/resize bridge automatically, using a proper mutex + condvar (syncBuffer) instead of a busy-wait loop.

  2. booba-wasm-build — a thin build-time wrapper around go build that patches the missing tty_js.go and signals_js.go stubs directly into the charm.land/bubbletea/v2 module cache before compiling. This resolves the upstream issue (charmbracelet/bubbletea#1410) without needing any local vendor forks.

  3. Automatic JS globals — once the WASM starts, go-booba registers bubbletea_read, bubbletea_write, and bubbletea_resize on globalThis so the browser glue code needs no knowledge of Go internals.

The only remaining replace directive is for github.com/atotto/clipboard, which still lacks js stubs upstream. The same _vendor/clipboard stub from the root module is reused.


API changes: v1 → v2

Area v1 (charmbracelet/*) v2 (charm.land/*/v2)
Key messages tea.KeyMsg tea.KeyPressMsg
View return type View() string View() tea.View
Alt-screen tea.WithAltScreen() program option v.AltScreen = true on the returned tea.View
Textarea styles t.FocusedStyle.X = ... styles := t.Styles(); styles.Focused.X = ...; t.SetStyles(styles)
Running the program tea.NewProgram(m, opts...).Run() booba.Run(m) (for WASM)

Minimal example:

import booba "github.com/NimbleMarkets/go-booba"

func (m model) View() tea.View {
    v := tea.NewView(myContent)
    v.AltScreen = true
    return v
}

func main() {
    if err := booba.Run(newModel()); err != nil {
        log.Fatal(err)
    }
}

Frontend changes

The example/ directory uses xterm.js v6 (the @xterm/ npm scope) and a redesigned HTML layout:

Area Old (v1) New (v2)
xterm package xterm@5.3.0 @xterm/xterm@6
fit addon xterm-addon-fit@0.8.0 @xterm/addon-fit@0.11
Container .terminal-container div with inline styles #terminal-container fills the full viewport
Loading state None — blank page while WASM loads #loading overlay with fade-out transition
JavaScript Inline <script> ES module (main.js) with async/await

Building

cd booba
./build.sh

build.sh performs three steps:

  1. go install booba-wasm-build at the version declared in go.mod.
  2. booba-wasm-build -o example/booba.wasm . — compiles the WASM binary.
  3. Copy wasm_exec.js from $GOROOT/lib/wasm/ (Go 1.21+) or $GOROOT/misc/wasm/ (Go ≤ 1.20) into example/.

Serving locally

cd example
python3 -m http.server
# open http://localhost:8000

Any static file server works; the only requirement is that the server sends Content-Type: application/wasm for .wasm files (Python's built-in server does this automatically since Python 3.7+).


Dependencies

Package Version Role
charm.land/bubbletea/v2 v2.0.6 TUI framework
charm.land/bubbles/v2 v2.1.0 UI components (textarea, help, key)
charm.land/lipgloss/v2 v2.0.3 Styling
github.com/NimbleMarkets/go-booba v0.6.0 WASM bridge + build tool
github.com/atotto/clipboard v0.1.4 (vendored) Clipboard stub for js/wasm

Releases

No releases published

Packages

 
 
 

Contributors