kit

package
v1.187.0 Latest Latest
Warning

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

Go to latest
Published: Aug 16, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

* ChatCLI - UI kit: badges * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0

* ChatCLI - UI kit: box border geometry * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0 * * The two border builders every card in the app uses, moved verbatim from * cli/agent. They return PLAIN (uncolored) lines: color belongs to the * caller — the agent wrappers keep their legacy ANSI-constant coloring * byte-identical, and kit-native components colorize through theme roles. * targetWidth is always the EXACT visible width to produce, measured with * VisibleLen (lipgloss.Width) on the matching body so every border row * agrees with every body row even under emoji-width drift.

* ChatCLI - UI kit: shared presentation components * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0 * * Package kit is the single presentation vocabulary for everything chatcli * prints: one width helper, one style API over ui/theme roles, one status * glyph set, and small pure components (Notice, Rule, Header, Badge, KVTable, * List). It exists because the CLI accreted five terminal-width helpers, * three box idioms and three success glyphs — every surface reinvented * presentation, which is exactly what read as unpolished. * * Contracts: * - LEAF package: may import ui/theme, lipgloss, runewidth, x/term and the * stdlib — never i18n or anything under cli/. Components receive * already-translated strings and only decorate them, so the package can * be shared by cli and cli/agent and stays out of the i18n gates. * - Components are pure functions returning strings; printing (and the * single blank line that separates top-level blocks) belongs to the call * site. * - Color always flows through a theme.Role. On colorless profiles every * escape vanishes (theme.ANSI/Reset return "") so piped output is plain. * - Spacing grid: 2-space indent, right margin of RightMargin columns, * components never emit leading/trailing blank lines.

* ChatCLI - UI kit: canonical status glyphs * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0 * * One glyph per meaning, defined once. The audit found three success marks, * four error marks and eight bullet variants in the wild; this file is the * whole vocabulary from now on. Every glyph is deliberately chosen WITHOUT * the Unicode Emoji property, so runewidth (StrictEmojiNeutral=false) * measures them exactly as terminals render them — 1 cell — keeping columns * and box borders aligned on every platform, including Windows. Colored * emoji remains legal only as a box Icon or in the welcome logo; the U+FE0F * variation selector is banned everywhere (it silently flips a glyph from 1 * to 2 cells).

* ChatCLI - UI kit: key/value tables * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0

* ChatCLI - UI kit: lists * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0

* ChatCLI - UI kit: status notices * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0

* ChatCLI - UI kit: horizontal rules and headers * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0

* ChatCLI - UI kit: role-based styling * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0

* ChatCLI - UI kit: terminal width * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0

* ChatCLI - UI kit: ANSI-aware wrapping * Copyright (c) 2024 Edilson Freitas * License: Apache-2.0 * * The three wrap flavors every boxed surface uses, moved verbatim from * cli/agent so chat, agent and command surfaces share one wrap math: * * - WrapText: prose word-wrap (collapses whitespace) — reasoning, replies. * - WrapStructured: glamour-rendered bodies — preserves (and dedents) * indentation so YAML/JSON/code keeps its shape. * - WrapPreserve / WrapStreamLine: raw tool output — every fitting line * verbatim, only overflowing lines break, indentation repeated.

Index

Constants

View Source
const (
	// RightMargin is the number of columns every full-width component leaves
	// free on the right so borders never touch the terminal edge (or a
	// native scrollbar). Previously scattered as ad-hoc "-2" and "-1".
	RightMargin = 2

	// MinContentWidth is the floor below which components stop shrinking and
	// accept horizontal overflow — matching the response envelope's historic
	// minimum so extremely narrow terminals degrade the same way everywhere.
	MinContentWidth = 40
)

Variables

This section is empty.

Functions

func Badge

func Badge(text string, r theme.Role) string

Badge renders a small bracketed chip — "[text]" — in the given role. Used for value qualifiers like source tags and "(default)" hints where a full component would be noise.

func BilateralBorder

func BilateralBorder(lc, rc rune, leftLabel, rightLabel string, targetWidth int) string

BilateralBorder constructs a horizontal border with optional left and right labels embedded between the corner glyphs:

<lc>─ LeftLabel ──────── RightLabel ─<rc>

Layout rules (in visible columns):

  • <lc> + '─' + leftLabel if leftLabel != "" (else <lc> + '─')
  • fill of '─' to absorb remaining width
  • rightLabel + '─' + <rc> if rightLabel != "" (else '─' + <rc>)

A degenerate case where the labels alone exceed targetWidth falls back to a minimal border (the labels survive; the fill goes to zero).

func Bold

func Bold(s string, r theme.Role) string

Bold renders s bold in the role's color — the "title weight" of the typographic hierarchy. No escapes are emitted on colorless profiles.

func Colorize

func Colorize(s string, r theme.Role) string

Colorize wraps s in the role's color span. On colorless profiles both the color and the reset are empty strings, so the output is exactly s.

func ContentWidth

func ContentWidth() int

