esc-cancel-f10-quit-autosave

star 0

互動式 app (GUI/TUI/遊戲) 離開語意鐵則:ESC 只 cancel/back,F10(或 Ctrl+Q)才離開,離開前跳 Yes/No 並自動存檔。觸發:做 input handler/選單導航/quit 鍵/存檔系統,或「按 ESC 跳出/結束」「按鍵設計」「離開前確認」「自動存檔」「quit dialog」「modal」「按錯鍵丟進度」。SDL/curses/web/Electron/CLI wizard 皆適用。完整鐵則見內文。

wicanr2 By wicanr2 schedule Updated 6/13/2026

name: esc-cancel-f10-quit-autosave description: 互動式 app (GUI/TUI/遊戲) 離開語意鐵則:ESC 只 cancel/back,F10(或 Ctrl+Q)才離開,離開前跳 Yes/No 並自動存檔。觸發:做 input handler/選單導航/quit 鍵/存檔系統,或「按 ESC 跳出/結束」「按鍵設計」「離開前確認」「自動存檔」「quit dialog」「modal」「按錯鍵丟進度」。SDL/curses/web/Electron/CLI wizard 皆適用。完整鐵則見內文。

ESC = cancel only · F10 = quit + auto-save + confirm dialog

核心:互動式 app(GUI / TUI / wizard / 遊戲)的離開語意,必須分成 「取消當前操作」與「結束整個 app」兩條完全不同的路徑。ESC 永遠是前者

本 skill 從 wizardry-1-cht v1.25.3 game-tester 回報事件 萃取:QA agent 兩次誤觸 ESC 把 60 分鐘 progress 噴掉 → 鐵則化。

鐵則

1. ESC 只能取消,不能結束

場景層級 ESC 行為
子選單 / 對話框 / picker 關閉這個 sub-mode,回上層
場景級(戰鬥 / 商店 / 旅館) 回上一個 scene(Castle / EdgeOfTown)
最外層選單(EdgeOfTown / 主選單) 回 Title,不能 kill main loop
Title 畫面 不做事 OR 進入確認流程,不能直接 exit

Why:使用者按 ESC 的肌肉記憶是「我按錯了,回去」。任何 ESC 路徑造成不可逆損失(沒存檔的進度、未提交的編輯、買到的 IAP)= 設計失誤

How to apply:每加一個新場景時,先想清楚 ESC 對應的「上一層」是什麼。寫 unit 或 smoke test 證明 ESC 不會 return false 主迴圈。

2. F10(或 Ctrl+Q)是唯一的離開手勢

選擇單一鍵作為明確的「我要離開」訊號。建議 F10(單鍵、跟 ESC 在物理鍵盤上分開)或 Ctrl+Q(保留 Ctrl 慣例)。

禁止

  • ❌ ESC = 在某個場景退出(破壞 ESC 一致性)
  • ❌ 視窗叉叉按下 = 直接 exit(沒給確認機會)
  • ❌ Alt+F4 / Cmd+Q 直接 kill(OS 級事件也應該攔截到 dialog 流程)

How to apply:在全域 input handler 加 F10 監聽,所有「離開」入口(menu L)eave Game、視窗關閉事件等)都路由到同一個 quit-dialog state。

3. 結束遊戲必須跳 Yes/No dialog

按下 F10 → 不能直接 exit,必須先彈出確認視窗:

+----------------------------------+
|        確定離開遊戲?             |
|                                  |
|  離開前會自動存檔到 Slot 1。      |
|                                  |
|  Y / Enter — 存檔退出             |
|  N / ESC   — 取消                 |
+----------------------------------+

UI 要求:

  • 置中 modal、translucent 黑色 scrim 暗化背後場景
  • 標題用 accent 色強調
  • Yes 按鍵明確(Y 或 Enter)
  • No 按鍵明確(N 或 ESC,因為 ESC = cancel 的鐵則延伸到這個 modal)
  • 不可有「確定」「取消」這種模糊雙按鈕;要有單鍵 yes/no 對應

4. Yes 一定先 auto-save,再 exit

按下 Yes 之後的順序必須是:

