cover

TUI 正在經歷一場文藝復興

終端介面(Terminal User Interface, TUI)正在重新崛起。如果你是開發者,幾乎不可能沒用過這些工具:lazygit 讓 Git 操作變得直覺、btop 把系統監控變成一幅即時儀表板、k9s 讓 Kubernetes 叢集管理從此告別冗長的 kubectl 指令、Charm 團隊打造的一系列工具證明了終端應用可以同時兼具美觀功能性

但問題來了——網頁 UI 有 Material Design、Apple Human Interface Guidelines、Ant Design 等成熟的設計規範,而 TUI 呢?幾乎沒有任何系統性的設計標準。

每個 TUI 工具都在「自己發明輪子」:有的用 j/k 導航,有的用方向鍵;有的用紅色表示錯誤,有的用紅色表示重要;有的按 q 退出,有的按 Ctrl+C,有的甚至需要 :q。使用者在不同工具之間切換時,必須重新學習操作方式,這大幅降低了效率。

本文嘗試提出一套 TUI 設計一致性框架(TUI Design Consistency Framework),為終端介面的色彩系統、互動模式、佈局原則與元件標準提供參考依據。這不是一份強制規格書,而是一份「如果大家都這樣做,使用者會更開心」的建議。

TUI 設計系統的分層架構

一個完整的 TUI 設計系統可以分為四個層級,由底層往上逐步構建:

graph TB
    subgraph Application["應用層 Application"]
        A1[lazygit]
        A2[k9s]
        A3[btop]
        A4[Claude Code]
    end

    subgraph Patterns["模式層 Patterns"]
        P1[導航模式 Navigation]
        P2[回饋模式 Feedback]
        P3[佈局模式 Layout]
        P4[錯誤處理 Error Handling]
    end

    subgraph Components["元件層 Components"]
        C1[文字輸入 Input]
        C2[列表 List]
        C3[表格 Table]
        C4[進度條 Progress]
        C5[對話框 Dialog]
    end

    subgraph Foundation["基礎層 Foundation"]
        F1[色彩系統 Colors]
        F2[文字排版 Typography]
        F3[間距系統 Spacing]
        F4[圖示符號 Icons/Symbols]
    end

    Foundation --> Components --> Patterns --> Application

每一層都依賴下一層的定義。基礎層決定了「我們有哪些顏色和字元可用」,元件層定義了「每個互動單元長什麼樣」,模式層描述了「元件如何組合成使用流程」,最終在應用層組裝成完整的工具。

為什麼 TUI 現在正在流行?

開發者工具回歸終端

越來越多開發者工具選擇 TUI 而非 GUI 作為介面:

工具用途取代了什麼
lazygitGit 視覺化操作GitKraken, SourceTree
lazydockerDocker 容器管理Docker Desktop
k9sKubernetes 叢集管理Kubernetes Dashboard
btop / htop系統資源監控系統監控 GUI
yazi檔案管理器Finder, Explorer

AI 編碼助手以 TUI 為主要介面

這是一個重大轉折:Claude CodeGitHub Copilot CLIaider 等 AI 編碼助手選擇了終端作為主要互動介面。當 AI 工具也選擇 TUI,代表這不只是復古潮流,而是一種有實質優勢的介面選擇。

核心優勢

  1. SSH 可達:透過遠端連線即可操作,不需要 GUI 環境。在伺服器上、在 Docker 容器中、在任何有 SSH 的地方都能使用
  2. 效能卓越:啟動時間通常在毫秒級,記憶體占用極低。相較之下,Electron 應用動輒數百 MB
  3. 鍵盤效率:對於熟練使用者,鍵盤操作遠比滑鼠快。不需要在鍵盤與滑鼠之間反覆切換
  4. 可腳本化:終端工具天然適合自動化和管道串接
  5. 現代框架降低門檻:Bubble Tea(Go)、Ink(JS)、Rich(Python)、Ratatui(Rust)讓打造精美 TUI 變得前所未有地簡單

