Protocol for TUIOW (terminal user interface over the wire)
Protobuf definition currently used for gRPC and is compiled for Golang and Python.
| Doc | Audience |
|---|---|
| DEVELOPERS.md | Server developers — concepts, primitives, working code |
| STREAMING.md | Live surfaces — GetPageStream, latency, Event input |
| doc/ | Documentation index + CHANGES.md |
| CHANGELOG-v2.md | Compact v2 / streaming checklist |
| .opencode/skills/uggly-server/ | OpenCode agent skill (optional) |
See the samples folder for some "hello world" example servers in Go and Python. View them with the uggly-client. For more advanced server examples check out the following:
- uggly-server - a server that hosts sites from static files.
- uggly-server-login - a server that gives examples of login flows and cookie usage
- puggly-server - a python server that shows dynamic navigation of a CSV using keyboard shortcuts
Uggly is a means to generate Terminal User Interfaces in a client-server architecture. Think of it as TUI over-the-wire (TUIOW). The client requests content from the server via gRPC protobuffers and the client handles rendering of that content. The server is sending "pages" of content one screen at a time. The protocol and page definitions take inspiration from CSS/HTML in that there are constructs such as DivBoxes, TextBlobs, Links, and Forms for example. It is opinionated in that only keyboard strokes are supported for link navigation.
Legacy DivBox / TextBlob / KeyStroke pages still work. New servers should prefer:
| Feature | Wire types | Purpose |
|---|---|---|
| Query + Event | PageRequest.query, PageRequest.event, KeyStroke.event |
Structured actions instead of page-name dialects (astro_n) |
| Widgets | Elements.tables, Elements.lists (ItemList), Elements.prompts |
Client expands to boxes before draw |
| Autofocus forms | Form.autofocus, Prompt |
No FormActivation key required |
| String runes | DivBox.borderCharStr / fillCharStr |
Avoid ord() / int32 boilerplate |
| Theme / Attr enum | Theme, Style.attrs |
Named styles |
| Layout tree | PageResponse.root (Node + Layout) |
Stack/absolute composition |
| Capability handshake | Meta.Hello |
Feature discovery |
| Errors | PageError |
Structured status |
| Live surfaces | GetPageStream + Events |
Games, maps, panable docs (see STREAMING.md) |
| Client scroll | KeyStroke.divScroll |
Scroll a named box without refetch |
import helpers as ug
page = ug.simple_page("home", "hello", width=w, height=h, title="Hi")
ug.add_table(page, ["A", "B"], [["1", "2"]])
page.key_strokes.append(ug.key_event("n", "next", hint="(n) Next"))
# optional: ug.expand_widgets(page, w, h) # for old clients onlyModern uggly-client expands widgets itself; servers can return high-level tables/prompts directly.