본문으로 건너뛰기
cccskills
무료GitHub에 공개

tui-design

Design specification for CLI TUI (Terminal User Interface). This skill provides comprehensive guidelines for implementing interactive terminal UI components, including page layout structure, color schemes, keyboard navigation, and multi-level navigation principles.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(2)

  • SKILL.md19.8 KB
  • LICENSE.txt1.0 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

CLI TUI Design Specification

This document provides comprehensive design guidelines for implementing interactive terminal user interface (TUI) applications.

When to Use This Skill

  • Developing or modifying CLI TUI components
  • Adding new UI pages or interactions
  • Implementing new visual styles or themes
  • Creating similar TUI applications following this pattern

1. Page Layout Structure

Every TUI page follows a consistent four-section layout:

┌────────────────────────────────────────────────────────────────┐
│  Header: Title Line + Separator + ASCII Logo + Separator       │
├────────────────────────────────────────────────────────────────┤
│  Info Bar: Context-dependent information (Dynamic)             │
├────────────────────────────────────────────────────────────────┤
│  Content Area: Interactive options/list (Scrollable)           │
│                                                                │
├────────────────────────────────────────────────────────────────┤
│  Footer: Status Bar + Separator + Keyboard Hints               │
└────────────────────────────────────────────────────────────────┘

Section Details

1.1 Header Section (Always Visible)

The header consists of a title line, separator, ASCII logo, and separator with symmetric spacing:

🚀 Skills-X - AI Agent Skills Manager  v0.2.12
────────────────────────────────────────────────────────────
                                                  <- 1 blank line (from logo constant leading \n)
   ███████╗██╗  ██╗██╗██╗     ...
   ╚══════╝╚═╝  ╚═╝╚═╝╚══════╝...
                                                  <- 1 blank line (from logo constant trailing \n + 1 extra \n)
────────────────────────────────────────────────────────────

Spacing rules:

  • The logo constant uses backtick string, which naturally includes a leading \n (after opening backtick) and trailing \n (before closing backtick)
  • After logoStyle.Render(logo), add exactly one \n — this produces symmetric 1-blank-line padding on both sides
  • Never add \n\n after logo — it creates asymmetric bottom padding
func RenderLogo(version string) string {
    var b strings.Builder
    // Title + version (strip -dirty suffix for display)
    b.WriteString(accentStyle.Render("🚀 Skills-X - AI Agent Skills Manager"))
    if version != "" {
        displayVersion := strings.TrimSuffix(version, "-dirty")
        b.WriteString("  " + hintStyle.Render(displayVersion))
    }
    b.WriteString("\n")
    // Upper separator
    b.WriteString(separatorStyle.Render(strings.Repeat("─", SeparatorWidth)))
    b.WriteString("\n")
    // Logo (constant has leading \n and trailing \n built-in)
    b.WriteString(logoStyle.Render(logo))
    b.WriteString("\n")  // exactly 1 \n — symmetric with leading \n
    // Lower separator
    b.WriteString(separatorStyle.Render(strings.Repeat("─", SeparatorWidth)))
    b.WriteString("\n")
    return b.String()
}

Newline budget between header and content:

  • RenderLogo ends with separator + \n
  • View should not add extra \n after RenderLogo() — content starts immediately

1.2 Info Bar Section (Dynamic)

  • Page-specific context: table headers, counts, search box
  • Context info uses dim/hint color
  • No working directory display on LV1 — only shown on pages where it adds value (e.g. LV3 skills list)

1.3 Content Area (Interactive)

  • Table View: Column headers + data rows with visual-width alignment
  • List View: Scrollable list with checkbox multi-selection
  • Progress View: Progress bar with percentage and counts

Key features:

  • Visual-width-aware column alignment (see Section 3)
  • Numeric columns right-aligned with fmt.Sprintf("%4d")
  • Checkbox (☐/☑) for multi-selection
  • Cursor indicator (❯) for current position
  • Empty line padding to maintain stable page height

1.4 Footer Section

(共 30 个,已安装 12 个,将安装 3 个,将卸载 1 个)

────────────────────────────────────────────────────────────
/ 搜索 | 空格 选择/反选 | A 全选 | Enter 确认安装/卸载 | b 返回 | q 退出

Composition:

  1. Status bar: \n + counts in dim color, parenthesized
  2. Separator: \n + 60-char ─ line + \n
  3. Hints: Keyboard shortcuts in dim color, pipe-separated