TUI 設計五大原則

原則一:鍵盤優先(Keyboard-First)

每一個操作都必須能透過鍵盤完成。 滑鼠支援是加分項,但絕不是必要條件。

這與網頁設計的「行動裝置優先」(Mobile-First)類似——先確保核心互動在最受限的情境下可用,再往上擴展。

好的做法:
  按 Enter 確認 | 按 Esc 取消 | 按 ? 顯示說明

不好的做法:
  [請點擊確認按鈕](終端使用者可能根本沒有滑鼠)

原則二:漸進式揭露(Progressive Disclosure)

先顯示最重要的資訊,將細節藏在「按需展開」的互動中。終端空間有限,不能像網頁一樣無限捲動。

  • 第一層:關鍵摘要(檔案名稱、狀態、數量)
  • 第二層:按 Enter 或方向鍵展開詳細資訊
  • 第三層:按特定鍵(如 d 查看 diff, l 查看 log)進入深層檢視

lazygit 是漸進式揭露的典範:左側面板顯示檔案列表,選取後右側顯示 diff,按 Enter 可進入逐行檢視。

原則三:情境感知(Context Awareness)

使用者在任何時刻都必須能回答三個問題:

  1. 我在哪裡?(目前在哪個面板/模式/頁面)
  2. 我能做什麼?(目前可用的操作)
  3. 剛才發生了什麼?(上一個操作的結果)

實作方式:

  • 使用高亮/反白標示目前焦點
  • 底部狀態列顯示可用的快捷鍵
  • 操作後顯示簡短的回饋訊息

原則四:優雅降級(Graceful Degradation)

TUI 必須在各種終端環境中運作:

環境寬度色彩支援字元支援
最小終端80x2416 色ASCII only
標準終端120x40256 色基本 Unicode
現代終端200+True Color完整 Unicode + Nerd Font

設計時應以 80 欄寬為基準,確保核心功能在最小終端中依然可用。寬螢幕是加分項,不是必要條件。

原則五:一致的回饋(Consistent Feedback)

每個操作都應該有可見的回饋:

  • 成功:綠色 ✓ Done[OK]
  • 失敗:紅色 ✗ Error: ...[FAIL]
  • 進行中:動畫旋轉器 ⠋ Loading...
  • 警告:黃色 ⚠ Warning: ...
  • 無操作:簡短說明為什麼什麼都沒發生

沒有回饋是最糟糕的使用者體驗。使用者按了一個鍵,什麼都沒發生——是成功了?失敗了?還是沒偵測到按鍵?

色彩系統(Color System)

ANSI 標準 16 色

終端色彩的基礎是 ANSI 16 色,這是幾乎所有終端都支援的最低公分母:

色彩標準用途語意映射
黑色 (Black)背景背景、隱藏文字
紅色 (Red)錯誤錯誤、刪除、危險操作
綠色 (Green)成功成功、新增、安全操作
黃色 (Yellow)警告警告、注意、修改中
藍色 (Blue)資訊導航、連結、說明
洋紅 (Magenta)強調特殊標記、關鍵字
青色 (Cyan)輔助資訊路徑、URL、次要資訊
白色 (White)主要文字主要內容、聚焦元素
亮色變體對應色的淺色版用於高亮或強調

語意色彩映射(Semantic Color Mapping)

不要用「紅色」思考,要用「語意」思考:

語意          色彩          符號      使用場景
──────────────────────────────────────────────────────
error         Red           ✗ [!]     錯誤訊息、失敗狀態
success       Green         ✓ [OK]    成功訊息、完成狀態
warning       Yellow        ⚠ [!]     警告訊息、需要注意
info          Blue          ℹ [i]     提示訊息、說明
primary       Bold/White    ▶         主要內容、焦點
secondary     Dim/Gray      ·         次要內容、已停用
destructive   Red + Bold    ✗✗        刪除、不可逆操作

256 色與 True Color

現代終端(iTerm2, WezTerm, Windows Terminal, Kitty, Alacritty)支援 256 色甚至 24-bit True Color。但要注意:

  • 永遠提供 16 色降級方案:不是每個終端都支援 True Color
  • $COLORTERM 環境變數偵測支援度truecolor24bit 表示支援
  • 256 色用於漸層和圖表:btop 的 CPU 使用率圖表就是好例子

無障礙:不要只靠顏色

這是最容易被忽略的原則:永遠不要讓顏色成為唯一的資訊傳遞方式

色覺障礙者(約 8% 的男性、0.5% 的女性)可能無法區分紅色和綠色。因此:

不好的做法:
  紅色文字 = 失敗  |  綠色文字 = 成功

好的做法:
  ✗ 紅色文字 = 失敗  |  ✓ 綠色文字 = 成功
  [FAIL] 紅色背景   |  [OK] 綠色背景

同時使用顏色 + 符號(或顏色 + 文字標籤),確保資訊在單色環境中依然可讀。

深色/淺色終端

許多開發者使用淺色背景的終端(是的,他們存在)。設計時要考慮:

  • 避免使用「白色文字 + 黑色背景」的硬編碼組合
  • 使用終端的「預設前景色」和「預設背景色」作為基準
  • 測試時同時在深色和淺色主題下驗證可讀性

佈局模式(Layout Patterns)

全螢幕應用佈局

最常見的 TUI 應用佈局:

┌─────────────────────────────────────────────────┐
│  標題列 (Title Bar)                    [模式/狀態] │
├──────────────┬──────────────────────────────────┤
│              │                                  │
│  側邊欄       │  主要內容區                       │
│  (Sidebar)   │  (Main Content)                  │
│              │                                  │
│  - 項目 1    │  詳細資訊顯示在這裡               │
│  > 項目 2    │  ...                             │
│  - 項目 3    │                                  │
│              │                                  │
├──────────────┴──────────────────────────────────┤
│  狀態列: ↑↓ 導航 | Enter 選取 | q 退出 | ? 說明  │
└─────────────────────────────────────────────────┘

關鍵設計點

  • 標題列:顯示應用名稱、目前模式、連線狀態等全域資訊
  • 側邊欄:寬度固定(通常 20-30 欄),顯示導航或列表
  • 主要內容區:佔據剩餘空間,顯示詳細內容
  • 狀態列:永遠顯示在最下方,列出目前可用的快捷鍵

列表/詳情分割(List-Detail Split)

這是最實用的 TUI 模式。lazygit、k9s 都使用這個模式:

  • 左側面板:項目列表,以高亮標示目前選取項
  • 右側面板:選取項的詳細資訊
  • 導航j/k↑/↓ 在列表中移動,焦點切換時右側即時更新

頁籤導航(Tab Navigation)

┌──────┬──────┬──────┬──────┐
│ 檔案 │ 分支 │ 提交 │ 暫存 │
└──┬───┴──────┴──────┴──────┘
   │ (目前頁籤底線標示)

使用 Tab / Shift+Tab 或數字鍵 1/2/3/4 切換頁籤。

模態對話框(Modal Dialog)

┌───────────────────────────────┐
│                               │
│   ┌─── 確認刪除 ──────────┐   │
│   │                       │   │
│   │  確定要刪除 main.js? │   │
│   │                       │   │
│   │  [Yes]     [No]      │   │
│   └───────────────────────┘   │
│                               │
└───────────────────────────────┘

模態對話框應該:

  • 使用方框字元(box-drawing characters)繪製邊框
  • 背景使用暗色遮罩(如果終端支援)
  • 預設焦點在安全選項上(通常是 “No” 或 “Cancel”)

響應式設計

根據終端寬度調整佈局:

寬度 >= 120:側邊欄 + 主內容(雙欄)
寬度 80-119:隱藏側邊欄,改用全螢幕列表 + Enter 進入詳情
寬度 < 80:  簡化顯示,截斷長文字,隱藏非必要欄位

