build

package
v0.2.0-rc.2 Latest Latest
Warning

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

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

Documentation

Overview

Package build constructs live tuilib components and a layout.Node tree from the YAML config. The output is owned by the screen, which holds the component pointers for state preservation across theme swaps.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ApplyData

func ApplyData(c *Component, data any, th theme.Theme)

ApplyData maps a data source response onto a bound component. Component shape and binding fields decide how the response is consumed:

  • list Cfg.Item (dot-path to display string)
  • table Column.Value (dot-path per column)
  • inspector field.Path (dot-path per field; static Value preserved when Path is empty)

The iterable root is selected via Cfg.Source's root path (already applied in screen.Model before this is called) so data is the already-rooted value. All three updates happen in place; tuilib's SetItems/SetRows/SetFields preserve cursor + filter + InitialDepth pre-expansion across refreshes.

func ApplyStreamLine

func ApplyStreamLine(c *Component, line string, th theme.Theme)

ApplyStreamLine handles one line from a streaming source bound to a non-logview component. Logview-bound streams are handled directly in screen.handleStream (just Append the line); this function exists for the cases that need parsing + projection.

Two table modes, decided by whether Cfg.RowKey is set:

  • Ring buffer (no RowKey): prepend each event as a new row, trimmed to Cfg.MaxRows. The live-tape pattern — chat, log overlays, recent-trades feeds.
  • Keyed upsert (RowKey set): each event identifies the row it updates via Cfg.RowKey. Matching key → update in place; unseen key → append. The L1 order-book / status-grid pattern.

In both modes, lines that don't parse as JSON or that project to all-empty cells are skipped silently — that's how diagnostic / handshake frames ("(connecting to …)", an empty subscription_succeeded confirmation) drop out of the live data view. Color rules apply per cell exactly as they do on a fetch-driven table.

func Substitute

func Substitute(s string, sel Selection) string

Substitute resolves ${selection.*} and ${env.*} tokens in s against the given Selection. Used by push-site `bind:` resolution to turn a template like `${selection.Namespace}` into the focused row's actual namespace value before that value is passed into a destination source's parameter binder.

func SubstituteAll

func SubstituteAll(argv []string, sel Selection, prompts map[string]string) []string

SubstituteAll runs ${selection.*}/${env.*}/${prompt.*} substitution on every entry in argv and returns a new slice. Used by action dispatch — the run argv references the focused row's selection AND any prompt values entered moments before dispatch. Pass nil for prompts when there were none.

func SubstituteCursor

func SubstituteCursor(s string, cursor Selection) string

SubstituteCursor resolves both ${cursor.*} (against the live cursor Selection) AND every other namespace (env, prompt-less) so a cursor bind template like `${cursor.Name}-${env.CLUSTER}` composes cleanly. Selection tokens pass through as literals — this is the runtime path, not the push-time one, and no push-site sel is in scope.

func SubstituteScreen

func SubstituteScreen(s *cfg.Screen, components map[string]*cfg.Component, sources map[string]*cfg.Source, sel Selection, params map[string]string) (*cfg.Screen, map[string]*cfg.Component, map[string]*cfg.Source, error)

SubstituteScreen returns deep-copied Screen + Components + Entries with every ${selection*} token replaced from sel. Original config is left untouched, so each push can re-substitute against a fresh selection. Leaf-kind entries go through the same substitution so a child's URL / headers / body can reference the parent row.

When params is non-nil, every cloned leaf entry that declares `parameters:` ALSO has BindLeafParams called against the subset of params it actually declares. This is how the explicit push-site bind: block feeds into the destination screen's parameterized sources. Missing required params surface as an error so the caller (tryPush) can pop an alert instead of building a broken screen.

Operator entries (filter, sort, …) are cloned without substitution — they don't carry templated string fields and the expression language is bind-time, not screen-time.

Pass nil for params to skip parameter binding (single-screen New and initial multi-screen construction — no push site context). Parameterized sources in that path will be left with unresolved ${params.*} templates and will fail at fetch with a clear URL.

Types

type Component

type Component struct {
	Cfg  *cfg.Component
	Kind Kind

	// Exactly one of these is non-nil based on Kind.
	List      *list.Model
	Table     *table.Model
	Logview   *logview.Model
	Tree      *tree.Model
	Inspector *inspector.Model
	Textview  *textview.Model

	// StreamRows is the ring buffer used by KTable components bound to
	// a streaming source. Newest events first; trimmed to Cfg.MaxRows
	// (defaulted to 100 for streaming bindings) on every append. nil
	// for non-streaming tables and non-table kinds.
	StreamRows []any
}

Component is a live, themed component pointer plus the originating config — kept so SetTheme can rebuild the same component against a new theme.

func NewComponent

func NewComponent(c *cfg.Component, th theme.Theme) (*Component, error)

NewComponent builds a Component from a config leaf and the active theme.

func (*Component) Rebuild

func (c *Component) Rebuild(th theme.Theme)

Rebuild reconstructs the component against a new theme, preserving as much in-flight state as the component exposes accessors for.

type Kind

type Kind int

Kind enumerates the supported leaf component types.

const (
	KList Kind = iota
	KTable
	KLogview
	KTree
	KInspector
	KTextview
)

type Selection

type Selection struct {
	// String is the primary representation: a list's selected item, or a
	// table's first cell. Drives bare ${selection}.
	String string
	// Cells are the full row of cells for a table source. Drives
	// ${selection.N} (1-based) and ${selection.COLNAME}.
	Cells []string
	// Columns are the parallel column titles for Cells. Used to resolve
	// ${selection.COLNAME} to the right cell.
	Columns []string
}

Selection is the data captured from a source component (list or table) when an on_enter binding fires. It seeds the ${selection} token in the pushed screen's config.

type Tree

type Tree struct {
	Root       *cfg.Node
	Components map[string]*Component
	Order      []string // names, in the order encountered while walking Root
}

Tree owns the live components and the layout config they're wired into. Components are keyed by the name used in the layout's `component:` refs; Order keeps an iteration order for fanout (Update/SetTheme/Help/etc.).

func Build

func Build(root *cfg.Node, defs map[string]*cfg.Component, th theme.Theme) (*Tree, error)

Build instantiates every component defined in the config (lazily — only names referenced by the layout are built) and wires the layout tree.

func (*Tree) All

func (t *Tree) All() []*Component

All returns components in walk order — handy for screen-level fanout.

func (*Tree) RenderNode

func (t *Tree) RenderNode() layout.Node

RenderNode walks the config tree producing a live layout.Node tree. Component leaves are resolved by name through Tree.Components.

Jump to

Keyboard shortcuts

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