web

package
v0.5.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Rendered for js/wasm

Overview

Package web bridges a bubbletea Program to an xterm.js terminal in a page.

The design rests on one fact: xterm.js already speaks the terminal protocol. It turns DOM key events into escape sequences and, once bubbletea asks for mouse tracking, emits SGR mouse sequences — and bubbletea already parses both. So this is a byte pipe, not a translation layer, and key and mouse parity with the terminal build comes for free rather than being maintained.

The rule that will break everything if ignored

A js.Func callback must never block. Go on WebAssembly runs on the single JavaScript thread: while a Go callback is running, no other JS event can fire. A callback that blocks waiting for something another event would deliver deadlocks the whole runtime, and the symptom is a page that draws one frame and then freezes with nothing in the console.

So every callback here does the same three things and returns: take the lock, append, signal without blocking. Nothing calls Program.Send from inside a callback either — Send blocks until the event loop reads it.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func LocalStorage

func LocalStorage() (kv store.KV, err error)

LocalStorage is a store.KV over the browser's localStorage.

Every call is wrapped, because localStorage throws rather than returning an error and an unrecovered panic in Go on WebAssembly kills the program. Two throws are ordinary rather than exceptional: Safari refuses access at all in private mode, and any browser raises QuotaExceededError once the origin's allowance is full. Neither should cost the player their game, so the first is answered by falling back to memory (see Storage) and the second by returning an error that reaches the UI's error line.

func Storage

func Storage() (store.KV, error)

Storage is the KV the browser build should use: localStorage when the browser allows it, and memory when it does not. A session that forgets everything on reload is a much better answer than a blank page.

Types

type Terminal

type Terminal struct {
	// contains filtered or unexported fields
}

Terminal is the xterm.js instance the page created for us.

func Open

func Open() (*Terminal, error)

Open finds the terminal the page set up. boot.js publishes it as globalThis.surmise = { term, … } before starting the Go program, so the contract between the two halves is that one object.

func (*Terminal) Attach

func (t *Terminal) Attach(send func(tea.Msg))

Attach wires the resize event to the running Program.

send is Program.Send, which blocks until the event loop reads the message — so it is called from a goroutine, never from the callback. The channel holds one pending size and drops the rest: a drag across the screen produces hundreds of these, and only the last one is true.

func (*Terminal) Done

func (t *Terminal) Done()

Done reveals the page's "that's all" overlay. A returned main tears the Go instance down, so without this the player is left looking at a dead terminal.

func (*Terminal) Reader

func (t *Terminal) Reader() io.Reader

Reader is where the player's keystrokes come from.

func (*Terminal) Size

func (t *Terminal) Size() (cols, rows int)

Size is the terminal's current size in cells. The page has already fitted it to the window before starting us, so this is the size of the first frame.

func (*Terminal) Writer

func (t *Terminal) Writer() io.Writer

Writer is where the renderer's bytes go.

type Transfer added in v0.5.0

type Transfer struct {
	// contains filtered or unexported fields
}

Transfer is the browser's implementation of the UI's backup file transfer.

func NewTransfer added in v0.5.0

func NewTransfer() (*Transfer, error)

NewTransfer wires up to the page. It returns an error when the page half is not there, which is what the Node smoke test sees: the game then runs with no backup row rather than with one that cannot work.

func (*Transfer) Load added in v0.5.0

func (t *Transfer) Load() (b []byte, from string, err error)

Load opens the page's file picker and waits for the answer.

It blocks, which is safe here and nowhere else in this package: the UI calls it from a bubbletea command, which runs on its own goroutine while the JS event loop keeps turning — so the callback that will wake it can still fire.

A player who closes the picker without choosing gets no bytes and no error. Choosing nothing is not a failure, and reporting it as one would put an error on screen for pressing escape.

func (*Transfer) Save added in v0.5.0

func (t *Transfer) Save(b []byte) (where string, err error)

Save hands the archive to the page, which offers it as a download. The name is the page's business — a browser puts it wherever downloads go — so what comes back is a file name and not a path.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL