agentsclimarketplace

Tui components

Skill dayvidpham/aura-plugins/.claude/skills/tui-components

Use when building or restyling strike's TUI — views, panels, modals, pickers, badges, dashboards, status rows, or any layout work in internal/tui. Covers the internal/tui/ui component catalog and internal/tui/theme tokens. Do not use for backend/engine work (internal/engine, internal/provider, internal/tool, internal/permission, internal/session, internal/auth, internal/host) — those packages have no UI surface.From its SKILL.md

Install
npx -y skills add dayvidpham/aura-plugins --skill tui-components

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

12.7 KB, as published. Nobody here has run it

TUI components (strike-cli)

internal/tui/theme is the resolved token source and internal/tui/ui is the component library. Root views compose completed theme styles and ui components, then perform only structural operations (joining, allocation, selection, and layout). Keep this catalog synchronized with internal/tui/ui.

Boundaries

  • Edit internal/tui/_src/<group>/ only for package tui. Flattened internal/tui/*.go are gitignored and regenerated by go generate ./internal/tui / make / CI — edits there are silently lost. Real packages theme, ui, term, common are edited in place.
  • internal/tui/... may import only internal/protocol, internal/host, internal/tui/..., stdlib, and third-party packages. The boundary test enforces this. Charm modules: v1 github.com/charmbracelet/… or v2 charm.land/… — never github.com/charmbracelet/…/v2.
  • internal/tui/ui may import only stdlib, lipgloss, bubbles, charmbracelet/x/ansi, and internal/tui/theme; components do not import the app model or handle tea.Msg.
  • Resolve a supplied theme before reading tokens: th = th.Resolve(). Colors, glyphs, borders, spacing, and emphasis come only from that resolved theme. Never put literal colors, visual glyphs, border/spacing values, or visual modifiers in a root view. A ui visual-modifier argument must trace to a resolved theme value; manually review unknown or interprocedural origins.
  • Components are pure renderers: exported rendering functions take a theme.Theme and options and return a string. State and key handling remain in internal/tui model files.

Component catalog

Tone is ToneDefault, ToneAccent, ToneAccentAlt, ToneSuccess, ToneWarning, ToneError, ToneDanger, or ToneMuted. Level is LevelInfo, LevelSuccess, LevelWarning, or LevelError.

ComponentExact signatureUse
PanelPanel(th theme.Theme, opts PanelOpts, body string) stringWidth-safe framed tile. PanelOpts has Title, Footer, mandatory Width, optional Height, Focused, Dim, Tone, and Borderless. Default chrome is soft (surface-filled body + rounded ╭╮╰╯ outline). Focused soft panes: body stays Surface, title edge uses SurfaceFocus, outline uses BorderFocus — no FocusBar. Solid chrome keeps title-edge SurfaceFocus + thin FocusBar. Never a full-panel wash. TextSelection is a separate role. Tone dialogs keep elevated SurfaceFocus body. chrome: bordered uses outline only. Soft degrades below width 6. Borderless omits chrome.
Panel geometryInnerWidth(width int) int; PanelInnerWidth(th theme.Theme, width int) int; PanelInnerHeight(width, height int) int; PanelInnerHeightFor(th, width, height int) int; PanelContentOrigin(th theme.Theme, width int) (x, y int)Body dimensions and content-cell origin under chrome. Themed callers must use PanelInnerWidth; InnerWidth is default-theme compatibility only. PanelInnerHeight uses default soft chrome; use PanelInnerHeightFor for non-default themes.
DialogDialog(th theme.Theme, opts DialogOpts, body string) stringFocused Panel with a muted final hint. DialogOpts: Title, Hint, Width, Height, Tone. Body lines longer than inner width are word-wrapped (idempotent if already wrapped).
BadgeBadge(th theme.Theme, tone Tone, text string) stringSoft pill: tone label on SurfaceMuted with XS pad. Stock delimiters empty; themes may set Icons.BadgeLeft/BadgeRight.
KeyHintsKeyHints(th theme.Theme, width int, hints []KeyHint) stringWidth-safe footer hints. KeyHint has Key, Label.
StatusBarStatusBar(th theme.Theme, width int, left, right string) stringOne exactly-width row with left/right content.
ListList(th theme.Theme, opts ListOpts) stringUnframed picker body. ListOpts: Items []ListItem, Cursor, Width, Visible, Wrap (word-wrap labels/details instead of ellipsis; use for question options), ShowFilter, Filter, Total, Empty; ListItem: Label, Detail, Current, Disabled.
TreeTree(th theme.Theme, opts TreeOpts) stringUnframed expand/collapse tree body. TreeOpts: Nodes []TreeNode, Cursor, Width, Visible, Empty; TreeNode: ID, Label, Detail, Children, Expanded, Lazy, Leaf, Disabled, Current, Tone. Helpers: FlattenTree, TreeNodeAt, TreeToggleExpanded. Indent uses Spacing.SM; expand glyphs are Icons.TreeExpanded / TreeCollapsed.
NoticeNotice(th theme.Theme, level Level, text string, width int) stringOne-line, level-colored feedback.
BentoBento(th theme.Theme, width int, cards []Card) stringCard packer. Card has Title, Footer, Body, Width, optional Height, Tone, Focused, Dim; body wraps to PanelInnerWidth(th, Width). Bento derives its inter-card gap from resolved th.Spacing.SM.
OverlayOverlayCenter(th theme.Theme, bg, fg string, width, height int) string; Scrim(th theme.Theme, s string) string; ModalWidth(screenWidth int) intANSI-aware centered overlay: scrims bg via OverlayScrim, pads every bg row to exact width (no bright spill strip), keeps fg sharp. Standalone Scrim; dialog width min(72, screenWidth-4).
CanvasCanvas(th theme.Theme, width, height int, body string) stringFinal full-screen fit operation and owner of the application background.
LogoLogo(th theme.Theme) string; LogoCompact(th theme.Theme) stringFull and compact Strike wordmarks.
DiffPreviewDiffPreview(th theme.Theme, opts DiffPreviewOpts) stringUnified +/-/context diff block. DiffPreviewOpts: Path, Old, New, MaxLines (hunk body; ≤0 → 12), mandatory Width (≤0 → ""), ShowStats, optional MoreHint (appended on overflow, e.g. expand key). Header (path and/or +N/−M) does not consume MaxLines; overflow ends with a muted ellipsis more-lines row. Helpers: DiffBodyLen, DiffExceeds.
MeterMeter(th theme.Theme, width int, ratio float64) stringFixed-width ratio bar. Negative ratio = unknown hollow (MeterEmpty).
SparklineSparkline(th theme.Theme, width int, samples []float64) stringFixed-width activity chart from non-negative samples. Empty/unknown series draws hollow MeterEmpty row; glyphs from Icons.Sparkline.
FormatTokensFormatTokens(n int) stringCompact token count for chrome (1.5k, 2M).

Dialog word-wraps body lines to PanelInnerWidth; Panel still truncates any over-long line as a safety net. Prefer pre-wrapping free text for clarity. List truncates by default; set Wrap: true for multi-line option bodies (question modal). Card wraps its own body. Size panel-backed child windows with ui.PanelInnerHeight / PanelInnerHeightFor rather than unconditionally subtracting border rows, so unbordered narrow panels retain their full height.

Theme tokens

Theme.Resolve fills every unset role from theme.Default(). It preserves only theme.NoBackground() as the explicit transparent-background choice; otherwise Background resolves to a solid lipgloss.TerminalColor.

TokenRole
Text, TextMutedprimary and secondary foreground
OverlayScrimde-emphasized modal background (scrim) fill
Accent, AccentAlt, Highlightprimary, secondary, and selected emphasis
Success, Warning, Error, Dangersemantic state colors
Backgroundapplication background (lipgloss.TerminalColor)
Surface, SurfaceFocus, SurfaceMutedpanel fills: body default / title-edge focus (and tone dialogs) / dim
Border, BorderFocus, BorderMutedsoft/bordered frame colors
UserLabel, ToolLabel, DiffAdded, DiffRemovedtranscript and diff roles
ChromeChromeSoft (default), ChromeSolid, or ChromeBordered
BorderStylesoft/bordered panel border weight and six glyphs
SpacingNone, XS, SM, MD, LG layout gaps; left|right pane gutter uses XS (keeps 93-col split); bento/welcome card gaps use SM; Label is the gap between a numbered permission-choice shortcut (for example, 1)) and its label, defaulting to 1 when resolved
Iconsglyph set below
AgentStateruntime status coloring via Theme.AgentStateColor / AgentStateStyle (not a palette field)

Icons fields are Prompt, Assistant, Tool, OK, Err, Info, Agent, Bolt, Dot, Cursor, InputCursor, FilterCursor, ToolGuide, FocusBar, BadgeLeft, BadgeRight, DetailSeparator, Ellipsis, LogoTopRule, LogoBottomRule, MeterFill, MeterEmpty, TreeExpanded, TreeCollapsed, and Sparkline (low→high bar runes). Stock badge delimiters are empty (soft pills). Use th.Icons, never the literal glyph.

theme.AgentState is the live session/agent status vocabulary for dynamic coloring: Ready → Success, Working → AccentAlt, Attention → Warning, Error → Error, and reserved Dead → TextMuted (unmapped by reducers until dead-session lifecycle exists). Use th.AgentStateColor/AgentStateStyle/AgentStateStrongStyle; never hardcode state colors in views. Spinner uses the working token (AccentAlt).

th.S() returns semantic styles: Text, Muted, Accent, AccentAlt, Title, Success, Warning, Error, Danger; their *Strong variants; Selected, SelectedUnderline; UserLabel, AssistantLabel, ToolLabel; Input, InputPrompt, InputPlaceholder, InputCursor, Spinner; Border, BorderFocus, BorderMuted; and DiffAdded, DiffRemoved. Call it once per render and reuse it.

BorderStyle selects BorderWeightUnset, BorderWeightLight, or BorderWeightHeavy; resolving selects the matching preset and fills invalid or missing one-cell glyphs. NewSpacing(xs, sm, md, lg) Spacing initializes XS, SM, MD, and LG, including explicit zero values. It does not initialize Label, so Theme.Resolve supplies the default Label value of 1. WithXS(v int) Spacing, WithSM(v int) Spacing, WithMD(v int) Spacing, WithLG(v int) Spacing, and WithLabel(v int) Spacing set their respective token and preserve an explicit zero. Literal nonzero fields, including Label, are inferred as set; use the constructor or corresponding With method when an explicit zero must be preserved.

Configure Bubble widgets from the resolved foreground/cursor/spinner styles and glyphs, without setting widget backgrounds. ui.Canvas owns background painting after root views and overlays compose, filling every cell unless the theme explicitly uses theme.NoBackground().

Recipes

body := ui.List(th, ui.ListOpts{
    Items: items, Cursor: m.cursor,
    Width: max(1, ui.PanelInnerWidth(th, width)),
    Visible: providerModalVisible, Empty: "no providers configured",
})
return ui.Dialog(th, ui.DialogOpts{Title: "Select provider", Width: width}, body)

The empty-transcript dashboard is app composition, not a component API. Its header owns the compact brand; the dashboard directly allocates fixed-height Panel cards (soft chrome by default), with one or two columns according to available width. It has no outer welcome panel or logo card. The keys card is always present; get started is present only when no provider is selected or the selected provider is unauthenticated; agents & skills requires at least one valid configured agent or skill; and recent prompts requires history. Preserve those conditions and the short-view fallback when changing the dashboard, without exposing app-private helpers as component recipes.

Extending and verification

Add a component under internal/tui/ui, make it width-safe and zero-value tolerant, document it, add its production-appropriate coverage, and update this catalog in the same change. New colors, glyphs, borders, spacing, and emphasis belong in theme.

go test ./internal/tui/... -count=1
make test

For visual review, the opt-in gallery renders parameterized scenarios rather than every view at both 80×24 and 120×40. Its current matrix is: 80×24 left dashboard and right context; 93×40 canonical split; 92×60 left-only; 120×40 split; 120×80 cycle, permission modal, provider picker, command palette, and busy transcript; and 160×45 long-data/status and danger-modal cases:

STRIKE_GALLERY=1 go test ./internal/tui/ -run Gallery -count=1 -v

Review the constrained 80×24 dashboard, split and modal scenarios, and the long-data cases appropriate to the change.

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.