tui

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 18, 2026 License: MIT Imports: 0 Imported by: 0

README

tui

Single source of truth for the TUI handler-kind contract shared by tinywasm/devtui (the terminal renderer) and tinywasm/app (the daemon that serializes handler state to the client).

Why this exists

A "handler" is any value a consumer registers with the TUI. The TUI inspects the handler's Go interface to decide how to render it and how to serialize it over the daemon↔client wire. That mapping used to be re-derived independently in several places (devtui's local switch, its wire constants, app's hand-copied constants and a second detection switch). Adding or reordering a handler kind silently broke one side — e.g. a Selection handler (radio buttons) collapsing into a plain text field in tinywasm -tui.

This package centralizes the entire contract so there is exactly one place to change, and a completeness test that fails loudly when a new kind isn't fully wired.

Surface

Symbol Purpose
Kind + Kind* consts The frozen handler-kind enum (also the wire value). Append-only.
AllKinds() Every kind in wire order — iterate it in consumer guardrail tests.
Classify(h any) (Kind, hasField bool) The one ordered interface-detection walk.
Extract(h any) Meta Pulls Name/Label/Value/Options/Shortcuts off a handler.
StateEntry The JSON wire format (produced by the daemon, consumed by the client).

The guardrail

AllKinds() + Classify let every consumer write a test that iterates all kinds and asserts it handles each one. Appending a Kind here turns those tests red until detection, serialization, reconstruction and rendering are all wired — instead of degrading silently. See classify_test.go for the in-repo completeness checks.

Zero heavy dependencies

Detection uses structural interfaces, so this package imports nothing from devtui and no charmbracelet/bubbletea/lipgloss. The daemon can import it without pulling the terminal renderer. The dependency arrow is one-way: devtui/apptui, never back.

Adding a new handler kind

  1. Append a Kind<Name> constant (new highest value — never renumber).
  2. Add it to AllKinds().
  3. Add a spec (with its detection predicate, in precedence order) to specs.
  4. Add a sample to sampleFor in the test.
  5. go test ./... — the completeness tests tell you what's missing.
  6. Bump the version; consumers wire the new kind, guided by their own AllKinds() guardrail tests going red.

Documentation

Overview

Package tui is the single source of truth for the TUI handler-kind contract shared by github.com/tinywasm/devtui (the renderer/consumer) and github.com/tinywasm/app (the daemon producer).

A "handler" is any value a consumer registers with the TUI. The TUI inspects the handler's Go interface to decide how to render it (button, text field, radio group, log stream…) and, in client/daemon mode, how to serialize it over the wire (StateEntry). Historically each consumer re-derived that mapping independently, so adding or reordering a handler kind silently broke one side. This package centralizes the whole contract:

  • Kind — the frozen enum (also the wire value).
  • Classify/Extract — the ONE ordered interface-detection walk + metadata.
  • StateEntry — the JSON wire format.
  • AllKinds — lets every consumer write a guardrail test that fails loudly when a new Kind is not fully wired.

It has zero heavy dependencies (no charmbracelet/bubbletea/lipgloss), so the daemon can import it without pulling the terminal renderer.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Kind added in v0.1.0

type Kind int

Kind identifies how a registered handler is rendered and serialized.

The integer values are the FROZEN wire protocol: they are written into StateEntry.HandlerType and read back by remote consumers. Never reorder or renumber existing kinds — only append new ones at the end. TestKindWireValues guards this.

const (
	KindDisplay     Kind = 0 // read-only footer text (Name + Content)
	KindEdit        Kind = 1 // free-text input field (Name + Label + Value + Change)
	KindExecution   Kind = 2 // action button (Name + Label + Execute)
	KindInteractive Kind = 3 // auto-editing field (Edit + WaitingForUser)
	KindLoggable    Kind = 4 // log stream only, no footer field (Name + SetLog)
	KindSelection   Kind = 5 // radio / segmented buttons (Edit + Options)
)

func AllKinds added in v0.1.0

func AllKinds() []Kind

AllKinds returns every declared Kind in wire order. Consumers iterate this in their own guardrail tests to assert they handle every kind — so a newly appended Kind turns those tests red instead of degrading silently.

func Classify added in v0.1.0

func Classify(h any) (kind Kind, hasField bool)

Classify runs the single ordered detection walk and returns the handler's Kind and whether it renders a footer field. hasField is false only for KindLoggable (log stream, no field). A handler matching nothing is treated as a log-only handler with no field.

func (Kind) String added in v0.1.0

func (k Kind) String() string

String returns the kind's name, for readable log and test output.

type Meta added in v0.1.0

type Meta struct {
	Name      string
	Label     string
	Value     string
	Options   []map[string]string
	Shortcuts []map[string]string
}

Meta is the data Extract pulls off a handler. Fields are populated only when the handler exposes the corresponding method; the rest stay zero.

func Extract added in v0.1.0

func Extract(h any) Meta

Extract reads the metadata a StateEntry needs off any handler. Each field is filled only when the handler exposes the matching method.

type StateEntry added in v0.1.0

type StateEntry struct {
	TabTitle     string              `json:"tab_title"`
	HandlerName  string              `json:"handler_name"`
	HandlerColor string              `json:"handler_color"`
	HandlerType  int                 `json:"handler_type"` // a Kind value
	Label        string              `json:"label"`
	Value        string              `json:"value"`
	Shortcut     string              `json:"shortcut"`  // primary key = handler Name()
	Shortcuts    []map[string]string `json:"shortcuts"` // from a Shortcuts() provider
	Options      []map[string]string `json:"options"`   // Selection buttons: ordered {value:label}
}

StateEntry is the JSON wire format for a single handler registered in the daemon TUI. Produced by the daemon (github.com/tinywasm/app's HeadlessTUI), consumed by the client (github.com/tinywasm/devtui client mode).

The JSON tags are the published contract: any producer must match them exactly, and existing tags must never change. Only additive changes (new fields) are allowed.

Jump to

Keyboard shortcuts

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