hottyHTML over the TTY

☰ Contents · Reference › hotty-go

hotty-go

hotty-go is the Go SDK for hotty, HTML over the TTY. With it, a terminal program shows small HTML documents, called surfaces, on rectangles of cells, and hears what the user does in them. A surface can be a card left in the scrollback, a chart that streams, a form that asks, or the panels of a full-screen Bubble Tea program.

It speaks hotty 0.4: a key is named as myelin key names name it (ctrl+a, arrow_left, shift+g; SPEC §10.4), a host's replies and events carry msgpack bodies (SPEC §3.3), and its query lists the version it speaks (a=q:n=1:v=0.4, SPEC §4). A terminal of another hotty version is no host to it, and the program draws in cells. A program written for 0.3 ports as hotty's CHANGES.md says: its data-keys, menu keys and tests move to the new names, a hotty.Platform's Mod is "ctrl" or "super", a key is read with hotty.ParseKey or Platform.ReadKey, and hotty.ModControl and hotty.ModMeta are hotty.ModCtrl and hotty.ModSuper.

The SDK:

  • encodes the protocol, and cuts it out of a byte stream, for a program that reads its terminal raw or a relay that reads another's output;
  • finds out whether the terminal is a hotty host;
  • keeps surfaces on screen while a full-screen frame changes;
  • edits a text field in cells as a host edits it on a surface, with the keymap the program gives both (hotty.TerminalKeys, hottyedit);
  • converts Markdown, tables and images into documents;
  • tests programs against a host that runs inside go test.

Every program has three renditions (SPEC §14): surfaces on a host, cells on any other terminal, and plain data into a pipe. The examples show all three.

t, err := hottyterm.Open("hello") // the terminal, whatever stdout is
if err != nil {
	fmt.Println("Hello.") // no terminal: a pipe, a cron job
	return
}
defer t.Close()
if !t.Detect(ctx) {
	fmt.Println("Hello.") // a terminal that is not a host
	return
}
_ = t.LineStart(ctx)
_, _ = t.Print("hello", `<p>Hello, <b>HTML</b> in the terminal.</p>`, hotty.Placement{Cols: 40, Rows: 1})
_, _ = t.Fence(ctx) // the host's replies, before the shell reads the terminal

Testing it needs no terminal:

h := hottytest.New(t)
run(ctx, hottyterm.New(h, h, "hello", h.TermSize, nil))
h.Surface("hello-hello").Text() // "Hello, HTML in the terminal."

The reference is in docs/api and on pkg.go.dev.

Install

The SDK is a few modules, so that a program takes on only the dependencies of what it imports:

module packages needs
github.com/neuroplastio/hotty-go hotty, chart, form, series, braille, blocks the standard library, and tinylib/msgp's runtime for a host's msgpack bodies
github.com/neuroplastio/hotty-go/hottyterm hottyterm ultraviolet, for the terminal's input
github.com/neuroplastio/hotty-go/hottytea hottytea Bubble Tea
github.com/neuroplastio/hotty-go/hottyedit hottyedit uax29, Unicode's grapheme segmentation
github.com/neuroplastio/hotty-go/hottytest hottytest x/net/html, hottyedit
github.com/neuroplastio/hotty-go/hottyvt hottyvt, asciicast x/vt, a terminal emulator; asciicast, the standard library
go get github.com/neuroplastio/hotty-go github.com/neuroplastio/hotty-go/hottyterm

Packages