ContentWidth is the width components should actually occupy: the terminal width minus the right margin, clamped to MinContentWidth.

func Dim

func Dim(s string) string

Dim renders s in the muted role — the "metadata weight" of the hierarchy.

func HardBreakWord

func HardBreakWord(w string, limit int) []string

HardBreakWord parte uma palavra (sem espaços) em pedaços cuja largura visível não excede limit. A quebra é por runa, medindo com lipgloss.Width, para não cortar sequências UTF-8 / wide-runes no meio. Retorna ao menos um elemento.

func Header(title string) string

Header renders a section title in the title weight of the hierarchy.

func KVTable

func KVTable(rows []KVRow) string

KVTable renders aligned key/value rows on the grid. The key column width is measured from the TRANSLATED keys at render time (VisibleLen), so alignment holds in every locale — unlike the fixed "%-32s" and the English-tuned literal spaces it replaces. Keys render dim; values in their role (or plain).

func List

func List(items []string) string

List renders items as a bulleted list on the grid:

· item

func Notice

func Notice(l Level, msg string) string

Notice renders a one-line status message on the grid: two-space indent, role-colored glyph, message. Error and warning messages are tinted with the glyph's role so the whole line reads as one signal; success and info keep the message in default text (the glyph alone carries the state).

func NoticeRole

func NoticeRole(g Glyph, msg string, r theme.Role) string

NoticeRole renders a notice-shaped line with an arbitrary glyph and role — the escape hatch for surfaces with domain-specific markers (e.g. the running spinner summary) that still want the grid alignment.

func Numbered

func Numbered(items []string) string

Numbered renders items as a numbered list with right-aligned numbers so double-digit lists keep their text column straight:

  1. item
  2. item

func PadRight

func PadRight(s string, cols int) string

PadRight pads s with spaces to the given visible width. Measurement is ANSI/emoji-aware (VisibleLen), unlike fmt's "%-Ns" which counts BYTES and silently misaligns any accented or multibyte label — the root cause of column drift in pt-BR surfaces. Strings already at or past the width are returned unchanged.

func Rule

func Rule() string

Rule renders a dim full-content-width horizontal rule — the single replacement for the fixed 39/50/60/70/80-column separators.

func RuleHeader

func RuleHeader(left, right string, width int) string

RuleHeader renders a full-width bilateral rule with pre-formatted labels embedded — the borderless reply header of the "sóbrio" treatment:

── {left} ─────────────── {right} ──

Labels arrive pre-colored (with any breathing spaces the caller wants); the dashes are dim. width <= 0 resolves to ContentWidth; a positive width pins the line for tests. Degenerate widths fall back to a minimal rule with the labels intact.

func RuleTitled

func RuleTitled(title string) string

RuleTitled renders a rule with an inline title:

── Title ────────────────

The title is bold in the header role; the dashes are dim. The line always spans ContentWidth.

func SplitLeadingIndent

func SplitLeadingIndent(line string) (indent int, codes string, content string)

SplitLeadingIndent separa a indentação inicial de uma linha do seu conteúdo, de forma ANSI-aware. O glamour emite sequências de cor de largura-zero ANTES (e entre) os espaços de indentação do markdown renderizado — então um strings.TrimLeft(" \t") reportaria indentação zero em YAML/JSON/código vindos do glamour. Retorna a largura visível do indent em colunas, os códigos ANSI vistos na região inicial (re-anexados ao conteúdo para que o primeiro token preserve a cor) e o conteúdo restante.

func StripANSI

func StripANSI(s string) string

StripANSI removes CSI color escapes so width and emptiness checks see plain text. Loop-based to avoid a regex dependency on hot render paths.

func StripVS16

func StripVS16(s string) string

StripVS16 removes every U+FE0F variation selector from s. Used by renderers to sanitize legacy catalog values during the migration; the selector flips glyph width between terminals and breaks column math.

func Style

func Style(r theme.Role) lipgloss.Style

Style returns a lipgloss style whose foreground is the role's color under the active theme and profile — the lipgloss counterpart of Colorize, mirroring the palette overlay's style(role) pattern.

func Subheader

func Subheader(title string) string

Subheader renders a secondary title in the metadata weight.

func Sym

func Sym(g Glyph) string

Sym renders the glyph in its role color — the everyday call.

func TermWidth

func TermWidth() int

TermWidth returns the live terminal width in columns, or fallbackWidth when stdout is not a terminal. Queried per call so live resizes are honored by the next render.

func TitledTopBorder

func TitledTopBorder(header string, targetWidth int) string

TitledTopBorder produces a `╭── header ─────╮` line whose VISIBLE width equals targetWidth. The two padding rules cover the two ways the header can fall short of the card width:

  • normal case: header fits, fill with dashes
  • header longer than card: emit a minimal top without filling (the card still closes at the right width because callers honor the body measurement; 1-2 cols of overflow is accepted degradation)

func TrimBlankBorderRows

func TrimBlankBorderRows(rows []string) []string