間距與結構字元

使用 Unicode 方框繪製字元建立視覺結構:

基本邊框:─ │ ┌ ┐ └ ┘
T 型接合:┬ ┤ ┴ ├
十字交叉:┼
雙線邊框:═ ║ ╔ ╗ ╚ ╝(用於強調、模態)
圓角邊框:╭ ╮ ╰ ╯(現代風格,但部分終端不支援)

互動模式(Interaction Patterns)

導航

建立一致的導航快捷鍵:

操作主要按鍵替代按鍵來源
上移kVim
下移jVim
左移/收合hVim
右移/展開lVim
跳到頂部gHomeVim (gg)
跳到底部GEndVim (G)
上一頁Ctrl+uPgUpVim
下一頁Ctrl+dPgDnVim
切換面板TabCtrl+w通用/Vim

為什麼選 Vim 鍵位? 因為終端使用者群與 Vim 使用者群高度重疊。不需要強制 Vim 鍵位,但同時支援 Vim 鍵位和方向鍵是最佳實踐。

選取與確認

操作按鍵說明
切換選取Space勾選/取消勾選當前項目
確認Enter執行操作或進入下一層
取消Esc返回上一層或關閉對話框
全選Ctrl+a選取所有項目

搜尋與篩選

操作按鍵說明
開始搜尋/進入搜尋模式(Vim 慣例)
下一個結果n跳到下一個匹配項
上一個結果N跳到上一個匹配項
清除搜尋Esc退出搜尋模式

系統操作

操作按鍵說明
顯示說明?開啟快捷鍵說明頁面
退出q退出應用(如果有未儲存變更,先確認)
強制退出Ctrl+C通用中斷訊號,永遠要能用
重新整理r重新載入/刷新資料

狀態列:動態提示可用操作

狀態列應根據目前情境顯示不同的可用操作:

在列表頁:
  ↑↓ 導航 | Enter 檢視 | d 刪除 | / 搜尋 | ? 說明

在編輯模式:
  Esc 取消 | Ctrl+S 儲存 | Tab 下一個欄位

在確認對話框:
  y 確認 | n 取消 | Enter 執行預設選項

元件標準(Component Standards)

文字輸入(Text Input)

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

┌─ 搜尋 ─────────────────────────────────┐
│ > main█                                │  ← 游標以方塊顯示
└─────────────────────────────────────────┘

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

設計要點:

  • 使用 > 作為輸入提示符號
  • 佔位文字使用暗色/灰色
  • 驗證錯誤即時顯示在輸入框下方
  • 支援 Ctrl+A(全選)、Ctrl+K(刪除到行尾)等 Emacs 風格編輯鍵

選取列表(Select / List)

  Select a branch:

    develop
  ▶ main                     ← 高亮 + 箭頭標示目前選取
    feature/auth
    feature/ui-update
    hotfix/login-bug

  [3/5] ↑↓ 移動 | Enter 選取 | / 篩選