package what it is
hotty Package hotty speaks hotty, HTML over the TTY (https://github.com/neuroplastio/hotty), from a Go program.
blocks Package blocks draws bars in terminal cells with the block elements (U+2581–U+2588): a column's height in eighths of a row, as a bar chart or a histogram in text has it.
braille Package braille draws in terminal cells with the Unicode braille patterns (U+2800–U+28FF): each cell is two dots across and four down, so a line chart in text has four times the rows and twice the columns of its cells.
chart Package chart draws line charts for hotty surfaces, as inline SVG that a program changes with deltas as values arrive, and in cells for a terminal that is not a host (Spark).
form Package form is a form for a hotty surface: a spec, the document it makes (real HTML controls, with ids a program can focus and change with deltas), and what a submit event brings back, read into typed answers.
hottyedit Package hottyedit edits a text field as a hotty host edits one (SPEC §10.2), for a program that draws its fields in cells: the same actions, the same words and lines, the same caret.
hottytea Package hottytea is hotty for a full-screen Bubble Tea program: it finds out whether the terminal is a hotty host, keeps the program's surfaces on screen as its frame changes, and turns what the host sends into messages.
hottyterm Package hottyterm is hotty for a program that is not a full-screen Bubble Tea program: a command that prints documents and exits, one that asks a question, a chart that streams.
hottytest Package hottytest is a hotty host that runs inside a test.
hottyvt Package hottyvt shows a terminal's screen on a hotty surface: what a program would write to a terminal goes in, and HTML comes out, first as an element of a document and then as deltas for the rows that changed.
hottyvt/asciicast Package asciicast reads terminal recordings in asciinema's asciicast format, versions 2 and 3: a header with the terminal's size, then what the recorded program wrote, each piece at the time it wrote it, with markers a recording may carry between them.
series Package series reads numbers from a stream of text, as plotting tools such as youplot and asciigraph do: lines of numbers separated by spaces, tabs or commas, a column a series, and a first line with no numbers naming them.

Examples

Each example is a program for one use case. It works in a hotty host, in any other terminal, and into a pipe, and its tests run it in each.

The examples are a module of their own (examples/go.mod), built against the SDK beside them: what they import is never the SDK's dependency. Run one from there:

cd examples
go run ./dashboard https://example.com
example use case what it shows
ask Ask asks for what a deploy needs with a form, and prints the answers as JSON for the script that ran it. It shows a surface the program reads events from: the form is the program's while it asks, takes the keyboard, reports a submit, and shows what is wrong without a round trip per key; then a detached summary replaces it, so nothing is left that reports to the shell (SPEC §5.5, §10).
card Card prints the result of a deploy as a card that stays in the scrollback, as a box of text where the terminal is not a hotty host, and as one line of data into a pipe. It shows the three renditions every hotty program has (SPEC §14): which one a run takes, and how each is made.
dashboard Dashboard watches HTTP endpoints, full screen: whether each is up, and its latency charted as the probes come back. It is a Bubble Tea program, and each endpoint is a card on a surface that hottytea keeps in place as the frame changes.
hello Hello prints a line of HTML in the terminal, and a line of text where the terminal cannot show HTML. It is the smallest hotty program: open the terminal, ask whether it is a host, print a document at the cursor, and read the host's replies before exiting, so that none is left for the shell.
livechart Livechart charts the numbers a command prints, live, below the command line, and leaves the chart in the scrollback when the command ends. series.Parser reads the numbers: a column a series, named by a first line with no numbers, or with -key the number after KEY=.
markdown Markdown shows a Markdown file among a command's output, its images and tables included, and leaves it in the scrollback. The SDK has no Markdown in it: the program renders with a Markdown library (goldmark) and sends the HTML, the way it would any other.
progress Progress shows a task's progress as a bar that moves in place, and leaves its last state in the scrollback. It shows the cheap way to change a surface many times a second: a custom property moves the bar (hotty.OpVar) and text ops change the labels (hotty.OpText), a few dozen bytes each, in one delta so the host shows them together (SPEC §6).
replay Replay plays a terminal session back on a surface, at the size it was recorded and scaled to fit, and leaves its last frame in the scrollback. It shows a terminal inside a document: hottyvt keeps a terminal emulator of the session's size, apart from the terminal the program runs in, and sends its screen as HTML, then a delta per row that changes, in synchronized output so the host shows each frame whole (SPEC §6).

Documentation

The API reference in docs/api is written from the code by internal/docgen: the doc comments, the Example functions, and the examples' package comments. make docs writes it again, as does go generate ./.... The tables above are written the same way, between their markers; the rest of this file is not. CI fails when what is checked in is not what make docs would write, and when an exported identifier has no doc comment.

Development

The toolchain is in mise.toml: run mise install first.

  • make check is the gate: formatting, tidy modules, vet, staticcheck, the tests with the race detector, coverage, and the documentation being current, in every module.
  • The modules build against each other as they are in the repository: each nested go.mod replaces the others with their directories. What a program that uses the SDK gets are the requirements between them, which make pin REV=… sets: before a release to a pushed commit, and for a release to its version (make pin REV=v0.1.0), committed, and then every module tagged at that commit (v0.1.0, hottyterm/v0.1.0, …).
  • make cover runs the tests with coverage, prints a table by package, and fails when a public package is under COVER_MIN (85%).
  • make docs writes the documentation.
  • make vectors copies the conformance vectors from a checkout of neuroplastio/hotty (HOTTY_DIR, by default ../../hotty/main).

Licensed under Apache-2.0.

Starting the terminal…