Documentation
¶
Overview ¶
Package present is the sole home for charm.land/lipgloss/v2 styling (D-01). It consumes already-computed plain data (query.StatusResult, the files result struct) and emits colorized, sectioned output; internal/query and internal/mcp never import charm — the boundary the TUI-01 archtest (internal/cli/present/archtest) enforces at build time.
present must NOT read the process environment or probe terminal state itself — real fd/env values are read only at the RunE call sites in internal/cli (D-03).
The shared style palette used to live here as three unexported package-level lipgloss.Style variables. It now folds into palette.go's Palette, built per call by NewPalette(dark bool) from the resolver's single background-detection answer (D-05) — no mutable package-level style, no setter. Section headings that used to render via a dedicated underlined variable now render via Palette.Header; there is no eighth role.
Index ¶
- func ChoosePresentation(isTTY bool, noColor string) bool
- func KV(w io.Writer, pal Palette, label, value string) error
- func Line(w io.Writer, pal Palette, role Role, text string) error
- func Lines(w io.Writer, pal Palette, role Role, texts ...string) error
- func NewLineWriter(w io.Writer, pal Palette, role Role) io.Writer
- func RenderAffected(r query.AffectedResult, pal Palette, w io.Writer) error
- func RenderCallees(r query.CalleesResult, pal Palette, w io.Writer) error
- func RenderCallers(r query.CallersResult, pal Palette, w io.Writer) error
- func RenderExplore(r query.ExploreResult, pal Palette, w io.Writer) error
- func RenderFiles(r query.FilesResult, pal Palette, w io.Writer) error
- func RenderHelp(c *cobra.Command, pal Palette, w io.Writer) error
- func RenderImpact(r query.ImpactResult, pal Palette, w io.Writer) error
- func RenderNode(d query.NodeDetail, pal Palette, w io.Writer) error
- func RenderNotice(notice string, pal Palette, w io.Writer) error
- func RenderSearch(locs []query.Location, pal Palette, w io.Writer) error
- func RenderSearchFull(nodes []*schema.Node, pal Palette, w io.Writer) error
- func RenderStatus(r query.StatusResult, projectPath string, pal Palette, w io.Writer) error
- type Palette
- type Progress
- type Role
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ChoosePresentation ¶
ChoosePresentation reports whether the pretty (lipgloss) branch should render, per D-04/D-05: isTTY must be true AND NO_COLOR must be unset/empty. It is pure and side-effect-free — it must NOT read the process environment or probe terminal state itself (04-03's colorflag.go resolver is the one place that happens now). Real fd/env values are read only at the RunE call sites (internal/cli/status.go, files.go, init.go, index.go, sync.go — D-03), which makes this the one shared, unit-testable branch selector every call site wires identically.
func KV ¶ added in v0.14.0
KV writes one "<label> <value>\n" row: pal.Label.Render(label) + " " + pal.Value.Render(sanitizeControl(value)) + "\n" (D-08) — label itself is never sanitized (callers pass a fixed literal, never user data, exactly like present/status.go's writeStatLine convention).
func Line ¶ added in v0.14.0
Line writes one styled, sanitized line (D-08): pal.Style(role).Render( sanitizeControl(text)) followed by a single trailing newline. text is sanitized in full before styling — Line never trusts caller-supplied text to already be free of embedded ANSI/OSC escapes or newlines (CR-01); an empty text renders to a bare "\n", matching fmt.Fprintln(out, "").
func Lines ¶ added in v0.14.0
Lines calls Line once per entry in texts, in order. Zero texts writes nothing.
func NewLineWriter ¶ added in v0.14.0
NewLineWriter returns an io.Writer that styles each complete "\n"-terminated line written to it in role, sanitizing control runes first exactly like Line. A trailing, non-newline-terminated partial segment from any given Write is sanitized but passed through UNSTYLED and is never buffered across calls (D-08) — used by later plans for stderr banners and pass-through writers that control write granularity themselves.
func RenderAffected ¶ added in v0.14.0
RenderAffected renders r — the affected shape: zero entries → "no test files affected\n" (Label-styled); else "N affected test(s):\n" (N → Count) then each entry indented " ". Mirrors affected.go's human (post-quiet) plain branch exactly.
func RenderCallees ¶ added in v0.14.0
RenderCallees renders r — the callees shape: header "Symbol calls N callee(s):\n", then each entry indented " ". Mirrors callees.go's plain branch exactly.
func RenderCallers ¶ added in v0.14.0
RenderCallers renders r — the callers shape: header "Symbol has N caller(s):\n" (Symbol → Value, N → Count), then each entry indented " " via writeLocationLine. Mirrors callers.go's plain branch exactly.
func RenderExplore ¶ added in v0.14.0
RenderExplore writes a lipgloss-styled rendering of r — the SAME sections, order and wording as query.RenderExplore's (and, for the empty case, exploreZeroResult's) markdown (D-07): hue replaces markdown syntax (**, backticks, "> ", fences), nothing is re-laid-out. r is consumed read-only; every repo/user-derived string passes sanitizeControl before styling (CR-01). present reads no file — source bytes come only from r.Sources (T-04-16).
func RenderFiles ¶
RenderFiles writes a lipgloss-styled rendering of r to w, mirroring the plain branch selection in internal/cli/files.go's RunE: r.Format == "tree" walks the FileTreeNode slice via writeFileTree; otherwise each FileEntry is rendered as a styled "Path (Language)" line. r is consumed read-only — the tree structure and file ordering are never recomputed here (D-02). Callers gate this behind ChoosePresentation (D-03) and build pal via NewPalette(mode.Dark) at the RunE boundary; RenderFiles itself never reads a TTY/env value.
func RenderHelp ¶ added in v0.14.0
RenderHelp writes a lipgloss-styled rendering of c's help text to w, replicating cobra's own stock help template's structure and wording (D-14): Long/Short, Usage:, then — for a command with subcommands — each of c.Groups() in registration order as a titled section listing its visible members, any visible ungrouped commands last under "Additional Commands:" (cobra's own wording, cited not tested per D-00), Flags:, Global Flags: when any exist, and the trailer. A subcommand with no groups of its own (every group belongs to the command that registered it via AddGroup, which is always root in this tree) renders no group section at all — only its own Usage:/flags.
Every piece of text RenderHelp renders is this project's own static command/flag metadata (names, Short/Long strings, flag usage text) — never a user- or filesystem-derived value — so no sanitizeControl call is needed here (contrast present/status.go's project-path/worktree fields, which ARE adversarial and are sanitized before styling, CR-01).
func RenderImpact ¶ added in v0.14.0
RenderImpact renders r — the impact shape: header "Symbol impact (depth=D): N node(s), M edge(s)\n" with D/N/M each styled Count, then each entry indented " ". Mirrors impact.go's plain branch exactly.
func RenderNode ¶ added in v0.14.0
RenderNode writes a lipgloss-styled rendering of d — the SAME sections, order and wording as query.RenderNode/RenderNodeMultiDef's markdown (D-07). Every mode consumes its detail struct read-only; present reads no file (T-04-16) — File mode's source is d.File.Source, SingleDef's calls/calledBy are exactly what the engine fetched, MultiDef fetches each candidate lazily via d.Multi.Definition(n), stopping at HARD_CAP.
func RenderNotice ¶ added in v0.14.0
RenderNotice renders the WORK-02 worktree notice: empty writes nothing; non-empty is styled Warning line-by-line (preserving blank lines and the trailing newline structure exactly) so the stripped bytes equal query.WorktreeNotice's plain string verbatim.
func RenderSearch ¶ added in v0.14.0
RenderSearch renders locs — the search default shape (D-01), one writeLocationLine per entry, no header, no indent. Mirrors search.go's plain loop over query.Location exactly.
func RenderSearchFull ¶ added in v0.14.0
RenderSearchFull renders nodes — the search --full shape (D-01): per node, line 1 via writeNodeLine, then line 2 (" " + QualifiedName, plus " " + Signature when non-empty) — the exact concatenation search.go's renderFullLine builds.
func RenderStatus ¶
RenderStatus writes a lipgloss-styled rendering of r to w, walking the SAME section order as query.RenderStatusText (header → Project → worktree warning when present → Index Statistics → Nodes by Kind → Edges by Kind → Files by Language → advisories) with pal's seven roles applied as structural chrome only (D-01/D-02/D-05). r is consumed read-only: counts, sort order, and wording are never re-derived here — only styling is added. Callers gate this behind ChoosePresentation (D-03) and build pal via NewPalette(mode.Dark) at the RunE boundary; RenderStatus itself never reads a TTY/env value.
v0.11.0 Phase 1 (D-01/D-04) added a new breakdown section between Nodes by Kind and Files by Language, matching query.RenderStatusText's addition. r.EdgesByKind may already be dense (internal/cli/status.go applies --all-kinds's densification before choosing this renderer or the piped one) — this file's edgeCounts, like sortedCounts, never re-derives that decision; it only renders whatever map it is given.
Types ¶
type Palette ¶ added in v0.14.0
Palette holds exactly seven lipgloss.Style values, one per semantic role (D-05). Every Render* takes a Palette as an explicit parameter; there is no mutable package-level style and no setter — the palette is built once per RunE call from the resolver's single Dark answer (NewPalette).
func NewPalette ¶ added in v0.14.0
NewPalette builds a Palette from ONE lipgloss.LightDark(dark) closure over paletteHex — Header/Warning/Error are bold, Label is faint, and Value/Path/Count carry no additional decoration beyond their hue (D-05).
type Progress ¶
type Progress struct {
// contains filtered or unexported fields
}
Progress is a hand-rolled, non-interactive, TTY-gated progress indicator (TUI-05, D-08/D-09). It writes exactly one lipgloss-styled spinner frame per progressTickInterval tick to the io.Writer supplied at construction — callers at the internal/cli RunE boundary MUST pass os.Stderr, never os.Stdout (D-08: progress must never reach the stdout/MCP JSON-RPC stream). Progress reads no stdin and never spawns a bubbletea Program — it is built from lipgloss styling plus a stdlib time.Ticker only (D-09/D-13), never charm.land/bubbles or charm.land/bubbletea.
Stop() terminates the ticker goroutine deterministically (T-06-08): it closes a stop channel and blocks until the goroutine has actually returned before clearing the line, so no goroutine is ever left running after Stop returns — the concurrency guarantee an interrupted or parallel index run depends on.
func NewProgress ¶
NewProgress constructs a Progress that writes exclusively to w. w is never defaulted or substituted internally — Progress has no reference to os.Stdout anywhere in its implementation; the TTY/stream choice is entirely the caller's (D-08).
func (*Progress) Start ¶
Start launches the single ticker goroutine and begins rendering frames labeled with label. Calling Start on an already-running Progress is a no-op (it does not launch a second goroutine).
func (*Progress) Stop ¶
func (p *Progress) Stop()
Stop terminates the ticker goroutine deterministically: it closes the stop channel and blocks until the goroutine has confirmed it returned (via doneCh) before Stop itself returns — by the time Stop returns, the line-clear sequence has already been written and the goroutine is guaranteed gone (no leak). Calling Stop on a Progress that was never started, or calling it a second time, is a safe no-op (idempotent).