func RenderStatusBar(total, installed, newSelected, deselected int) string {
    return "\n" + hintStyle.Render(
        fmt.Sprintf("(共 %d 个,已安装 %d 个,将安装 %d 个,将卸载 %d 个)",
            total, installed, newSelected, deselected))
}

func RenderHint(hint string) string {
    return "\n" + RenderSeparator() + hintStyle.Render(hint)
}

func RenderSeparator() string {
    return separatorStyle.Render(strings.Repeat("─", SeparatorWidth)) + "\n"
}

2. Style Definitions

Color Palette

Color NameHex CodeVariableUsage
Primary#00D4AAprimaryColorSelected items, logo
Secondary#FF6B6BsecondaryColorCursor, errors, alerts
Accent#4ECDC4accentColorTitle line, highlights
Dim#666666dimColorHints, separators
White#FFFFFFwhiteColorSection titles, headers
Yellow#FFCC00yellowColorWarnings (reserved)
Blue#5599FFblueColorLinks (reserved)
Cyan#5A9FB8cyanColorSelectable items

Component Styles

logoStyle       = lipgloss.NewStyle().Foreground(primaryColor).Bold(true)
titleStyle      = lipgloss.NewStyle().Foreground(whiteColor).Bold(true)
selectedStyle   = lipgloss.NewStyle().Foreground(primaryColor).Bold(true)
normalStyle     = lipgloss.NewStyle().Foreground(lipgloss.Color("#AAAAAA"))
selectableStyle = lipgloss.NewStyle().Foreground(cyanColor)
hintStyle       = lipgloss.NewStyle().Foreground(dimColor)
cursorStyle     = lipgloss.NewStyle().Foreground(secondaryColor).Bold(true)
successStyle    = lipgloss.NewStyle().Foreground(lipgloss.Color("#00FF00"))
separatorStyle  = lipgloss.NewStyle().Foreground(dimColor)
searchStyle     = lipgloss.NewStyle().Foreground(whiteColor).Background(lipgloss.Color("#333333"))

Status Indicators

SymbolMeaningStyle
☑SelectedselectedStyle
☐Not selected—
❯Cursor positioncursorStyle

3. Column Alignment & Visual Width

The Problem

fmt.Sprintf width specifiers count bytes, not terminal columns. This breaks alignment when:

  • ANSI escape codes (lipgloss styles) add invisible bytes
  • CJK characters (中文、日本語) occupy 2 terminal columns per rune

The Solution: Pad First, Style After

Rule: Always pad plain text to the target visual width, then apply lipgloss style.

// termWidth returns the visual width of a string in terminal columns.
func termWidth(s string) int {
    w := 0
    for _, r := range s {
        if isCJK(r) {
            w += 2
        } else {
            w++
        }
    }
    return w
}

// padRight pads a string with spaces to reach the target visual width.
func padRight(s string, targetWidth int) string {
    w := termWidth(s)
    for w < targetWidth {
        s += " "
        w++
    }
    return s
}

Usage Pattern

// Header: pad plain text, then style
headerName := padRight("AI 工具", 22)   // "AI 工具" = 7 visual cols → 15 spaces added
b.WriteString(fmt.Sprintf("  %s  %s  %s\n",
    titleStyle.Render(headerName),           // styled AFTER padding
    titleStyle.Render(padRight("全局", 4)),   // "全局" = 4 visual cols → 0 spaces
    titleStyle.Render(padRight("项目", 4))))

// Data rows: same column widths
name := padRight(p.Name, 22)             // "Claude Code" = 11 cols → 11 spaces
globalStr := fmt.Sprintf("%4d", count)   // right-aligned number, 4 chars
b.WriteString(fmt.Sprintf("%s%s  %s  %s\n",
    prefix,                               // "  " or "❯ " (always 2 visual cols)
    style.Render(name),                   // styled AFTER padding
    globalCountStyle.Render(globalStr),   // styled AFTER formatting
    projectCountStyle.Render(projectStr)))

Column Width Standards

ColumnVisual WidthAlignmentPadding Method
Name22LeftpadRight(text, 22)
Skill name40LeftpadRight(text, 40)
Number4Rightfmt.Sprintf("%4d", n)
Status (✓/✗)1CenterDirect render
Prefix (❯)2Left" " or cursorStyle("❯ ")

