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 ¶
const ( LogOpen = "[..." // Start or update same line with auto-animation LogClose = "...]" // Update same line and stop auto-animation )
LogOpen and LogClose are special prefixes for progress indication. Use LogOpen at the start of a long operation to show an animated spinner. Use LogClose when the operation completes to stop the animation.
Example:
handler.log(tui.LogOpen, "Deploying to production") // ... long operation ... handler.log(tui.LogClose, "Deployment complete")
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Cancelable ¶ added in v0.1.1
type Cancelable interface {
Cancel() // Called when user presses ESC to exit interactive mode
}
Cancelable defines the optional interface for handlers that want to be notified when the user cancels. Interactive handlers can implement this to clean up or reset their state when ESC is pressed.
type HandlerDisplay ¶ added in v0.1.1
type HandlerDisplay interface {
Name() string // Full text to display in footer (handler responsible for content) eg. "System Status Information Display"
Content() string // Display content (e.g., "help\n1-..\n2-...", "executing deploy wait...")
}
HandlerDisplay defines the interface for read-only information display handlers. These handlers show static or dynamic content without user interaction.
type HandlerEdit ¶ added in v0.1.1
type HandlerEdit interface {
Name() string // Identifier for logging: "ServerPort", "DatabaseURL"
Label() string // Field label (e.g., "Server Port", "Host Configuration")
Value() string // Current/initial value (e.g., "8080", "localhost")
Change(newValue string) // Handle user input + content display via log
}
HandlerEdit defines the interface for interactive fields that accept user input. These handlers allow users to modify values through text input.
type HandlerExecution ¶ added in v0.1.1
type HandlerExecution interface {
Name() string // Identifier for logging: "DeployProd", "BuildProject"
Label() string // Button label (e.g., "Deploy to Production", "Build Project")
Execute() // Execute action + content display via log
}
HandlerExecution defines the interface for action buttons that execute operations. These handlers trigger business logic when activated by the user.
type HandlerInteractive ¶ added in v0.1.1
type HandlerInteractive interface {
Name() string // Identifier for logging: "ChatBot", "ConfigWizard"
Label() string // Field label (updates dynamically)
Value() string // Current input value
Change(newValue string) // Handle user input + content display via log
WaitingForUser() bool // Should edit mode be auto-activated?
}
HandlerInteractive defines the interface for interactive content handlers. These handlers combine content display with user interaction capabilities. All content display is handled through progress() for consistency.
type HandlerSelection ¶ added in v0.1.1
type HandlerSelection interface {
Name() string // Identifier for logging: "CompilerMode"
Label() string // Group label shown before the buttons: "Compiler Mode"
Value() string // Key of the currently active option (e.g. "L")
Change(newValue string) // Called on confirm with the selected option's key
Options() []map[string]string // Ordered {value: label} pairs (>= 2 recommended)
}
HandlerSelection defines a radio / segmented-control field: a group of mutually-exclusive options rendered as evenly distributed buttons in the footer. Exactly one option is active at a time.
It is a superset of HandlerEdit: Name/Label/Value/Change behave identically, and Options() advertises the discrete choice set that the TUI renders as buttons instead of a free-text input. Classify checks HandlerSelection BEFORE HandlerEdit for exactly this reason (see classify.go's specs order).
Interaction: the user presses Enter to enter selection mode, moves the highlight with Left/Right, presses Enter to confirm (which calls Change(selectedValue)), or Esc to cancel.
Options() returns an ORDERED slice of single-entry maps {value: label}, identical in shape to ShortcutProvider.Shortcuts(). The map KEY is the stable value passed to Change(); the map VALUE is the human-readable button caption. Value() must return the key of the currently active option.
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.
type Loggable ¶ added in v0.1.1
Loggable defines optional logging capability for handlers. Handlers implementing this receive a logger function from the TUI when registered via AddHandler.
The log function provided by the TUI: - Is never nil (safe to call immediately) - Automatically tracks messages by handler Name() - Stores full history internally - Displays only most recent log in terminal (clean view)
Example implementation:
type WasmClient struct {
log func(message ...any)
}
func NewWasmClient() *WasmClient {
return &WasmClient{
log: func(message ...any) {}, // no-op until SetLog called
}
}
func (w *WasmClient) Name() string { return "WASM" }
func (w *WasmClient) SetLog(logger func(message ...any)) {
w.log = logger
}
func (w *WasmClient) Compile() {
w.log("Compiling...")
}
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
Extract reads the metadata a StateEntry needs off any handler. Each field is filled only when the handler exposes the matching method. Options and Shortcuts are read via the same named interfaces (HandlerSelection, ShortcutProvider) that Classify uses, not ad-hoc method checks.
type ShortcutProvider ¶ added in v0.1.1
type ShortcutProvider interface {
Shortcuts() []map[string]string // Returns ordered list of single-entry maps with shortcut->description, preserving registration order
}
ShortcutProvider defines the optional interface for handlers that provide global shortcuts. HandlerEdit/HandlerSelection implementations can implement this to enable global shortcut keys.
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.
type StreamingLoggable ¶ added in v0.1.1
type StreamingLoggable interface {
Loggable
AlwaysShowAllLogs() bool // Return true to show all messages
}
StreamingLoggable enables handlers to display ALL log messages instead of the default "last message only" behavior.