render

package
v0.7.6 Latest Latest
Warning

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

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

Documentation

Overview

Package render holds the shared bus-journal rendering machinery: decoding list_events pages, resolving the alias→label map, and rendering rows into the WHO/WHAT vocabulary the humancli `events`/`watch` commands and the station TUI's feed pane both use. It has no daemon-transport code of its own — callers hand in a small Caller so render stays a peer of humancli and station rather than a third copy of daemon-client plumbing — and it uses internal/display for the one canonical sanitizer.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func LoadLabels

func LoadLabels(c Caller) map[string]string

LoadLabels fetches the current alias→label map, best-effort: on any error the map is nil and aliases render as-is. Labels are LIVE (a live session's label is re-read from tmux exactly like `muster agents`), so callers read in today's terms; a dead session's stored label snapshot stands in.

func PadDisplay

func PadDisplay(s string, w int) string

PadDisplay right-pads s with spaces to w DISPLAY COLUMNS (not rune count), matching the units display.Width and display.Sanitize already use. fmt's "%-*s" pads by rune count, which misaligns columns and blows the line budget once a field holds wide (CJK/fullwidth) runes — a rune-counted pad adds more spaces than the display actually needs, since each wide rune already occupies 2 columns worth of the target width. Every column whose content can legitimately hold non-ASCII (WHO, and — for symmetry — TIME/ THREAD, which are ASCII in practice but should never silently rely on that) goes through this helper instead.

Types

type Caller

type Caller interface {
	Call(op string, args map[string]any) (json.RawMessage, error)
}

Caller sends one daemon op and returns its Data as JSON, or an error if the transport failed or the daemon reported !OK. humancli and station each wrap their own daemon-client plumbing (both ultimately through internal/client.Call) behind this interface, so render never dials a socket itself.

type EventRow

type EventRow struct {
	ID       int64  `json:"id"`
	TS       int64  `json:"ts"`
	Kind     string `json:"kind"`
	Agent    string `json:"agent"`
	Target   string `json:"target"`
	ThreadID int64  `json:"thread_id"`
	Count    int    `json:"count"`
	Detail   string `json:"detail"`
	Subject  string `json:"subject"`
	// Intent is the event's thread's EFFECTIVE intent (store's effectiveIntent),
	// joined at query time exactly like Subject: "" (unspecified) | "fyi" |
	// "reply-requested" | "action-requested". what() renders it as a tag on
	// send/task rows.
	Intent string `json:"intent"`
}

EventRow mirrors store.Event's wire JSON.

type EventsPage

type EventsPage struct {
	Events []EventRow `json:"events"`
	MaxID  int64      `json:"max_id"`
}

EventsPage is the decoded {events, max_id} envelope list_events returns.

func FetchEvents

func FetchEvents(c Caller, agent, kind string, threadID, afterID int64, limit int) (EventsPage, error)

FetchEvents calls list_events with the given filters. afterID < 0 selects backlog mode (send backlog:true + limit, omit after_id); afterID >= 0 selects follow mode (send after_id as a decimal string, omit backlog) — never both in the same call.

type Renderer

type Renderer struct {
	// contains filtered or unexported fields
}

Renderer holds the display state shared by events, watch, and the station feed pane: the alias→label map, column widths, and time format. Widths are seeded from the rows about to be printed (events) or the backlog (watch/station) and only grow, so columns stay aligned to real content instead of guessed fixed widths while the feed streams.

func NewRenderer

func NewRenderer(rows []EventRow, labels map[string]string, aliases, fullTime bool, width int) *Renderer

NewRenderer sizes columns from rows. labels may be nil (aliases render as-is). width <= 0 falls back to $COLUMNS, then defaultLineWidth.

func (*Renderer) HasLabel

func (r *Renderer) HasLabel(alias string) bool

HasLabel reports whether alias currently has a non-empty entry in the renderer's label map — used by streaming callers (watch, station's feed) to decide whether the map needs a refresh for a newly-seen agent.

func (*Renderer) Header

func (r *Renderer) Header(w io.Writer)

Header writes the column header line shared by events, watch, and station.

func (*Renderer) Line

func (r *Renderer) Line(w io.Writer, e EventRow)

Line writes exactly one line for e — safe for line-by-line streaming (no long-lived tabwriter that only aligns at Flush). The WHAT column is capped to the remaining terminal width so a row can never wrap. Column padding is display-width based throughout (PadDisplay), matching the `used` budget math below, which sums display-width column widths — the two must stay in the same unit or the WHAT truncation cap drifts from the line's real rendered width.

func (*Renderer) SetAliases

func (r *Renderer) SetAliases(aliases bool)

SetAliases toggles raw-alias mode (true = ignore labels, show aliases as-is) — station's `a` key flips this at runtime; every other caller sets it once at construction via NewRenderer.

func (*Renderer) SetLabels

func (r *Renderer) SetLabels(labels map[string]string)

SetLabels replaces the renderer's label map (e.g. after a streaming caller refreshes it for a newly-registered agent).

func (*Renderer) SetWidth

func (r *Renderer) SetWidth(width int)

SetWidth updates the renderer's total line-width budget — station's bordered feed pane calls this on every render so Line()'s WHAT-column cap tracks the pane's actual INNER width rather than the whole terminal's (a pane narrower than the terminal must still never let a line run past its own box). Mirrors SetLabels/SetAliases' role as a runtime knob on an otherwise construction-time Renderer. width <= 0 falls back exactly like NewRenderer's own construction-time default ($COLUMNS, then defaultLineWidth) — callers that don't yet know a real pane width (no tea.WindowSizeMsg has landed) get the same behavior NewRenderer always had.

Jump to

Keyboard shortcuts

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