設計要點:

  • 使用 或反白標示目前選取項
  • 顯示「目前位置/總數」(如 [3/5]
  • 列表超出可視範圍時顯示捲動提示(↑ 還有更多 / ↓ 還有更多
  • 支援增量搜尋(輸入文字即時篩選)

表格(Table)

┌──────────────┬──────────┬──────────┬───────────┐
│ 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      │
└──────────────┴──────────┴──────────┴───────────┘
  1-4 of 12 | ▼ 表示降序排序 | Tab 切換排序欄位

設計要點:

  • 標題列使用粗體或不同顏色
  • 排序方向使用 / 標示
  • 數值靠右對齊,文字靠左對齊
  • 狀態使用語意色彩(Running=綠, Warning=黃, Stopped=紅)

進度指示(Progress Indicator)

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

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

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

確認對話框(Confirmation Dialog)

一般確認:
  是否繼續? [Y/n]          ← 大寫字母 = 預設選項

危險操作確認:
  ⚠ 此操作將刪除所有資料且無法復原。
  請輸入 "delete-all" 以確認:> █

  (對於不可逆的危險操作,要求使用者輸入確認文字)

通知與訊息(Notifications)

成功:✓ 檔案已儲存                    ← 綠色,2 秒後自動消失
錯誤:✗ 無法連線到伺服器:timeout      ← 紅色,停留直到按鍵關閉
警告:⚠ 磁碟空間不足(剩餘 2%)       ← 黃色,停留 5 秒
資訊:ℹ 有 3 個更新可用               ← 藍色,2 秒後自動消失

現代 TUI 框架比較

框架語言架構風格最適合學習曲線
Bubble TeaGoElm 架構(Model-Update-View)生產級 CLI 工具中等
InkJavaScript/TypeScriptReact 元件模型Node.js 開發者工具低(熟悉 React 的話)
RichPython宣告式 API腳本、資料展示、美化輸出
RatatuiRust即時模式渲染(Immediate Mode)高效能、系統工具
TextualPythonCSS-like 樣式系統複雜的全功能應用中等

Bubble Tea 範例:基本計數器

Bubble Tea 使用 Elm 架構——把狀態管理和 UI 渲染完全分離。這是 Go 生態中最受歡迎的 TUI 框架。

package main
 
import (
    "fmt"
    tea "github.com/charmbracelet/bubbletea"
)
 
// Model:應用狀態
type model struct {
    count    int
    quitting bool
}
 
// Init:初始化指令
func (m model) Init() tea.Cmd {
    return nil
}
 
// Update:處理訊息,更新狀態
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
}
 
// View:根據狀態渲染 UI
func (m model) View() string {
    if m.quitting {
        return "Bye!\n"
    }
    return fmt.Sprintf(
        "\n  計數器: %d\n\n  ↑/k 增加 | ↓/j 減少 | q 退出\n",
        m.count,
    )
}
 
func main() {
    p := tea.NewProgram(model{count: 0})
    if _, err := p.Run(); err != nil {
        fmt.Printf("Error: %v\n", err)
    }
}

設計重點:注意 View() 中的狀態列永遠顯示可用操作,同時支援 Vim 鍵位(j/k)和方向鍵。

Rich 範例:美觀的資料表格

Rich 是 Python 生態中最流行的終端美化函式庫,特別適合輸出格式化資料。

from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich.text import Text
from rich import box
 
console = Console()
 
# 建立表格
table = Table(
    title="🖥 服務狀態監控",
    box=box.ROUNDED,          # 使用圓角邊框
    show_lines=True,          # 顯示行分隔線
    header_style="bold cyan", # 標題列樣式
)
 
# 定義欄位
table.add_column("服務名稱", style="bold white", min_width=15)
table.add_column("狀態", justify="center", min_width=10)
table.add_column("CPU", justify="right", min_width=8)
table.add_column("記憶體", justify="right", min_width=10)
table.add_column("回應時間", justify="right", min_width=10)
 
# 加入資料(使用語意色彩)
table.add_row(
    "web-server",
    "[green]● Running[/green]",
    "23.4%",
    "512 MB",
    "[green]45ms[/green]",
)
table.add_row(
    "api-gateway",
    "[green]● Running[/green]",
    "11.2%",
    "256 MB",
    "[green]12ms[/green]",
)
table.add_row(
    "db-primary",
    "[yellow]● Warning[/yellow]",
    "[yellow]67.8%[/yellow]",
    "[yellow]2.1 GB[/yellow]",
    "[yellow]230ms[/yellow]",
)
table.add_row(
    "worker-01",
    "[red]● Stopped[/red]",
    "0.0%",
    "0 MB",
    "[red]N/A[/red]",
)
 
# 輸出表格
console.print()
console.print(table)
console.print()
 
# 加入摘要面板
summary = Text()
summary.append("✓ 2 ", style="green")
summary.append("正常運行  ")
summary.append("⚠ 1 ", style="yellow")
summary.append("需要關注  ")
summary.append("✗ 1 ", style="red")
summary.append("已停止")
 
console.print(Panel(summary, title="摘要", border_style="blue"))

設計重點:使用語意色彩(綠=正常、黃=警告、紅=錯誤)+ 符號(●, ✓, ⚠, ✗)雙重傳遞狀態資訊。

Ratatui 的架構特點

Ratatui 使用即時模式渲染,每一幀都重新繪製整個畫面。這種方式特別適合需要高效能更新的應用(如系統監控):

// Ratatui 的核心渲染迴圈概念
fn ui(frame: &mut Frame, app: &App) {
    // 將終端分割為多個區域
    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Length(3),    // 標題列
            Constraint::Min(0),       // 主內容
            Constraint::Length(1),    // 狀態列
        ])
        .split(frame.area());
 
    // 渲染標題
    let title = Paragraph::new("System Monitor")
        .style(Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD))
        .block(Block::default().borders(Borders::ALL));
    frame.render_widget(title, chunks[0]);
 
    // 渲染主內容(表格、圖表等)
    // ...
 
    // 渲染狀態列
    let status = Paragraph::new(" q: Quit | r: Refresh | ?: Help")
        .style(Style::default().fg(Color::DarkGray));
    frame.render_widget(status, chunks[2]);
}

