[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CHS1WN41CB704Y665EQKSB":3,"skill-same:01M6CHS1WN41CB704Y665EQKSB":103},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":46,"linkOnly":47,"markdownContent":48,"markdownBody":49,"frontMatter":50,"refName":51,"commitSha":52,"skillDirectory":53,"forksCount":54,"repositoryHtmlUrl":55,"repositoryHomepageUrl":6,"ownerHtmlUrl":56,"ownerBlogUrl":57,"ownerDescription":6,"updatedAt":58,"install":59,"resources":72},"01M6CHS1WN41CB704Y665EQKSB","tui-components",null,"Use when building or restyling strike's TUI — views, panels, modals, pickers, badges, dashboards, status rows, or any layout work in internal\u002Ftui. Covers the internal\u002Ftui\u002Fui component catalog and internal\u002Ftui\u002Ftheme tokens. Do not use for backend\u002Fengine work (internal\u002Fengine, internal\u002Fprovider, internal\u002Ftool, internal\u002Fpermission, internal\u002Fsession, internal\u002Fauth, internal\u002Fhost) — those packages have no UI surface.","dayvidpham\u002Faura-plugins","dayvidpham","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F22456875?v=4","01M6CHR0F6J2M4QEHFR85XRNSR","David Huu Pham","01M6CHR0KQJ4DK5KSZ9TRTP7DW",2,"MIT","https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Faura-plugins\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Ftui-components\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45],"tui components","tui","components","use","when","for","s tui","theme","panels","host","tokens","do","ui component","provider","strike","backend","catalog","session","work","engine","tool","dashboards","ui","packages","status","auth","component","2026-11-10 10:12:30",false,"---\nname: tui-components\ndescription: Use when building or restyling strike's TUI — views, panels, modals, pickers, badges, dashboards, status rows, or any layout work in internal\u002Ftui. Covers the internal\u002Ftui\u002Fui component catalog and internal\u002Ftui\u002Ftheme tokens. Do not use for backend\u002Fengine work (internal\u002Fengine, internal\u002Fprovider, internal\u002Ftool, internal\u002Fpermission, internal\u002Fsession, internal\u002Fauth, internal\u002Fhost) — those packages have no UI surface.\n---\n\n# TUI components (strike-cli)\n\n`internal\u002Ftui\u002Ftheme` is the resolved token source and `internal\u002Ftui\u002Fui` is the\ncomponent library. Root views compose completed theme styles and `ui`\ncomponents, then perform only structural operations (joining, allocation,\nselection, and layout). Keep this catalog synchronized with `internal\u002Ftui\u002Fui`.\n\n## Boundaries\n\n- **Edit `internal\u002Ftui\u002F_src\u002F\u003Cgroup>\u002F` only** for package `tui`. Flattened\n  `internal\u002Ftui\u002F*.go` are gitignored and regenerated by\n  `go generate .\u002Finternal\u002Ftui` \u002F make \u002F CI — edits there are silently lost.\n  Real packages `theme`, `ui`, `term`, `common` are edited in place.\n- `internal\u002Ftui\u002F...` may import only `internal\u002Fprotocol`, `internal\u002Fhost`,\n  `internal\u002Ftui\u002F...`, stdlib, and third-party packages. The boundary test\n  enforces this. Charm modules: v1 `github.com\u002Fcharmbracelet\u002F…` or v2\n  `charm.land\u002F…` — never `github.com\u002Fcharmbracelet\u002F…\u002Fv2`.\n- `internal\u002Ftui\u002Fui` may import only stdlib, lipgloss, bubbles,\n  `charmbracelet\u002Fx\u002Fansi`, and `internal\u002Ftui\u002Ftheme`; components do not import\n  the app model or handle `tea.Msg`.\n- Resolve a supplied theme before reading tokens: `th = th.Resolve()`. Colors,\n  glyphs, borders, spacing, and emphasis come only from that resolved theme.\n  Never put literal colors, visual glyphs, border\u002Fspacing values, or visual\n  modifiers in a root view. A `ui` visual-modifier argument must trace to a\n  resolved theme value; manually review unknown or interprocedural origins.\n- Components are pure renderers: exported rendering functions take a\n  `theme.Theme` and options and return a string. State and key handling remain\n  in `internal\u002Ftui` model files.\n\n## Component catalog\n\n`Tone` is `ToneDefault`, `ToneAccent`, `ToneAccentAlt`, `ToneSuccess`,\n`ToneWarning`, `ToneError`, `ToneDanger`, or `ToneMuted`. `Level` is\n`LevelInfo`, `LevelSuccess`, `LevelWarning`, or `LevelError`.\n\n| Component | Exact signature | Use |\n|---|---|---|\n| Panel | `Panel(th theme.Theme, opts PanelOpts, body string) string` | Width-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. |\n| Panel geometry | `InnerWidth(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. |\n| Dialog | `Dialog(th theme.Theme, opts DialogOpts, body string) string` | Focused 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). |\n| Badge | `Badge(th theme.Theme, tone Tone, text string) string` | Soft pill: tone label on `SurfaceMuted` with XS pad. Stock delimiters empty; themes may set `Icons.BadgeLeft`\u002F`BadgeRight`. |\n| KeyHints | `KeyHints(th theme.Theme, width int, hints []KeyHint) string` | Width-safe footer hints. `KeyHint` has `Key`, `Label`. |\n| StatusBar | `StatusBar(th theme.Theme, width int, left, right string) string` | One exactly-width row with left\u002Fright content. |\n| List | `List(th theme.Theme, opts ListOpts) string` | Unframed picker body. `ListOpts`: `Items []ListItem`, `Cursor`, `Width`, `Visible`, `Wrap` (word-wrap labels\u002Fdetails instead of ellipsis; use for question options), `ShowFilter`, `Filter`, `Total`, `Empty`; `ListItem`: `Label`, `Detail`, `Current`, `Disabled`. |\n| Tree | `Tree(th theme.Theme, opts TreeOpts) string` | Unframed expand\u002Fcollapse 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` \u002F `TreeCollapsed`. |\n| Notice | `Notice(th theme.Theme, level Level, text string, width int) string` | One-line, level-colored feedback. |\n| Bento | `Bento(th theme.Theme, width int, cards []Card) string` | Card 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`. |\n| Overlay | `OverlayCenter(th theme.Theme, bg, fg string, width, height int) string`; `Scrim(th theme.Theme, s string) string`; `ModalWidth(screenWidth int) int` | ANSI-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)`. |\n| Canvas | `Canvas(th theme.Theme, width, height int, body string) string` | Final full-screen fit operation and owner of the application background. |\n| Logo | `Logo(th theme.Theme) string`; `LogoCompact(th theme.Theme) string` | Full and compact Strike wordmarks. |\n| DiffPreview | `DiffPreview(th theme.Theme, opts DiffPreviewOpts) string` | Unified +\u002F-\u002Fcontext diff block with LCS multi-hunk matching, line-number gutter (auto when width ≥ 16; `NoLineNumbers` to disable), SurfaceMuted wash on add\u002Fremove rows, bold word-diff on paired replaces (`NoWordDiff` to disable), and `Offset` scroll window. `DiffPreviewOpts`: `Path`, `Old`, `New`, `MaxLines` (hunk body; ≤0 → 12), mandatory `Width` (≤0 → \"\"), `ShowStats`, `MoreHint`, `Offset`, `NoLineNumbers`, `NoWordDiff`. Header (path and\u002For +N\u002F−M) does not consume `MaxLines`; overflow ends with a muted ellipsis more-lines row. Helpers: `DiffBodyLen`, `DiffExceeds`, `DiffMaxOffset`. |\n| Meter | `Meter(th theme.Theme, width int, ratio float64) string` | Fixed-width ratio bar. Negative ratio = unknown hollow (MeterEmpty). |\n| Sparkline | `Sparkline(th theme.Theme, width int, samples []float64) string` | Fixed-width activity chart from non-negative samples. Empty\u002Funknown series draws hollow MeterEmpty row; glyphs from `Icons.Sparkline`. |\n| FormatTokens | `FormatTokens(n int) string` | Compact token count for chrome (`1.5k`, `2M`). |\n\nDialog word-wraps body lines to `PanelInnerWidth`; Panel still truncates any\nover-long line as a safety net. Prefer pre-wrapping free text for clarity.\n`List` truncates by default; set `Wrap: true` for multi-line option bodies\n(question modal). `Card` wraps its own body. Size panel-backed child windows\nwith `ui.PanelInnerHeight` \u002F `PanelInnerHeightFor` rather than unconditionally\nsubtracting border rows, so unbordered narrow panels retain their full height.\n\n## Theme tokens\n\n`Theme.Resolve` fills every unset role from `theme.Default()`. It preserves\nonly `theme.NoBackground()` as the explicit transparent-background choice;\notherwise `Background` resolves to a solid `lipgloss.TerminalColor`.\n\n| Token | Role |\n|---|---|\n| `Text`, `TextMuted` | primary and secondary foreground |\n| `OverlayScrim` | de-emphasized modal background (scrim) fill |\n| `Accent`, `AccentAlt`, `Highlight` | primary, secondary, and selected emphasis |\n| `Success`, `Warning`, `Error`, `Danger` | semantic state colors |\n| `Background` | application background (`lipgloss.TerminalColor`) |\n| `Surface`, `SurfaceFocus`, `SurfaceMuted` | panel fills: body default \u002F title-edge focus (and tone dialogs) \u002F dim |\n| `Border`, `BorderFocus`, `BorderMuted` | soft\u002Fbordered frame colors |\n| `UserLabel`, `ToolLabel`, `DiffAdded`, `DiffRemoved` | transcript and diff roles |\n| `Chrome` | `ChromeSoft` (default), `ChromeSolid`, or `ChromeBordered` |\n| `BorderStyle` | soft\u002Fbordered panel border weight and six glyphs |\n| `Spacing` | `None`, `XS`, `SM`, `MD`, `LG` layout gaps; left\\|right pane gutter uses `XS` (keeps 93-col split); bento\u002Fwelcome 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 |\n| `Icons` | glyph set below |\n| `AgentState` | runtime status coloring via `Theme.AgentStateColor` \u002F `AgentStateStyle` (not a palette field) |\n\n`Icons` fields are `Prompt`, `Assistant`, `Tool`, `OK`, `Err`, `Info`,\n`Agent`, `Bolt`, `Dot`, `Cursor`, `InputCursor`, `FilterCursor`, `ToolGuide`,\n`FocusBar`,\n`BadgeLeft`, `BadgeRight`, `DetailSeparator`, `Ellipsis`, `LogoTopRule`,\n`LogoBottomRule`, `MeterFill`, `MeterEmpty`, `TreeExpanded`,\n`TreeCollapsed`, `CheckboxOn` (`[x]`), `CheckboxOff` (`[ ]`), and `Sparkline`\n(low→high bar runes). Stock badge delimiters are empty (soft pills). Use\n`th.Icons`, never the literal glyph.\n\n`theme.AgentState` is the live session\u002Fagent status vocabulary for dynamic\ncoloring: `Ready` → `Success`, `Working` → `AccentAlt`, `Attention` →\n`Warning`, `Error` → `Error`, and reserved `Dead` → `TextMuted` (unmapped by\nreducers until dead-session lifecycle exists). Use\n`th.AgentStateColor`\u002F`AgentStateStyle`\u002F`AgentStateStrongStyle`; never hardcode\nstate colors in views. `Spinner` uses the working token (`AccentAlt`).\n\n`th.S()` returns semantic styles: `Text`, `Muted`, `Accent`, `AccentAlt`,\n`Title`, `Success`, `Warning`, `Error`, `Danger`; their `*Strong` variants;\n`Selected`, `SelectedUnderline`; `UserLabel`, `AssistantLabel`, `ToolLabel`;\n`Input`, `InputPrompt`, `InputPlaceholder`, `InputCursor`, `Spinner`;\n`Border`, `BorderFocus`, `BorderMuted`; `DiffAdded`, `DiffRemoved`,\n`DiffAddedStrong`, `DiffRemovedStrong` (bold word-diff spans); and\n`DiffAddedLine`, `DiffRemovedLine` (role FG on `SurfaceMuted` wash). Call\nit once per render and reuse it.\n\n`BorderStyle` selects `BorderWeightUnset`, `BorderWeightLight`, or\n`BorderWeightHeavy`; resolving selects the matching preset and fills invalid\nor missing one-cell glyphs. `NewSpacing(xs, sm, md, lg) Spacing` initializes\n`XS`, `SM`, `MD`, and `LG`, including explicit zero values. It does not\ninitialize `Label`, so `Theme.Resolve` supplies the default `Label` value of\n`1`. `WithXS(v int) Spacing`, `WithSM(v int) Spacing`, `WithMD(v int) Spacing`,\n`WithLG(v int) Spacing`, and `WithLabel(v int) Spacing` set their respective\ntoken and preserve an explicit zero. Literal nonzero fields, including\n`Label`, are inferred as set; use the constructor or corresponding `With`\nmethod when an explicit zero must be preserved.\n\nConfigure Bubble widgets from the resolved foreground\u002Fcursor\u002Fspinner styles\nand glyphs, without setting widget backgrounds. `ui.Canvas` owns background\npainting after root views and overlays compose, filling every cell unless the\ntheme explicitly uses `theme.NoBackground()`.\n\n## Recipes\n\n```go\nbody := ui.List(th, ui.ListOpts{\n    Items: items, Cursor: m.cursor,\n    Width: max(1, ui.PanelInnerWidth(th, width)),\n    Visible: providerModalVisible, Empty: \"no providers configured\",\n})\nreturn ui.Dialog(th, ui.DialogOpts{Title: \"Select provider\", Width: width}, body)\n```\n\nThe empty-transcript dashboard is app composition, not a component API. Its\nheader owns the compact brand; the dashboard directly allocates fixed-height\n`Panel` cards (soft chrome by default), with one or two columns according to\navailable width. It has no outer welcome panel or logo card. The `keys` card is\nalways present; `get started` is present only when no provider is selected or\nthe selected provider is unauthenticated; `agents & skills` requires at least\none valid configured agent or skill; and `recent prompts` requires history.\nPreserve those conditions and the short-view fallback when changing the\ndashboard, without exposing app-private helpers as component recipes.\n\n## Extending and verification\n\nAdd a component under `internal\u002Ftui\u002Fui`, make it width-safe and zero-value\ntolerant, document it, add its production-appropriate coverage, and update\nthis catalog in the same change. New colors, glyphs, borders, spacing, and\nemphasis belong in `theme`.\n\n```sh\ngo test .\u002Finternal\u002Ftui\u002F... -count=1\nmake test\n```\n\nFor visual review, the opt-in gallery renders parameterized scenarios rather\nthan every view at both 80×24 and 120×40. Its current matrix is: 80×24 left\ndashboard and right context; 93×40 canonical split; 92×60 left-only; 120×40\nsplit; 120×80 cycle, permission modal, provider picker, command palette, and\nbusy transcript; and 160×45 long-data\u002Fstatus and danger-modal cases:\n\n```sh\nSTRIKE_GALLERY=1 go test .\u002Finternal\u002Ftui\u002F -run Gallery -count=1 -v\n```\n\nReview the constrained 80×24 dashboard, split and modal scenarios, and the\nlong-data cases appropriate to the change.\n","# TUI components (strike-cli)\n\n`internal\u002Ftui\u002Ftheme` is the resolved token source and `internal\u002Ftui\u002Fui` is the\ncomponent library. Root views compose completed theme styles and `ui`\ncomponents, then perform only structural operations (joining, allocation,\nselection, and layout). Keep this catalog synchronized with `internal\u002Ftui\u002Fui`.\n\n## Boundaries\n\n- **Edit `internal\u002Ftui\u002F_src\u002F\u003Cgroup>\u002F` only** for package `tui`. Flattened\n  `internal\u002Ftui\u002F*.go` are gitignored and regenerated by\n  `go generate .\u002Finternal\u002Ftui` \u002F make \u002F CI — edits there are silently lost.\n  Real packages `theme`, `ui`, `term`, `common` are edited in place.\n- `internal\u002Ftui\u002F...` may import only `internal\u002Fprotocol`, `internal\u002Fhost`,\n  `internal\u002Ftui\u002F...`, stdlib, and third-party packages. The boundary test\n  enforces this. Charm modules: v1 `github.com\u002Fcharmbracelet\u002F…` or v2\n  `charm.land\u002F…` — never `github.com\u002Fcharmbracelet\u002F…\u002Fv2`.\n- `internal\u002Ftui\u002Fui` may import only stdlib, lipgloss, bubbles,\n  `charmbracelet\u002Fx\u002Fansi`, and `internal\u002Ftui\u002Ftheme`; components do not import\n  the app model or handle `tea.Msg`.\n- Resolve a supplied theme before reading tokens: `th = th.Resolve()`. Colors,\n  glyphs, borders, spacing, and emphasis come only from that resolved theme.\n  Never put literal colors, visual glyphs, border\u002Fspacing values, or visual\n  modifiers in a root view. A `ui` visual-modifier argument must trace to a\n  resolved theme value; manually review unknown or interprocedural origins.\n- Components are pure renderers: exported rendering functions take a\n  `theme.Theme` and options and return a string. State and key handling remain\n  in `internal\u002Ftui` model files.\n\n## Component catalog\n\n`Tone` is `ToneDefault`, `ToneAccent`, `ToneAccentAlt`, `ToneSuccess`,\n`ToneWarning`, `ToneError`, `ToneDanger`, or `ToneMuted`. `Level` is\n`LevelInfo`, `LevelSuccess`, `LevelWarning`, or `LevelError`.\n\n| Component | Exact signature | Use |\n|---|---|---|\n| Panel | `Panel(th theme.Theme, opts PanelOpts, body string) string` | Width-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. |\n| Panel geometry | `InnerWidth(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. |\n| Dialog | `Dialog(th theme.Theme, opts DialogOpts, body string) string` | Focused 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). |\n| Badge | `Badge(th theme.Theme, tone Tone, text string) string` | Soft pill: tone label on `SurfaceMuted` with XS pad. Stock delimiters empty; themes may set `Icons.BadgeLeft`\u002F`BadgeRight`. |\n| KeyHints | `KeyHints(th theme.Theme, width int, hints []KeyHint) string` | Width-safe footer hints. `KeyHint` has `Key`, `Label`. |\n| StatusBar | `StatusBar(th theme.Theme, width int, left, right string) string` | One exactly-width row with left\u002Fright content. |\n| List | `List(th theme.Theme, opts ListOpts) string` | Unframed picker body. `ListOpts`: `Items []ListItem`, `Cursor`, `Width`, `Visible`, `Wrap` (word-wrap labels\u002Fdetails instead of ellipsis; use for question options), `ShowFilter`, `Filter`, `Total`, `Empty`; `ListItem`: `Label`, `Detail`, `Current`, `Disabled`. |\n| Tree | `Tree(th theme.Theme, opts TreeOpts) string` | Unframed expand\u002Fcollapse 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` \u002F `TreeCollapsed`. |\n| Notice | `Notice(th theme.Theme, level Level, text string, width int) string` | One-line, level-colored feedback. |\n| Bento | `Bento(th theme.Theme, width int, cards []Card) string` | Card 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`. |\n| Overlay | `OverlayCenter(th theme.Theme, bg, fg string, width, height int) string`; `Scrim(th theme.Theme, s string) string`; `ModalWidth(screenWidth int) int` | ANSI-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)`. |\n| Canvas | `Canvas(th theme.Theme, width, height int, body string) string` | Final full-screen fit operation and owner of the application background. |\n| Logo | `Logo(th theme.Theme) string`; `LogoCompact(th theme.Theme) string` | Full and compact Strike wordmarks. |\n| DiffPreview | `DiffPreview(th theme.Theme, opts DiffPreviewOpts) string` | Unified +\u002F-\u002Fcontext diff block with LCS multi-hunk matching, line-number gutter (auto when width ≥ 16; `NoLineNumbers` to disable), SurfaceMuted wash on add\u002Fremove rows, bold word-diff on paired replaces (`NoWordDiff` to disable), and `Offset` scroll window. `DiffPreviewOpts`: `Path`, `Old`, `New`, `MaxLines` (hunk body; ≤0 → 12), mandatory `Width` (≤0 → \"\"), `ShowStats`, `MoreHint`, `Offset`, `NoLineNumbers`, `NoWordDiff`. Header (path and\u002For +N\u002F−M) does not consume `MaxLines`; overflow ends with a muted ellipsis more-lines row. Helpers: `DiffBodyLen`, `DiffExceeds`, `DiffMaxOffset`. |\n| Meter | `Meter(th theme.Theme, width int, ratio float64) string` | Fixed-width ratio bar. Negative ratio = unknown hollow (MeterEmpty). |\n| Sparkline | `Sparkline(th theme.Theme, width int, samples []float64) string` | Fixed-width activity chart from non-negative samples. Empty\u002Funknown series draws hollow MeterEmpty row; glyphs from `Icons.Sparkline`. |\n| FormatTokens | `FormatTokens(n int) string` | Compact token count for chrome (`1.5k`, `2M`). |\n\nDialog word-wraps body lines to `PanelInnerWidth`; Panel still truncates any\nover-long line as a safety net. Prefer pre-wrapping free text for clarity.\n`List` truncates by default; set `Wrap: true` for multi-line option bodies\n(question modal). `Card` wraps its own body. Size panel-backed child windows\nwith `ui.PanelInnerHeight` \u002F `PanelInnerHeightFor` rather than unconditionally\nsubtracting border rows, so unbordered narrow panels retain their full height.\n\n## Theme tokens\n\n`Theme.Resolve` fills every unset role from `theme.Default()`. It preserves\nonly `theme.NoBackground()` as the explicit transparent-background choice;\notherwise `Background` resolves to a solid `lipgloss.TerminalColor`.\n\n| Token | Role |\n|---|---|\n| `Text`, `TextMuted` | primary and secondary foreground |\n| `OverlayScrim` | de-emphasized modal background (scrim) fill |\n| `Accent`, `AccentAlt`, `Highlight` | primary, secondary, and selected emphasis |\n| `Success`, `Warning`, `Error`, `Danger` | semantic state colors |\n| `Background` | application background (`lipgloss.TerminalColor`) |\n| `Surface`, `SurfaceFocus`, `SurfaceMuted` | panel fills: body default \u002F title-edge focus (and tone dialogs) \u002F dim |\n| `Border`, `BorderFocus`, `BorderMuted` | soft\u002Fbordered frame colors |\n| `UserLabel`, `ToolLabel`, `DiffAdded`, `DiffRemoved` | transcript and diff roles |\n| `Chrome` | `ChromeSoft` (default), `ChromeSolid`, or `ChromeBordered` |\n| `BorderStyle` | soft\u002Fbordered panel border weight and six glyphs |\n| `Spacing` | `None`, `XS`, `SM`, `MD`, `LG` layout gaps; left\\|right pane gutter uses `XS` (keeps 93-col split); bento\u002Fwelcome 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 |\n| `Icons` | glyph set below |\n| `AgentState` | runtime status coloring via `Theme.AgentStateColor` \u002F `AgentStateStyle` (not a palette field) |\n\n`Icons` fields are `Prompt`, `Assistant`, `Tool`, `OK`, `Err`, `Info`,\n`Agent`, `Bolt`, `Dot`, `Cursor`, `InputCursor`, `FilterCursor`, `ToolGuide`,\n`FocusBar`,\n`BadgeLeft`, `BadgeRight`, `DetailSeparator`, `Ellipsis`, `LogoTopRule`,\n`LogoBottomRule`, `MeterFill`, `MeterEmpty`, `TreeExpanded`,\n`TreeCollapsed`, `CheckboxOn` (`[x]`), `CheckboxOff` (`[ ]`), and `Sparkline`\n(low→high bar runes). Stock badge delimiters are empty (soft pills). Use\n`th.Icons`, never the literal glyph.\n\n`theme.AgentState` is the live session\u002Fagent status vocabulary for dynamic\ncoloring: `Ready` → `Success`, `Working` → `AccentAlt`, `Attention` →\n`Warning`, `Error` → `Error`, and reserved `Dead` → `TextMuted` (unmapped by\nreducers until dead-session lifecycle exists). Use\n`th.AgentStateColor`\u002F`AgentStateStyle`\u002F`AgentStateStrongStyle`; never hardcode\nstate colors in views. `Spinner` uses the working token (`AccentAlt`).\n\n`th.S()` returns semantic styles: `Text`, `Muted`, `Accent`, `AccentAlt`,\n`Title`, `Success`, `Warning`, `Error`, `Danger`; their `*Strong` variants;\n`Selected`, `SelectedUnderline`; `UserLabel`, `AssistantLabel`, `ToolLabel`;\n`Input`, `InputPrompt`, `InputPlaceholder`, `InputCursor`, `Spinner`;\n`Border`, `BorderFocus`, `BorderMuted`; `DiffAdded`, `DiffRemoved`,\n`DiffAddedStrong`, `DiffRemovedStrong` (bold word-diff spans); and\n`DiffAddedLine`, `DiffRemovedLine` (role FG on `SurfaceMuted` wash). Call\nit once per render and reuse it.\n\n`BorderStyle` selects `BorderWeightUnset`, `BorderWeightLight`, or\n`BorderWeightHeavy`; resolving selects the matching preset and fills invalid\nor missing one-cell glyphs. `NewSpacing(xs, sm, md, lg) Spacing` initializes\n`XS`, `SM`, `MD`, and `LG`, including explicit zero values. It does not\ninitialize `Label`, so `Theme.Resolve` supplies the default `Label` value of\n`1`. `WithXS(v int) Spacing`, `WithSM(v int) Spacing`, `WithMD(v int) Spacing`,\n`WithLG(v int) Spacing`, and `WithLabel(v int) Spacing` set their respective\ntoken and preserve an explicit zero. Literal nonzero fields, including\n`Label`, are inferred as set; use the constructor or corresponding `With`\nmethod when an explicit zero must be preserved.\n\nConfigure Bubble widgets from the resolved foreground\u002Fcursor\u002Fspinner styles\nand glyphs, without setting widget backgrounds. `ui.Canvas` owns background\npainting after root views and overlays compose, filling every cell unless the\ntheme explicitly uses `theme.NoBackground()`.\n\n## Recipes\n\n```go\nbody := ui.List(th, ui.ListOpts{\n    Items: items, Cursor: m.cursor,\n    Width: max(1, ui.PanelInnerWidth(th, width)),\n    Visible: providerModalVisible, Empty: \"no providers configured\",\n})\nreturn ui.Dialog(th, ui.DialogOpts{Title: \"Select provider\", Width: width}, body)\n```\n\nThe empty-transcript dashboard is app composition, not a component API. Its\nheader owns the compact brand; the dashboard directly allocates fixed-height\n`Panel` cards (soft chrome by default), with one or two columns according to\navailable width. It has no outer welcome panel or logo card. The `keys` card is\nalways present; `get started` is present only when no provider is selected or\nthe selected provider is unauthenticated; `agents & skills` requires at least\none valid configured agent or skill; and `recent prompts` requires history.\nPreserve those conditions and the short-view fallback when changing the\ndashboard, without exposing app-private helpers as component recipes.\n\n## Extending and verification\n\nAdd a component under `internal\u002Ftui\u002Fui`, make it width-safe and zero-value\ntolerant, document it, add its production-appropriate coverage, and update\nthis catalog in the same change. New colors, glyphs, borders, spacing, and\nemphasis belong in `theme`.\n\n```sh\ngo test .\u002Finternal\u002Ftui\u002F... -count=1\nmake test\n```\n\nFor visual review, the opt-in gallery renders parameterized scenarios rather\nthan every view at both 80×24 and 120×40. Its current matrix is: 80×24 left\ndashboard and right context; 93×40 canonical split; 92×60 left-only; 120×40\nsplit; 120×80 cycle, permission modal, provider picker, command palette, and\nbusy transcript; and 160×45 long-data\u002Fstatus and danger-modal cases:\n\n```sh\nSTRIKE_GALLERY=1 go test .\u002Finternal\u002Ftui\u002F -run Gallery -count=1 -v\n```\n\nReview the constrained 80×24 dashboard, split and modal scenarios, and the\nlong-data cases appropriate to the change.\n",{"name":5,"description":7},"main","7fe8098d35c17a7d6497a81e4c08abb4c2d02735",".claude\u002Fskills\u002Ftui-components",0,"https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Faura-plugins","https:\u002F\u002Fgithub.com\u002Fdayvidpham","dhpham.com","2026-11-11 16:48:56",{"source":8,"skillName":5,"commands":60,"prompt":70,"zipAvailable":71},[61,64,67],{"tool":62,"command":63},"npx","npx skills add dayvidpham\u002Faura-plugins --skill tui-components",{"tool":65,"command":66},"bunx","bunx skills add dayvidpham\u002Faura-plugins --skill tui-components",{"tool":68,"command":69},"pnpm","pnpm dlx skills add dayvidpham\u002Faura-plugins --skill tui-components","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Faura-plugins\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Ftui-components\u002FSKILL.md",true,[73,79,83,87,91,97],{"path":74,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":77,"sourceHtmlUrl":78},"core\u002Feasing.py","SCRIPT","脚本",6265,"https:\u002F\u002Fgithub.com\u002FSESELOVSKYDarian\u002FVase\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fslack-gif-creator\u002Fcore\u002Feasing.py",{"path":80,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":81,"sourceHtmlUrl":82},"core\u002Fframe_composer.py",4548,"https:\u002F\u002Fgithub.com\u002FSESELOVSKYDarian\u002FVase\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fslack-gif-creator\u002Fcore\u002Fframe_composer.py",{"path":84,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":85,"sourceHtmlUrl":86},"core\u002Fgif_builder.py",9847,"https:\u002F\u002Fgithub.com\u002FSESELOVSKYDarian\u002FVase\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fslack-gif-creator\u002Fcore\u002Fgif_builder.py",{"path":88,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":89,"sourceHtmlUrl":90},"core\u002Fvalidators.py",3785,"https:\u002F\u002Fgithub.com\u002FSESELOVSKYDarian\u002FVase\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fslack-gif-creator\u002Fcore\u002Fvalidators.py",{"path":92,"resourceType":93,"resourceTypeDesc":94,"sizeBytes":95,"sourceHtmlUrl":96},"LICENSE.txt","LICENSE","许可证",11357,"https:\u002F\u002Fgithub.com\u002FSESELOVSKYDarian\u002FVase\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fslack-gif-creator\u002FLICENSE.txt",{"path":98,"resourceType":99,"resourceTypeDesc":100,"sizeBytes":101,"sourceHtmlUrl":102},"requirements.txt","REFERENCE","参考文档",66,"https:\u002F\u002Fgithub.com\u002FSESELOVSKYDarian\u002FVase\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fslack-gif-creator\u002Frequirements.txt",[104,130,159,164,170,175],{"skillNo":105,"name":106,"nameZh":6,"description":107,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":108,"categories":109,"keywords":110,"publishedAt":46},"01M70PPVS3CW9RYWGZ4KSNV7MV","afk-implement","Run an unattended (\"AFK\") implementation session as the supervisor — scope small progressive iterations, push feature branches, open pull requests, drive review waves to clean, and never merge. Use when the user says they are going AFK or to bed, asks for overnight autonomous implementation, says \"don't block on me\", \"open PRs but don't merge\", \"progressive adoption\", or \"stack changes\".","https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Faura-plugins\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fafk-implement\u002FSKILL.md",[],[111,112,113,114,115,116,117,118,119,120,23,121,122,123,124,125,126,127,128,129],"implement","afk implement","pull","bed","never","scope","clean","branches","open","autonomous","review","pull requests","stack","me","run","merge","unattended","overnight","but",{"skillNo":131,"name":132,"nameZh":6,"description":133,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":134,"categories":135,"keywords":141,"publishedAt":46},"01M6CHSCKDTSV05385SJ05XWW6","protocol","Aura protocol reference documentation — 12-phase workflow, agent roles, constraints, and coding standards. Read when you need to understand the full workflow or look up conventions.","https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Faura-plugins\u002Fblob\u002Fmain\u002Fskills\u002Fprotocol\u002FSKILL.md",[136],{"categoryNo":137,"categoryName":138,"taxonomyType":139,"taxonomyTypeDesc":140},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[132,142,143,144,145,146,147,148,149,150,151,152,23,153,154,155,156,157,158],"coding","standards","documentation","read","up","coding standards","reference","look","workflow","full","constraints","you","phase","roles","understand","conventions","aura",{"skillNo":160,"name":132,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":161,"categories":162,"keywords":163,"publishedAt":46},"01M6CHSBJDVKGWAJWQCNQ22F9F","https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Faura-plugins\u002Fblob\u002Fmain\u002Fnix\u002Fhm-module-test-data\u002Ffull\u002Fskills\u002Fprotocol\u002FSKILL.md",[],[132],{"skillNo":165,"name":166,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":167,"categories":168,"keywords":169,"publishedAt":46},"01M6CHSAJY85M0W8NEAN4PRF82","example","https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Faura-plugins\u002Fblob\u002Fmain\u002Fnix\u002Fhm-module-test-data\u002Ffull\u002Fskills\u002Fexample\u002FSKILL.md",[],[166],{"skillNo":171,"name":166,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":172,"categories":173,"keywords":174,"publishedAt":46},"01M6CHS9KQJYCWR1C9F2GJDZYX","https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Faura-plugins\u002Fblob\u002Fmain\u002Fnix\u002Fhm-module-test-data\u002Ffull\u002F.opencode\u002Fskill\u002Fexample\u002FSKILL.md",[],[166],{"skillNo":176,"name":166,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":177,"categories":178,"keywords":179,"publishedAt":46},"01M6CHS8MSW2SDQ5Y05A3GZES4","https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Faura-plugins\u002Fblob\u002Fmain\u002Fnix\u002Fhm-module-test-data\u002Ffull\u002F.agents\u002Fskills\u002Fexample\u002FSKILL.md",[],[166]]