TrimBlankBorderRows drops fully-blank rows from the leading and trailing edges of a wrapped-text slice. A row is "blank" when it has zero visible width — color codes alone don't count as visible content. Blank rows in the MIDDLE are preserved so paragraph breaks the author put in markdown survive.

func TrimBlankBoxBodyRows

func TrimBlankBoxBodyRows(rendered string) string

TrimBlankBoxBodyRows removes fully-empty content rows directly adjacent to the top or bottom border of a lipgloss-rendered box. An empty row looks like "│ │" — same width as the sides but zero printable content between them. Middle blanks are kept (author-intended paragraph breaks survive).

func Truncate

func Truncate(s string, maxCols int) string

Truncate clamps s to at most maxCols visible columns, preserving ANSI color sequences and appending an ellipsis plus a reset when content was dropped so styling never bleeds into the next line.

func VisibleLen

func VisibleLen(s string) int

VisibleLen measures the display width of s in terminal cells, ignoring ANSI escapes. This is the kit's single measurement path (lipgloss.Width), shared with the agent renderer so every component agrees on geometry.

func WrapPreserve

func WrapPreserve(text string, limit int) []string

WrapPreserve quebra texto preservando a estrutura: cada linha que cabe no limite é mantida exatamente como está (indentação e espaçamento de colunas intactos) e só as que estouram são quebradas, repetindo a indentação nas continuações — usada em output cru de tool (YAML/JSON/tabelas), onde colapsar whitespace como o word-wrap de prosa destruiria o layout.

func WrapStreamLine

func WrapStreamLine(line string, width int) []string

WrapStreamLine quebra UMA linha de output cru de tool na largura visível informada. Diferente de WrapText, NÃO colapsa espaços em branco: a indentação inicial é preservada (e repetida nas continuações) para que YAML/JSON estruturado continue legível dentro do box. A quebra é por runa (ANSI/wide-rune aware via lipgloss.Width), evitando cortar sequências multibyte no meio.

func WrapStructured

func WrapStructured(text string, limit int) []string

WrapStructured quebra um corpo já renderizado pelo glamour para exibição dentro de um box. Diferente de WrapText (word-wrap de prosa, que colapsa a indentação via strings.Fields), ele PRESERVA a indentação inicial de cada linha. Mecânica, por linha:

  • Detecta o indent inicial visível (ANSI-aware: o glamour emite cores ANTES dos espaços, então um TrimLeft simples não enxerga o indent).
  • Deslova (dedent) toda linha pela margem mínima comum — o glamour aplica uma margem de documento uniforme (tipicamente 2 cols) a prosa E código; como o box já tem seu próprio padding, carregar a margem do glamour também empurraria tudo para a direita.
  • Linhas que cabem na largura interna são emitidas verbatim (alinhamento de colunas intacto). Só as que estouram passam por word-wrap, repetindo o indent da linha em cada continuação.

func WrapText

func WrapText(text string, limit int) []string

WrapText quebra o texto em linhas que não excedem o limite. - Preserva quebras de linha originais - Faz word-wrap por largura visível (ignora ANSI) - Não destrói formatação do markdown renderizado (ANSI + linhas)

Types

type Glyph

type Glyph int

Glyph identifies one entry of the canonical status vocabulary.

const (
	// GlyphSuccess marks a completed action.
	GlyphSuccess Glyph = iota
	// GlyphError marks a failed action.
	GlyphError
	// GlyphWarn marks a warning.
	GlyphWarn
	// GlyphInfo marks an informational line.
	GlyphInfo
	// GlyphBullet marks a list item.
	GlyphBullet
	// GlyphArrow marks user input echoes and drill-downs.
	GlyphArrow
	// GlyphRunning marks an in-progress action.
	GlyphRunning
	// GlyphAssistant marks assistant text in compact timelines.
	GlyphAssistant
	// GlyphEllipsis marks truncation.
	GlyphEllipsis
)

func (Glyph) Role

func (g Glyph) Role() theme.Role

Role returns the semantic color role the glyph carries.

func (Glyph) String

func (g Glyph) String() string

String returns the glyph's textual form under the active profile: Unicode on any capable terminal, the ASCII fallback on dumb terminals and pipes (mirroring how color spans vanish there).

type KVRow

type KVRow struct {
	Key       string
	Value     string
	ValueRole theme.Role
	Note      string
}

KVRow is one row of a KVTable. Key and Value arrive already translated. ValueRole tints the value; the zero role renders it as plain text (a border-colored value is never what a table wants, so the zero value is repurposed as "unstyled"). Note is an optional dim qualifier appended after the value — e.g. a "(default)" hint.

type Level

type Level int

Level classifies a Notice.

const (
	// LevelSuccess reports a completed action.
	LevelSuccess Level = iota
	// LevelError reports a failure.
	LevelError
	// LevelWarn reports a warning.
	LevelWarn
	// LevelInfo reports neutral information.
	LevelInfo
)

Jump to

Keyboard shortcuts

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