Documentation
¶
Overview ¶
Package termmosaic is the root of the TermMosaic TUI framework.
It holds the two narrow interfaces every other package is written against — Terminal and Sink (ADR 0001) — plus the geometry, capability, event and widget types they traffic in. Nothing here talks to a real terminal: the concrete backends live in the headless and term packages, and the core depends only on these interfaces. That is what makes every widget CI-testable with no terminal, which ADR 0001 records as a first-class deliverable rather than a stretch goal.
The import graph is a DAG with no cycles:
buffer leaf: Cell, Colour, Attr, Buffer, geometry layout leaf: the pure constraint solver (ADR 0004) termmosaic this package: Terminal, Sink, Caps, Event, Widget headless headless Terminal + MemorySink render the hybrid renderer (ADR 0003)
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Caps ¶
type Caps struct {
// TrueColor reports support for SGR 38;2 and 48;2.
TrueColor bool
// Color256 reports support for the xterm-256 palette.
Color256 bool
// Unicode reports whether box-drawing and block glyphs render correctly,
// as opposed to mojibake or blank cells on a misconfigured locale.
Unicode bool
// Mouse reports support for mouse reporting.
Mouse bool
// KittyKeyboard reports that this terminal MAY SUPPORT the kitty keyboard
// protocol, which gives unambiguous key events for keys xterm cannot encode.
//
// "May support" is the whole of the claim, and it is deliberately NOT the same
// as "the protocol is active". Detection is a TERM substring heuristic, so it
// is right sometimes and wrong sometimes, and whether the protocol is actually
// in force is established by a handshake on a running input.Source and
// reported by Source.KittyActive(). Negotiation state lives on the Source
// because it is a property of a session rather than of a device; see ADR 0005.
KittyKeyboard bool
// BracketedPaste reports support for bracketed paste, which is what makes
// a multi-line paste one event instead of several keystrokes.
BracketedPaste bool
}
Caps reports what a terminal supports.
Detection is a single point of truth so the truecolor->256->16 and Unicode->ASCII degradation ladders have one home, which ADR 0001 calls out as a direct benefit of owning the terminal layer. TermMosaic has no terminfo database; it sniffs a fixed, well-chosen subset, which ADR 0001 records as a deliberate accepted limitation.
func DefaultCaps ¶
func DefaultCaps() Caps
DefaultCaps returns the capabilities of a modern, well-configured terminal. A headless Terminal starts here; a real one starts from probing and degrades.
func (Caps) ColourDepth ¶
func (c Caps) ColourDepth() buffer.ColourDepth
ColourDepth returns the rung of the degradation ladder these capabilities select.
type Compose ¶
type Compose struct {
Text string
Cursor int
Phase ComposePhase
}
Compose is the payload of an EventCompose.
RESERVED, NEVER EMITTED IN v0.x. IME support is deferred by ADR 0005 and this type exists so the union can already express composition. The field set is what an implementation needs and no more: Text is the preedit string as the terminal reports it, Cursor is the byte offset of the caret within Text, and Phase says whether this is the start of a composition, an update to it, the committed text, or the end.
type ComposePhase ¶
type ComposePhase uint8
ComposePhase is the stage of an IME composition.
const ( // ComposeStart begins a composition. ComposeStart ComposePhase = iota // ComposeUpdate replaces the preedit string of an in-progress composition. ComposeUpdate // ComposeCommit carries the committed text and ends the composition. ComposeCommit // ComposeEnd discards a composition without committing it. ComposeEnd )
Composition phases.
func (ComposePhase) String ¶
func (p ComposePhase) String() string
String returns the composition phase's name.
type Event ¶
type Event struct {
// Kind discriminates the event.
Kind EventKind
// Key is the non-printable key for EventKey, or KeyNone for a printable
// character.
Key Key
// Rune is the printable character for EventKey, or 0.
Rune rune
// Mod is the modifier state.
Mod KeyMod
// Type is press, repeat or release. Always KeyPress unless the kitty
// keyboard protocol reported event types (ADR 0005 section 2).
Type KeyType
// Mouse is the payload for EventMouse.
Mouse Mouse
// Size is the new size for EventResize.
Size Size
// Text is the payload for EventPaste, the entire paste undelimited and
// unescaped, and the committed text for EventCompose.
Text string
// Compose is the payload for EventCompose, and nil otherwise. This is the
// only pointer field in Event and the one field allowed to be nil; see
// ADR 0005 section 7 for why composition is reserved but deferred.
Compose *Compose
// Truncated reports that a paste payload exceeded the configured maximum
// and was cut short. The text is still a valid prefix of what was pasted.
Truncated bool
// Focused reports the new state for EventFocus.
Focused bool
}
Event is a single input event delivered to the widget tree.
One struct rather than an interface, because events cross the input path per keystroke and an interface here would mean a heap allocation for no benefit. Unused fields are zero, with one exception: Compose is nil unless Kind is EventCompose.
INVARIANT: this struct is copied by value on the hot path and must not grow without a deliberate decision. TestEventSizeIsBounded pins sizeof(Event); see ADR 0005 section 8.
func ResizeEvent ¶
ResizeEvent returns an EventResize for the given size.
func SpecialKeyEvent ¶
SpecialKeyEvent returns an EventKey for a non-printable key.
type EventKind ¶
type EventKind int
EventKind discriminates an Event.
const ( // EventNone is the zero value and carries no event. EventNone EventKind = iota // EventKey is a key press, repeat or release. EventKey // EventResize reports a new terminal size. EventResize // EventMouse is a mouse button press, release or motion. EventMouse // EventPaste is a bracketed-paste payload, delivered whole. EventPaste // EventFocus reports gaining or losing terminal focus. EventFocus // EventCompose reports IME composition state. // // DECLARED BUT NEVER EMITTED IN v0.x. IME support is deferred by ADR 0005 // §7; this constant exists so the union can already express composition, // and so a widget author writing a switch today sees the case and knows // composition is coming. Do not emit it and do not handle it as if it were // live. EventCompose )
Event kinds. A zero Event has Kind EventNone, which widgets ignore.
type Focusable ¶
type Focusable interface {
Widget
// Focused reports whether the widget currently has focus.
Focused() bool
// SetFocused gives or removes focus. Implementations invalidate themselves.
SetFocused(bool)
}
Focusable is an optional interface a Widget implements if it can take focus. The renderer uses it to maintain focus order without requiring every widget to carry a focus method.
type Key ¶
type Key int
Key identifies a key press. Printable keys are carried as their rune in Event.Rune; Key is only set for keys that have no printable form.
The set is deliberately the xterm-encodable set, with no F13–F35. A new key must be APPENDED at the end of the iota block below: the constants are contiguous and inserting one in the middle silently renumbers every later value.
TermMosaic decodes the kitty keyboard protocol when the handshake on an input.Source succeeds — not merely when a terminal advertises the capability. Caps.KittyKeyboard is a TERM heuristic meaning "may support", and the negotiation state lives on the Source because it is a property of a running session rather than of a device. See ADR 0005 §2. Without a successful handshake some combinations are indistinguishable; that is an accepted limitation of owning the input layer.
const ( // KeyNone means the event carries a printable rune rather than a special // key, so Event.Rune holds the character. KeyNone Key = iota // KeyEnter is the return key. KeyEnter // KeyTab is the tab key. KeyTab // KeyBacktab is shift-tab, reported as its own key because terminals // encode it that way and a widget usually wants to treat it differently // from tab. KeyBacktab // KeyBackspace is the backspace key. A terminal may deliver it as a // control character rather than as an escape sequence, depending on the // terminfo setting, and the input decoder reconciles that: both 0x7f and // 0x08 decode to KeyBackspace by default. See ADR 0005 §2, and // input.Config.BackspaceByte to pin one. KeyBackspace // KeyEscape is the escape key, which a bare escape is indistinguishable // from the start of an escape sequence. KeyEscape // KeySpace is the space bar, which terminals may deliver as a rune. KeySpace // KeyUp is the up arrow. KeyUp // KeyDown is the down arrow. KeyDown // KeyLeft is the left arrow. KeyLeft // KeyRight is the right arrow. KeyRight // KeyHome is the home key. KeyHome // KeyEnd is the end key. KeyEnd // KeyPageUp is the page-up key. KeyPageUp // KeyPageDown is the page-down key. KeyPageDown // KeyDelete is the delete key. KeyDelete // KeyInsert is the insert key. KeyInsert // KeyF1 is the F1 key. KeyF1 // KeyF2 is the F2 key. KeyF2 // KeyF3 is the F3 key. KeyF3 // KeyF4 is the F4 key. KeyF4 // KeyF5 is the F5 key. KeyF5 // KeyF6 is the F6 key. KeyF6 // KeyF7 is the F7 key. KeyF7 // KeyF8 is the F8 key. KeyF8 // KeyF9 is the F9 key. KeyF9 // KeyF10 is the F10 key. KeyF10 // KeyF11 is the F11 key. KeyF11 // KeyF12 is the F12 key. KeyF12 )
Non-printable keys.
type KeyMod ¶
type KeyMod uint8
KeyMod is a bitmask of modifier keys held during an event.
const ( // ModShift is the Shift key. ModShift KeyMod = 1 << iota // ModAlt is the Alt key, labelled Option on macOS. ModAlt // ModCtrl is the Control key. ModCtrl // ModSuper is the Super or Command key. ModSuper )
Modifier keys.
type KeyType ¶
type KeyType uint8
KeyType is what happened to a key.
Without the kitty keyboard protocol every event is KeyPress and autorepeat is indistinguishable from a second press; that is an accepted limitation of the xterm encoding, not a bug.
const ( // KeyPress is a key going down. It is the zero value, so a synthesized // EventKey is a press unless something says otherwise. KeyPress KeyType = iota // KeyRepeat is an autorepeat, distinguishable from a second press only // under the kitty keyboard protocol. KeyRepeat // KeyRelease is a key coming up. KeyRelease )
Key event types.
type Minimizable ¶
type Minimizable interface {
Widget
// MinSize returns the widget's smallest meaningful size in cells. It must be
// pure, must not depend on the current Bounds, and must be safe to call before
// the widget has ever been drawn.
MinSize() Size
}
Minimizable is an optional interface a Widget implements if it has a smallest size at which it can render something meaningful.
This is the existing Focusable pattern exactly: optional, so that adding it costs no widget anything, and discoverable by a type assertion. A widget that does not implement it is fully supported — there is no framework default minimum and no framework reaction (ADR 0007 §4).
MinSize is the size of the WHOLE widget INCLUDING its own chrome — a bordered Table with MinSize{20, 5} needs 20x5 cells, not a 20x5 content area. Pinning this here is deliberate: three authors would otherwise each decide whether their border counts, and every caller's arithmetic would then be wrong for some subset of the catalog.
The framework does nothing with a MinSize. What to do when the available space is below it is the application's decision, because only the application knows whether losing a table is acceptable (ADR 0007, rejected alternative: a framework-owned "too small" screen).
type Mouse ¶
type Mouse struct {
X, Y int
Button MouseButton
Action MouseAction
// Mod is the modifier state at the time of the event.
Mod KeyMod
}
Mouse is the payload of an EventMouse.
type MouseAction ¶
type MouseAction int
MouseAction is what happened to the button.
const ( // MousePress is a button going down. MousePress MouseAction = iota // MouseRelease is a button coming up. MouseRelease // MouseDrag is motion with a button held. MouseDrag // MouseMove is motion with no button held. MouseMove )
Mouse actions.
type MouseButton ¶
type MouseButton int
MouseButton identifies a mouse button.
const ( // MouseNone means no button, for motion events. MouseNone MouseButton = iota // MouseLeft is the primary button. MouseLeft // MouseMiddle is the middle button. MouseMiddle // MouseRight is the secondary button. MouseRight // MouseWheelUp is a wheel-up notch. MouseWheelUp // MouseWheelDown is a wheel-down notch. MouseWheelDown )
Mouse buttons.
type Rect ¶
Rect is an axis-aligned rectangle in cell coordinates. Aliased from buffer so that a layout result can be handed to a widget's Bounds() without conversion.
type Sink ¶
type Sink interface {
// Write appends bytes to the sink. A short write with a nil error is
// treated as an error by the renderer.
Write(p []byte) (int, error)
// Flush pushes buffered bytes to the underlying device.
Flush() error
}
Sink consumes the bytes the diff produces.
The renderer writes here and knows nothing else about its output. Two implementations exist in v1: the real terminal's writer, and a MemorySink that records frames for tests (ADR 0001).
type Size ¶
Size is a terminal width and height in cells. Aliased from buffer for the same reason as Rect.
type Terminal ¶
type Terminal interface {
// Size returns the terminal's current size in cells.
Size() (w, h int)
// EnterRawMode disables line buffering and echo. It is not idempotent:
// implementations must track their own state and make a second call a
// no-op, because an unbalanced LeaveRawMode leaves the user's shell broken.
EnterRawMode() error
// LeaveRawMode restores the terminal's mode on entry.
LeaveRawMode() error
// EnterAltScreen switches to the alternate screen buffer.
EnterAltScreen() error
// LeaveAltScreen switches back to the main screen buffer.
LeaveAltScreen() error
// Capabilities reports what the terminal supports, which selects both the
// colour-degradation rung and the box-drawing character set.
Capabilities() Caps
// Read reads input bytes. It returns io.EOF when the terminal is closed.
Read(p []byte) (int, error)
// ResizeEvents yields a value whenever the terminal is resized. The channel
// is closed when the terminal is.
ResizeEvents() <-chan Size
// Close releases the terminal. It must leave raw mode and the alternate
// screen even if the caller forgot to.
Close() error
}
Terminal controls the physical terminal.
Implementations are the real x/sys-backed terminal and a headless fake for CI. Nothing in the core depends on which: the renderer depends only on Sink, and widget tests depend only on Terminal plus Sink (ADR 0001).
type Widget ¶
type Widget interface {
// Bounds returns the widget's rectangle in screen cells. It must be safe to
// call before the widget has ever been drawn.
Bounds() Rect
// Draw writes the widget's cells into buf, which is clipped to Bounds.
// It is called every frame for every widget and is expected to be cheap
// and idempotent: writing the same state twice must produce the same cells.
Draw(buf *buffer.Buffer)
// Invalidate marks Bounds dirty. It must be safe to call from any
// goroutine (ADR 0003), which the buffer's dirty accumulator provides.
Invalidate()
// Handle offers the event to the widget and reports whether it consumed it.
// Events reach a widget only after the application's keymap has declined
// them (ADR 0009), so a binding in a keymap shadows a switch in this
// widget. A widget's own key handling is therefore the fallback, not the
// primary path, and a key that matters to both should be a command.
Handle(Event) bool
}
Widget is a retained node in the UI tree, exactly as ADR 0003 specifies it.
The four-method surface is the whole contract, and its smallness is the point: there is no reconciler, no virtual tree, no incremental-draw interface, and no message algebra. ADR 0003 argues each of those away explicitly — most importantly that requiring widget authors to implement correct incremental invalidation is a discipline Go cannot enforce, and that silent invalidation bugs are the worst class of bug a TUI can have.
The trade, recorded in ADR 0003 and accepted: Draw runs for every widget every frame, cheap today (~81 µs for 12,000 cells) with no lever to pull if it ever stops being cheap. The dirty-rectangle accumulator is the hook an incremental API would attach to, when that day comes.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package buffer implements TermMosaic's cell buffer: the padding-free 16-byte Cell, the packed Colour type, the Attr bitmask, and the row-major Buffer that widgets draw into.
|
Package buffer implements TermMosaic's cell buffer: the padding-free 16-byte Cell, the packed Colour type, the Attr bitmask, and the row-major Buffer that widgets draw into. |
|
cmd
|
|
|
capture
command
Command capture renders every widget in the TermMosaic catalog and writes the plain-text and HTML captures the documentation site displays.
|
Command capture renders every widget in the TermMosaic catalog and writes the plain-text and HTML captures the documentation site displays. |
|
examples
|
|
|
dashboard
command
Command dashboard is TermMosaic's showcase: one screen that composes nine widgets from the catalog and moves data through all of them.
|
Command dashboard is TermMosaic's showcase: one screen that composes nine widgets from the catalog and moves data through all of them. |
|
hello
command
Command hello is TermMosaic's runnable example: it clears the screen, draws a bordered block that fills the terminal, shows a live frame counter and the negotiated colour depth, and exits on q.
|
Command hello is TermMosaic's runnable example: it clears the screen, draws a bordered block that fills the terminal, shows a live frame counter and the negotiated colour depth, and exits on q. |
|
markets
command
|
|
|
search
command
|
|
|
Package geometry holds TermMosaic's coordinate types: the rectangle and the size, in cell coordinates.
|
Package geometry holds TermMosaic's coordinate types: the rectangle and the size, in cell coordinates. |
|
Package headless provides a terminal that touches no real device and a sink that exposes the resulting cell buffer.
|
Package headless provides a terminal that touches no real device and a sink that exposes the resulting cell buffer. |
|
Package input decodes a terminal's byte stream into termmosaic.Event values.
|
Package input decodes a terminal's byte stream into termmosaic.Event values. |
|
internal
|
|
|
ansi
Package ansi encodes the fixed subset of ANSI/VT sequences TermMosaic emits.
|
Package ansi encodes the fixed subset of ANSI/VT sequences TermMosaic emits. |
|
diff
Package diff implements TermMosaic's two-tier cell diff: a per-row skip on top of a per-cell pass.
|
Package diff implements TermMosaic's two-tier cell diff: a per-row skip on top of a per-cell pass. |
|
docsgen
Package docsgen renders every widget in the catalog and writes the captures the documentation site displays.
|
Package docsgen renders every widget in the catalog and writes the captures the documentation site displays. |
|
Package keymap is TermMosaic's command layer: a named action an application can invoke, and a mapping from a decoded Event to it.
|
Package keymap is TermMosaic's command layer: a named action an application can invoke, and a mapping from a decoded Event to it. |
|
Package layout implements TermMosaic's constraint-based layout solver.
|
Package layout implements TermMosaic's constraint-based layout solver. |
|
Package render implements TermMosaic's hybrid renderer.
|
Package render implements TermMosaic's hybrid renderer. |
|
Package term implements the real terminal backend: a termmosaic.Terminal driving a tty and a termmosaic.Sink writing to it.
|
Package term implements the real terminal backend: a termmosaic.Terminal driving a tty and a termmosaic.Sink writing to it. |
|
Package virtual is the scroll engine for data widgets: it renders a window of rows out of a much larger collection, in constant time per frame regardless of how large the collection is.
|
Package virtual is the scroll engine for data widgets: it renders a window of rows out of a much larger collection, in constant time per frame regardless of how large the collection is. |
|
widgets
|
|
|
basic
Package basic provides the two text widgets: Text, which writes styled spans on one line, and Paragraph, which wraps them.
|
Package basic provides the two text widgets: Text, which writes styled spans on one line, and Paragraph, which wraps them. |
|
block
Package block provides Block, the catalog's only owner of borders and titles.
|
Package block provides Block, the catalog's only owner of borders and titles. |
|
data
Package data provides the catalog's scrolling data widgets: List, Table, Tree and Pager.
|
Package data provides the catalog's scrolling data widgets: List, Table, Tree and Pager. |
|
dialog
Package dialog provides Dialog: the catalog's modal — a box with a title, a body, and either a row of buttons or a list of choices.
|
Package dialog provides Dialog: the catalog's modal — a box with a title, a body, and either a row of buttons or a list of choices. |
|
form
Package form provides the input widgets: TextInput, TextArea, Select, Checkbox, Radio, Toggle, Tabs, Button and KeyHint.
|
Package form provides the input widgets: TextInput, TextArea, Select, Checkbox, Radio, Toggle, Tabs, Button and KeyHint. |
|
menu
Package menu provides Menu, a keyboard-driven menu with nested submenus to arbitrary depth.
|
Package menu provides Menu, a keyboard-driven menu with nested submenus to arbitrary depth. |
|
split
Package split provides Split, the pane composer: it solves one layout and hands each pane a rectangle.
|
Package split provides Split, the pane composer: it solves one layout and hands each pane a rectangle. |
|
viz
Package viz provides the catalog's measurement and display widgets: ProgressBar, Gauge, Meter, Sparkline and BarChart.
|
Package viz provides the catalog's measurement and display widgets: ProgressBar, Gauge, Meter, Sparkline and BarChart. |
|
widgettest
Package widgettest renders widgets through the whole stack in a test, with no terminal involved.
|
Package widgettest renders widgets through the whole stack in a test, with no terminal involved. |
