cover

系列文章:原則、色彩與佈局本篇:元件、框架與實戰分析

元件標準

文字輸入

┌─ 搜尋 ──────────────────────┐
│ > 輸入關鍵字...              │  ← 灰色佔位文字
└─────────────────────────────┘

┌─ Email ─────────────────────┐
│ > not-an-email              │
│ ✗ 請輸入有效的 Email 地址   │  ← 紅色驗證錯誤
└─────────────────────────────┘

> 作輸入提示、灰色佔位文字、驗證錯誤即時顯示。支援 Emacs 風格快捷鍵(Ctrl+A 全選、Ctrl+K 刪到行尾)——這不是偏好問題,是 readline 的通用慣例,終端使用者預期這些會動。

選取列表

  Select a branch:

    develop
  ▶ main                     ← 高亮 + 箭頭
    feature/auth
    feature/ui-update

  [2/4] ↑↓ 移動 | Enter 選取 | / 篩選

顯示「目前位置/總數」。超出可視範圍時提示「還有更多」。支援增量搜尋(打字即時篩選)——這是從 fzf 學來的互動模式,現在已經是標配。

表格

┌──────────────┬──────────┬──────────┬───────────┐
│ Name         │ Status ▼ │ CPU      │ Memory    │
├──────────────┼──────────┼──────────┼───────────┤
│ web-server   │ Running  │ 23.4%    │ 512 MB    │
│ api-gateway  │ Running  │ 11.2%    │ 256 MB    │
│ db-primary   │ Warning  │ 67.8%    │ 2.1 GB    │
│ worker-01    │ Stopped  │ 0.0%     │ 0 MB      │
└──────────────┴──────────┴──────────┴───────────┘

標題列粗體或不同色、排序方向用 ▲/▼ 標示、數值靠右對齊、狀態用語意色彩(Running=綠、Warning=黃、Stopped=紅)。

進度指示

短時操作(< 2 秒)用旋轉器:
  ⠋ 正在載入...

長時操作用進度條:
  下載中 ████████████░░░░░░░░  62% | 3.1 MB/s | 剩餘 12s

多步驟操作:
  [1/4] ✓ 安裝相依套件
  [2/4] ✓ 編譯原始碼
  [3/4] ⠋ 執行測試...
  [4/4]   部署(等待中)

旋轉器和進度條的選擇不是美觀問題——使用者看到旋轉器知道「在跑了」,看到進度條知道「大概還要多久」。

現代 TUI 框架

框架語言架構適合
Bubble TeaGoElm 架構生產級 CLI 工具
InkJS/TSReact 元件模型Node.js 工具
RichPython宣告式 API資料展示、美化輸出
RatatuiRust即時模式渲染高效能系統工具
TextualPythonCSS-like 樣式複雜全功能應用

Bubble Tea 範例

Bubble Tea 用 Elm 架構——Model(狀態)、Update(處理事件)、View(渲染 UI)完全分離:

type model struct {
    count    int
    quitting bool
}
 
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        switch msg.String() {
        case "q", "ctrl+c":
            m.quitting = true
            return m, tea.Quit
        case "k", "up":
            m.count++
        case "j", "down":
            if m.count > 0 { m.count-- }
        }
    }
    return m, nil
}
 
func (m model) View() string {
    if m.quitting { return "Bye!\n" }
    return fmt.Sprintf(
        "\n  計數器: %d\n\n  ↑/k 增加 | ↓/j 減少 | q 退出\n",
        m.count,
    )
}

注意 View 裡的狀態列——永遠告訴使用者能做什麼。同時支援 Vim 鍵位和方向鍵。這就是前篇提到的設計原則在 code 裡的體現。

Rich 範例

Rich 特別適合「把終端輸出變好看」:

from rich.console import Console
from rich.table import Table
from rich import box
 
console = Console()
table = Table(title="服務狀態", box=box.ROUNDED, header_style="bold cyan")
 
table.add_column("服務", style="bold white")
table.add_column("狀態", justify="center")
table.add_column("CPU", justify="right")
 
table.add_row("web-server", "[green]● Running[/green]", "23.4%")
table.add_row("db-primary", "[yellow]● Warning[/yellow]", "[yellow]67.8%[/yellow]")
table.add_row("worker-01", "[red]● Stopped[/red]", "0.0%")
 
console.print(table)

語意色彩 + 符號雙重傳遞狀態。色覺障礙者看不到綠紅差異,但能看到 後面的文字。

真實世界 TUI 分析

lazygit:TUI 設計的標竿

如果你只能研究一個 TUI 工具的設計,選 lazygit。

  • 五面板佈局:Status、Files、Branches、Commits、Stash 各佔左側一格,右側即時詳情
  • 漸進式揭露的極致:檔案列表 → Enter 看 diff → 在 diff 裡選特定行做 stage
  • 上下文操作:同一個 d 鍵,在檔案面板是刪除檔案,在分支面板是刪除分支
  • 每個操作都有即時回饋

k9s:複雜 API 的終端化

k9s 把 Kubernetes 的複雜性收進了 TUI:

  • :pods:deployments 的指令列模式——跟 Vim 一樣
  • / 即時篩選
  • Pod 狀態一目瞭然(Running=綠、Pending=黃、Error=紅)
  • 頂部永遠顯示 Context > Namespace > Resource 麵包屑

btop:終端可以有多美

CPU 使用率波形圖用 Unicode 方塊字元實現像素級渲染、從綠到黃到紅的色彩漸層、根據終端大小動態調整比例。證明了 TUI 不等於醜。

常見問題

色彩壞掉了? 偵測 $TERM$COLORTERM,以 16 色為基線。提供 --no-color 或遵循 NO_COLOR 環境變數。

Unicode 字元顯示亂碼? 提供 ASCII 降級:

Unicode: ╭────╮  →  ASCII: +----+
         ✓ 成功  →         [OK] 成功

終端太小? 設最小大小(60x20),太小顯示提示。監聽 SIGWINCH 動態重排。核心功能優先,次要資訊空間不足時隱藏。

程式異常退出,終端壞掉?defer/finally 確保清理。捕獲 SIGINTSIGTERM。現代框架通常內建處理,但一定要測試異常情境。

發布前檢查清單

基礎
□ 所有操作都能鍵盤完成
□ 色彩遵循語意(紅=錯誤、綠=成功、黃=警告)
□ 80 欄寬可正常使用
□ 16 色終端可正常使用

導航
□ 支援 j/k 和方向鍵
□ ? 顯示快捷鍵
□ q 和 Ctrl+C 都能退出
□ Tab 切換面板

回饋
□ 每個操作有可見回饋
□ 錯誤有清楚訊息
□ 載入有進度指示

無障礙
□ 不只靠顏色(有符號/文字搭配)
□ Unicode 有 ASCII 降級
□ 深色和淺色背景都測過

健壯性
□ 終端 resize 正確處理
□ 異常退出恢復終端狀態
□ 長文字有截斷/換行

TUI 正在經歷文藝復興,但設計標準還沒跟上。如果你正在開發 TUI 工具,花十分鐘對照這份清單,你的使用者會感謝你。


延伸閱讀