Gap Between Columns

Use 2 spaces (" ") between columns in fmt.Sprintf:

fmt.Sprintf("%s%s  %s  %s\n", prefix, name, col1, col2)
//                ^^    ^^  two-space gaps

4. Separator Rules

All separators use the same constant width:

const SeparatorWidth = 60
LocationCharacterWidthColor
Above ASCII logo─60dimColor
Below ASCII logo─60dimColor
Below table header─60dimColor
Above footer hints─60dimColor

All separators use SeparatorWidth (60). Never hardcode different widths.


5. Keyboard Navigation

Universal Keys (All Levels)

KeyAction
qQuit application
Ctrl+CForce quit

List Navigation

KeyAction
↑Move cursor up (wrap to bottom)
↓Move cursor down (wrap to top)
PgUpPage up
PgDownPage down
HomeJump to first item
EndJump to last item

Selection

KeyAction
SpaceToggle selection (cursor stays)
a / ASelect all visible items
EnterConfirm selection

Multi-Level Navigation

KeyAction
Esc / bGo back to previous level
EnterProceed to next level

Search Mode

KeyAction
/ / Ctrl+FEnter search mode
TypingAppend to search query
BackspaceRemove last character
EscExit search mode, clear query

6. Multi-Level Page Architecture

Page Flow

Level 1           Level 2             Level 3            Level 4
  │                  │                   │                  │
  ▼                  ▼                   ▼                  ▼
Select ────────► Select ──────────► Select ──────────► Install
AI Tool          Target (global/     Skills (multi-     Progress
                 project)            select + search)

Navigation Rules

  1. Forward: Enter confirms and proceeds to next level
  2. Backward: Esc or b returns to previous level (restarts the loop)
  3. Quit: q or Ctrl+C exits at any level
  4. State preservation: Going back re-runs the previous level from scratch

Orchestration Pattern

Alt screen is managed once at the top level to avoid flicker between pages:

func RunTUI(opts TUIOptions) error {
    // Enter alt screen ONCE for the entire TUI session
    fmt.Print(EnterAltScreen)
    fmt.Print(HideCursor)
    defer func() {
        fmt.Print(ShowCursor)
        fmt.Print(ExitAltScreen)
    }()
    return runTUIFlow(opts)
}

func runTUIFlow(opts TUIOptions) error {
    // Level 1
    fmt.Print(ClearScreen)
    product, err := RunProductSelect(opts.Version, opts.TargetDir)
    if err != nil || product == nil {
        return err
    }

    // Level 2
    fmt.Print(ClearScreen)
    target, err := RunInstallTargetSelect(product, opts.TargetDir)
    ...

    // Level 3
    fmt.Print(ClearScreen)
    selected, deselected, err := RunSkillsSelect(...)
    if err != nil && err.Error() == "go back" {
        return runTUIFlow(opts)  // recursive restart
    }

    // Level 4
    fmt.Print(ClearScreen)
    RunInstaller(selected, deselected, targetDir)

    // Exit alt screen to show final result on normal terminal
    fmt.Print(ShowCursor)
    fmt.Print(ExitAltScreen)
    fmt.Printf("Completed: %d\n", completed)
    return nil
}

7. Terminal Control

Two-Layer Rendering Strategy

The rendering uses a two-layer approach to eliminate flicker:

Layer 1: Alt screen lifecycle (managed by RunTUI)

  • Enter alt screen once at the start, exit once at the end
  • ClearScreen between each page transition (within the alt screen buffer)
  • Never use tea.WithAltScreen() on individual programs — it causes exit/enter cycles that flash the normal terminal

Layer 2: Per-page rendering (managed by Bubble Tea)

  • Each page runs as tea.NewProgram(model) (no WithAltScreen)
  • Bubble Tea handles incremental rendering within the page
  • View() returns complete page content; never include ClearScreen in View()
// Individual programs: NO WithAltScreen
p := tea.NewProgram(model)  // Bubble Tea renders incrementally

// Page transitions: ClearScreen within the shared alt screen
fmt.Print(ClearScreen)      // clears alt screen buffer between pages

Anti-patterns:

  • tea.NewProgram(model, tea.WithAltScreen()) per level → flicker on every page switch
  • ClearScreen inside View() → full repaint on every keystroke
  • fmt.Printf(...) between levels while in alt screen → cursor position corruption

ANSI Sequences

