
系列文章:原則、色彩與佈局 → 本篇:元件、框架與實戰分析
元件標準
文字輸入
┌─ 搜尋 ──────────────────────┐
│ > 輸入關鍵字... │ ← 灰色佔位文字
└─────────────────────────────┘
┌─ 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 Tea | Go | Elm 架構 | 生產級 CLI 工具 |
| Ink | JS/TS | React 元件模型 | Node.js 工具 |
| Rich | Python | 宣告式 API | 資料展示、美化輸出 |
| Ratatui | Rust | 即時模式渲染 | 高效能系統工具 |
| Textual | Python | CSS-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 確保清理。捕獲 SIGINT、SIGTERM。現代框架通常內建處理,但一定要測試異常情境。
發布前檢查清單
基礎
□ 所有操作都能鍵盤完成
□ 色彩遵循語意(紅=錯誤、綠=成功、黃=警告)
□ 80 欄寬可正常使用
□ 16 色終端可正常使用
導航
□ 支援 j/k 和方向鍵
□ ? 顯示快捷鍵
□ q 和 Ctrl+C 都能退出
□ Tab 切換面板
回饋
□ 每個操作有可見回饋
□ 錯誤有清楚訊息
□ 載入有進度指示
無障礙
□ 不只靠顏色(有符號/文字搭配)
□ Unicode 有 ASCII 降級
□ 深色和淺色背景都測過
健壯性
□ 終端 resize 正確處理
□ 異常退出恢復終端狀態
□ 長文字有截斷/換行
TUI 正在經歷文藝復興,但設計標準還沒跟上。如果你正在開發 TUI 工具,花十分鐘對照這份清單,你的使用者會感謝你。
延伸閱讀
- TUI 設計規範(一):原則、色彩與佈局
- Charm.sh — Bubble Tea 等 Go TUI 工具
- Ratatui 文件 — Rust TUI 框架
- Textual 文件 — Python TUI 框架
- Rich 文件 — Python 終端美化
- Ink 文件 — React 語法寫 TUI
- Terminal.sexy — 終端配色設計工具
- The Art of Command Line