save_game(state, default_save_path())  // 先存
↓
return false                            // 才退出主迴圈

理由:玩家明確選了 Yes = 他相信你不會吃他的存檔。Save 失敗時(disk full / 權限問題)必須回頭顯示錯誤訊息 + 不退出,不能默默吞掉

例外:title 場景 / 還沒進遊戲狀態時可以不存(沒東西可存)。但其他每個場景都要存。

實作模式(C++ / SDL2 範例)

// 1. State 加 flag
struct State {
    bool quit_dialog_active = false;
    ...
};

// 2. 主 tick() 最頂層處理 dialog 覆蓋
bool tick(State& state, const SDL_Event* ev, const UI& ui) {
    if (state.quit_dialog_active) {
        if (ev && ev->type == SDL_KEYDOWN) {
            auto k = ev->key.keysym.sym;
            if (k == SDLK_y || k == SDLK_RETURN) {
                save_game(state, default_save_path());
                return false;  // 主迴圈退出
            }
            if (k == SDLK_n || k == SDLK_ESCAPE) {
                state.quit_dialog_active = false;
                return true;
            }
        }
        draw_quit_dialog(ui);
        return true;
    }

    // 3. F10 全域熱鍵
    if (ev && ev->type == SDL_KEYDOWN && ev->key.keysym.sym == SDLK_F10) {
        state.quit_dialog_active = true;
        return true;
    }

    // 4. 場景級 ESC 永遠只 back 一層
    // ...
}

// 5. L)eave Game 選單路由同一個 dialog
if (target == Scene::Quit) {
    state.quit_dialog_active = true;
    return true;  // NOT return false
}

對應到其他框架

框架 ESC = cancel 離開鍵 Save 入口
SDL2 game SDLK_ESCAPE 回上一 scene SDLK_F10 全域熱鍵 save_game(state, path)
curses TUI KEY_ESC 退出子模式 Ctrl+Q(KEY_DC 等) 自訂 persist 函式
Electron / Web keydown event.key === 'Escape' 關 modal Ctrl/Cmd+Q 或選單「離開」 localStorage / IndexedDB sync
CLI wizard (rich/prompt_toolkit) ESC 回上一個 step Ctrl+C 但 trap 到 dialog 寫 partial draft 到 disk
iOS / Android back button 退一層 「結束」menu item Core Data / Room save

反模式(會踩雷)

  • ❌ ESC 在最外層直接 return false — QA 誤觸丟進度
  • ❌ F10 / Quit 路徑沒有確認 → 玩家手滑直接結束
  • ❌ Dialog 用 OK/Cancel 兩按鈕但 ESC 預設選 OK → ESC 意義崩壞
  • ❌ Save 失敗時靜默 exit — 玩家不知道存檔沒寫進去
  • ❌ Title 畫面 ESC = exit + 其他畫面 ESC = back — 不一致

When to apply

  • 任何 GUI / TUI / interactive CLI app
  • 有「狀態值得保留」(存檔、編輯中的文件、購物車、未提交的表單)
  • 多層次選單結構(>= 2 層)
  • 寫 SDL2 / SDL3 / pygame / curses / Electron / Qt 的 keyboard event handler
  • 改動 menu navigation 邏輯
  • 加 modal 對話框
  • 設計 wizard 流程的「上一步 / 取消 / 完成」按鍵

When NOT to apply

  • 純一次性 CLI(grepls)— ESC 沒語意
  • Vim 等以 ESC 為模式切換的特殊應用(ESC = 進 normal mode 是它的鐵則之一,跟這條不衝突)
  • 對話 chat 介面(按 Enter 送、按上下換歷史,沒有「結束」概念)
  • 純讀取展示用的 viewer(沒狀態可保留)

Reference case

  • wizardry-1-cht v1.25.3 — 原本 ESC 在 EdgeOfTown 直接 return false,QA agent 兩次誤觸丟進度。 修法:state.quit_dialog_active flag + F10 全域熱鍵 + 三個 Scene::Quit 短路徑改路由到 dialog + draw_quit_dialog() centred modal。
Install via CLI
npx skills add https://github.com/wicanr2/my_skill --skill esc-cancel-f10-quit-autosave
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator