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.
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)
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)
}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:
-
booba.Run(model)— a drop-in replacement fortea.NewProgram(...).Run()that wires up the input/output/resize bridge automatically, using a proper mutex + condvar (syncBuffer) instead of a busy-wait loop. -
booba-wasm-build— a thin build-time wrapper aroundgo buildthat patches the missingtty_js.goandsignals_js.gostubs directly into thecharm.land/bubbletea/v2module cache before compiling. This resolves the upstream issue (charmbracelet/bubbletea#1410) without needing any local vendor forks. -
Automatic JS globals — once the WASM starts, go-booba registers
bubbletea_read,bubbletea_write, andbubbletea_resizeonglobalThisso 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.
| 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)
}
}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 |
cd booba
./build.shbuild.sh performs three steps:
go installbooba-wasm-buildat the version declared ingo.mod.booba-wasm-build -o example/booba.wasm .— compiles the WASM binary.- Copy
wasm_exec.jsfrom$GOROOT/lib/wasm/(Go 1.21+) or$GOROOT/misc/wasm/(Go ≤ 1.20) intoexample/.
cd example
python3 -m http.server
# open http://localhost:8000Any 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+).
| 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 |