真實世界 TUI 設計分析

lazygit:把 Git 複雜性馴服在終端裡

lazygit 是 TUI 設計的標竿之作,值得仔細研究它的設計決策:

  • 五面板佈局:Status、Files、Branches、Commits、Stash 分別佔據左側五個面板,右側顯示詳情
  • Tab 切換:數字鍵 1-5 快速切換面板焦點
  • 漸進式揭露的極致:在檔案列表中按 Enter 可以看到 diff,在 diff 中可以選取特定行進行 stage
  • 上下文選單:不同面板中按同一個鍵(如 d)會執行不同操作(刪除檔案 vs 刪除分支)
  • 即時回饋:每個操作都立即反映在 UI 上

k9s:Kubernetes 的終端儀表板

k9s 把複雜的 Kubernetes API 轉化為直覺的 TUI 介面:

  • 指令列模式:按 : 進入指令列,輸入資源類型(:pods, :deployments
  • 快速篩選:按 / 即時篩選資源
  • 上下文感知的操作:選取不同資源時,可用操作會動態更新
  • 色彩編碼:Pod 狀態一目瞭然(Running=綠, Pending=黃, Error=紅)
  • 麵包屑導航:頂部始終顯示 Context > Namespace > Resource 的路徑

btop:系統監控的藝術品

btop 展示了 TUI 可以有多美觀:

  • 即時圖表:CPU、記憶體使用率以波形圖呈現,利用 Unicode 方塊字元實現像素級渲染
  • 色彩漸層:從綠到黃到紅的漸層表示資源使用程度
  • 自適應佈局:根據終端大小動態調整圖表和表格的比例
  • 主題系統:支援多種配色方案,適應不同終端和偏好

Claude Code:AI 時代的 TUI 先驅

Claude Code 作為 AI 編碼助手,展示了 TUI 在 AI 時代的設計方向:

  • 對話式互動:主要介面是文字輸入與輸出的串流,符合終端的天然強項
  • 結構化輸出:程式碼區塊、diff 顯示、檔案樹都有清晰的視覺區隔
  • 進度回饋:長時間操作(讀取檔案、思考中)有明確的載入指示
  • 上下文保持:對話歷史在終端中清晰可追溯

建議的 TUI 設計檢查清單

在發布任何 TUI 應用之前,建議對照以下清單逐項檢查:

### 基礎
- [ ] 所有操作都可以透過鍵盤完成
- [ ] 色彩編碼遵循語意映射(紅=錯誤, 綠=成功, 黃=警告)
- [ ] 在 80 欄寬的終端中可正常使用
- [ ] 在 16 色終端中可正常使用(色彩降級)
 
### 導航
- [ ] 支援 j/k(Vim)和方向鍵兩種導航方式
- [ ] 按 ? 可顯示所有快捷鍵說明
- [ ] 按 q 或 Ctrl+C 可以退出
- [ ] Tab / Shift+Tab 可在面板間切換
 
### 回饋
- [ ] 每個操作都有可見的回饋
- [ ] 錯誤狀態有清晰的錯誤訊息
- [ ] 載入狀態有進度指示器
- [ ] 成功操作有確認訊息
 
### 無障礙
- [ ] 不只靠顏色傳遞資訊(有搭配符號或文字)
- [ ] Unicode 字元有 ASCII 降級方案
- [ ] 支援深色和淺色終端背景
 
### 健壯性
- [ ] 正確處理終端大小調整(resize)
- [ ] 異常退出時恢復終端狀態(不留下亂碼)
- [ ] 長文字有截斷或換行處理

常見問題與風險

色彩在某些終端中壞掉

問題:你在 iTerm2 中精心調整的色彩,在 Linux TTY 或舊版 PuTTY 中變得面目全非。

解決方案

  • 偵測 $TERM$COLORTERM 環境變數,動態調整色彩方案
  • 永遠以 ANSI 16 色為基線設計
  • 提供 --no-color 或遵循 NO_COLOR 環境變數標準(參見 no-color.org)

Unicode 字元無法渲染

問題:方框繪製字元 ┌─┐、圓角 ╭╮、或符號 ✓ ✗ 在某些終端中顯示為亂碼或問號。

解決方案

提供 ASCII 降級:
  Unicode:  ╭──────────╮    ASCII:  +----------+
            │  Hello   │           |  Hello   |
            ╰──────────╯           +----------+

  Unicode:  ✓ 成功      ASCII:  [OK] 成功
  Unicode:  ✗ 失敗      ASCII:  [FAIL] 失敗
  Unicode:  ⚠ 警告      ASCII:  [WARN] 警告

終端太小

問題:使用者在 80x24 甚至更小的終端中執行你的應用。

解決方案

  • 設定最小終端大小(例如 60x20),太小時顯示友善提示
  • 監聽 SIGWINCH 訊號(終端大小變更),動態重排佈局
  • 優先保留核心功能,次要資訊在空間不足時自動隱藏

螢幕閱讀器的困境

問題:TUI + 螢幕閱讀器(Screen Reader)基本上是一個未解決的問題。終端輸出對螢幕閱讀器來說很難解析。

目前的最佳實踐

  • 避免使用純裝飾性的 Unicode 字元(螢幕閱讀器會逐字唸出來)
  • 提供純文字模式(--accessible 旗標)
  • 確保所有狀態訊息是完整的句子,而不只是符號
  • 這是整個 TUI 生態系統需要共同改進的領域

終端狀態殘留

問題:程式異常退出後,終端留在 raw mode,游標消失、echo 關閉。

解決方案

  • 使用 deferfinally 確保清理程式碼一定會執行
  • 捕獲 SIGINTSIGTERM 訊號,在退出前恢復終端狀態
  • 現代框架(Bubble Tea, Ratatui)通常已內建處理,但務必測試異常情境

展望:TUI 設計的未來

TUI 設計正處於一個有趣的轉折點。隨著 AI 工具越來越多地選擇終端作為主要介面,TUI 的重要性只會持續增長。我們可能會看到:

  1. TUI 設計系統的標準化:就像網頁有 Material Design,TUI 也需要統一的設計語言
  2. 更好的無障礙支援:終端模擬器和 TUI 框架需要共同改善螢幕閱讀器支援
  3. 跨框架的元件生態:標準化的 TUI 元件可以在不同框架間共享
  4. AI 驅動的自適應介面:根據使用者習慣和情境自動調整佈局和快捷鍵

延伸閱讀