const ClearScreen    = "\033[2J\033[H"    // Clear screen + cursor home
const EnterAltScreen = "\033[?1049h"      // Enter alternate screen buffer
const ExitAltScreen  = "\033[?1049l"      // Exit alternate screen buffer
const HideCursor     = "\033[?25l"        // Hide cursor
const ShowCursor     = "\033[?25h"        // Show cursor

8. Layout Examples

Level 1: Product Selection

🚀 Skills-X - AI Agent Skills Manager  v0.2.12
────────────────────────────────────────────────────────────

   ███████╗██╗  ██╗██╗██╗     ██╗     ███████╗     ██╗  ██╗
   ...
   ╚══════╝╚═╝  ╚═╝╚═╝╚══════╝╚══════╝╚══════╝     ╚═╝  ╚═╝

────────────────────────────────────────────────────────────
  AI 工具                 全局  项目
────────────────────────────────────────────────────────────
❯ Claude Code               45     0
  Cursor                      0    36
  Windsurf                    0     0

────────────────────────────────────────────────────────────
↑↓ 选择 | Enter 确定 | q 退出

Level 3: Skills Selection

🚀 Skills-X - AI Agent Skills Manager  v0.2.12
────────────────────────────────────────────────────────────

   ███████╗...
   ╚══════╝...

────────────────────────────────────────────────────────────

选择要安装的 Skills  Cursor
📁 /path/to/project

 🔍 搜索技能...
                                          已安装
────────────────────────────────────────────────────────────
❯ ☑ anthropic/brainstorming                ✓
  ☑ anthropic/doc-coauthoring              ✓
  ☐ anthropic/frontend-design              ✗
  ...

↑ 3/30 ↓

(共 30 个,已安装 12 个,将安装 3 个,将卸载 1 个)

────────────────────────────────────────────────────────────
/ 搜索 | 空格 选择/反选 | A 全选 | Enter 确认安装/卸载 | b 返回 | q 退出

Level 4: Installation Progress

Installing Skills

Progress: [========================================] 100% (9/9)
Completed: 9 | Failed: 0

To uninstall:
   [OK] anthropic/doc-coauthoring
   [OK] anthropic/frontend-design

────────────────────────────────────────────────────────────
Done! 9 completed.

9. Constants

const SeparatorWidth  = 60  // All separators use this width
const DefaultPageSize = 10  // Default items per page in scrollable lists

10. Design Principles Summary

  1. Consistent Layout: Every page has Header → Info Bar → Content → Footer
  2. Uniform Separators: All ─ lines use SeparatorWidth (60), never hardcode other values
  3. Symmetric Spacing: Logo has equal blank-line padding above and below
  4. Visual-Width Alignment: Use padRight/termWidth for CJK-aware column alignment; pad plain text first, style after
  5. Flicker-Free: Manage alt screen at RunTUI level (enter once, exit once), use ClearScreen between pages, never ClearScreen in View()
  6. Stable UI: Fixed column widths, empty line fill, scroll offset tracking
  7. Multi-Level Flow: Independent programs per level, back/forward navigation

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

百度语音识别 - 将语音消息转换为文本。支持中文普通话、英语、粤语、四川话。专为国内服务器环境优化,自动绕过代理访问百度 API。

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

castle-x/skills-x182026년 4월 16일 업데이트

You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

castle-x/skills-x182026년 4월 16일 업데이트

Use when facing 2+ independent tasks that can be worked on without shared state or sequential dependencies

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

castle-x/skills-x182026년 4월 16일 업데이트

Use when you have a written implementation plan to execute in a separate session with review checkpoints

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

castle-x/skills-x182026년 4월 16일 업데이트

帮助用户将飞书群机器人 Webhook 通知接入 Claude Code,使得每次任务完成后自动发送飞书消息卡片通知。 触发场景(当用户提到以下任意内容时激活此 skill): - "飞书通知"、"接入飞书"、"飞书提醒" - "task 完成通知"、"任务完成提醒" - "claude 完成提醒"、"claude 通知" - "Stop hook 通知"、"hook 通知" - "发飞书消息"、"飞书机器人" - "任务结束通知"、"配置通知"

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

castle-x/skills-x182026년 4월 16일 업데이트

Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

castle-x/skills-x182026년 4월 16일 업데이트

castle-x의 스킬 모두 보기

이 스킬의 문제 신고