From a5d5d07d3e4ba304243389f932ad4d50c80de577 Mon Sep 17 00:00:00 2001 From: ChangeHow Date: Fri, 31 Jul 2026 00:14:09 +0800 Subject: [PATCH] feat(ui): add session list shortcut --- packages/ui/src/components/ui/HelpDialog.tsx | 5 +++++ packages/ui/src/hooks/useKeyboardShortcuts.ts | 5 +++++ packages/ui/src/lib/i18n/messages/en.settings.ts | 1 + packages/ui/src/lib/i18n/messages/es.settings.ts | 1 + packages/ui/src/lib/i18n/messages/fr.settings.ts | 1 + packages/ui/src/lib/i18n/messages/ja.settings.ts | 1 + packages/ui/src/lib/i18n/messages/ko.settings.ts | 1 + packages/ui/src/lib/i18n/messages/pl.settings.ts | 1 + packages/ui/src/lib/i18n/messages/pt-BR.settings.ts | 1 + packages/ui/src/lib/i18n/messages/uk.settings.ts | 1 + packages/ui/src/lib/i18n/messages/zh-CN.settings.ts | 1 + packages/ui/src/lib/i18n/messages/zh-TW.settings.ts | 1 + packages/ui/src/lib/shortcuts/DOCUMENTATION.md | 2 +- packages/ui/src/lib/shortcuts/config.ts | 7 +++++++ packages/ui/src/lib/shortcuts/schema.test.ts | 3 ++- 15 files changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/components/ui/HelpDialog.tsx b/packages/ui/src/components/ui/HelpDialog.tsx index 363d0615..7ce7df29 100644 --- a/packages/ui/src/components/ui/HelpDialog.tsx +++ b/packages/ui/src/components/ui/HelpDialog.tsx @@ -135,6 +135,11 @@ export const HelpDialog: React.FC = () => { icon: 'git-branch', keys: '', }, + { + id: 'open_session_list', + icon: 'list-unordered', + keys: '', + }, { id: 'focus_input', descriptionKey: "helpDialog.item.focusChatInput", icon: "text", keys: '' }, { id: 'toggle_prompt_navigator', diff --git a/packages/ui/src/hooks/useKeyboardShortcuts.ts b/packages/ui/src/hooks/useKeyboardShortcuts.ts index 97d45bf2..08f87108 100644 --- a/packages/ui/src/hooks/useKeyboardShortcuts.ts +++ b/packages/ui/src/hooks/useKeyboardShortcuts.ts @@ -99,6 +99,11 @@ export const useKeyboardShortcuts = () => { open_timeline_dialog: () => { useUIStore.getState().setTimelineDialogOpen(true); }, + open_session_list: () => { + const state = useUIStore.getState(); + if (state.isMobile) state.setSessionSwitcherOpen(true); + else state.setSessionDropdownOpen(true); + }, toggle_prompt_navigator: () => { const state = useUIStore.getState(); const hasOverlay = state.isSettingsDialogOpen diff --git a/packages/ui/src/lib/i18n/messages/en.settings.ts b/packages/ui/src/lib/i18n/messages/en.settings.ts index 1772694f..3e095b78 100644 --- a/packages/ui/src/lib/i18n/messages/en.settings.ts +++ b/packages/ui/src/lib/i18n/messages/en.settings.ts @@ -1142,6 +1142,7 @@ export const settingsDict = { 'settings.openchamber.keyboardShortcuts.error.exactConflict': 'This combination is already used by {action}.', 'settings.openchamber.keyboardShortcuts.action.open_draft_project_picker.label': 'Open draft project picker', 'settings.openchamber.keyboardShortcuts.action.open_draft_worktree_picker.label': 'Open draft worktree picker', + 'settings.openchamber.keyboardShortcuts.action.open_session_list.label': 'Open session list', 'settings.openchamber.keyboardShortcuts.action.toggle_dictation.label': 'Voice input', 'settings.projects.sidebar.total': 'Total {count}', 'settings.projects.sidebar.actions.addProject': 'Add project', diff --git a/packages/ui/src/lib/i18n/messages/es.settings.ts b/packages/ui/src/lib/i18n/messages/es.settings.ts index 5ee1ebb0..c2fdd90f 100644 --- a/packages/ui/src/lib/i18n/messages/es.settings.ts +++ b/packages/ui/src/lib/i18n/messages/es.settings.ts @@ -1109,6 +1109,7 @@ export const settingsDict = { "settings.openchamber.keyboardShortcuts.error.exactConflict": "Esta combinación ya la usa {action}.", "settings.openchamber.keyboardShortcuts.action.open_draft_project_picker.label": "Abrir selector de proyecto de borrador", "settings.openchamber.keyboardShortcuts.action.open_draft_worktree_picker.label": "Abrir selector de árbol de trabajo de borrador", + "settings.openchamber.keyboardShortcuts.action.open_session_list.label": "Abrir lista de sesiones", "settings.openchamber.keyboardShortcuts.action.toggle_dictation.label": "Entrada de voz", "settings.projects.sidebar.total": "Total {count}", "settings.projects.sidebar.actions.addProject": "Añadir proyecto", diff --git a/packages/ui/src/lib/i18n/messages/fr.settings.ts b/packages/ui/src/lib/i18n/messages/fr.settings.ts index 46e8f21f..f2957df1 100644 --- a/packages/ui/src/lib/i18n/messages/fr.settings.ts +++ b/packages/ui/src/lib/i18n/messages/fr.settings.ts @@ -1030,6 +1030,7 @@ export const settingsDict = { 'settings.openchamber.keyboardShortcuts.error.exactConflict': 'Cette combinaison est déjà utilisée par {action}.', 'settings.openchamber.keyboardShortcuts.action.open_draft_project_picker.label': 'Ouvrir le sélecteur de projet de brouillon', 'settings.openchamber.keyboardShortcuts.action.open_draft_worktree_picker.label': 'Ouvrir le sélecteur de worktree de brouillon', + 'settings.openchamber.keyboardShortcuts.action.open_session_list.label': 'Ouvrir la liste des sessions', 'settings.openchamber.keyboardShortcuts.action.toggle_dictation.label': 'Saisie vocale', 'settings.projects.sidebar.total': 'Total {count}', 'settings.projects.sidebar.actions.addProject': 'Ajouter un projet', diff --git a/packages/ui/src/lib/i18n/messages/ja.settings.ts b/packages/ui/src/lib/i18n/messages/ja.settings.ts index a02f3cc2..e26df24d 100644 --- a/packages/ui/src/lib/i18n/messages/ja.settings.ts +++ b/packages/ui/src/lib/i18n/messages/ja.settings.ts @@ -1142,6 +1142,7 @@ export const settingsDict = { 'settings.openchamber.keyboardShortcuts.error.exactConflict': 'この組み合わせは {action} で使用されています。', 'settings.openchamber.keyboardShortcuts.action.open_draft_project_picker.label': '下書きプロジェクト選択を開く', 'settings.openchamber.keyboardShortcuts.action.open_draft_worktree_picker.label': '下書きワークツリー選択を開く', + 'settings.openchamber.keyboardShortcuts.action.open_session_list.label': 'セッション一覧を開く', 'settings.openchamber.keyboardShortcuts.action.toggle_dictation.label': '音声入力', 'settings.projects.sidebar.total': '合計 {count}', 'settings.projects.sidebar.actions.addProject': 'プロジェクトを追加', diff --git a/packages/ui/src/lib/i18n/messages/ko.settings.ts b/packages/ui/src/lib/i18n/messages/ko.settings.ts index a56696cf..23bc6322 100644 --- a/packages/ui/src/lib/i18n/messages/ko.settings.ts +++ b/packages/ui/src/lib/i18n/messages/ko.settings.ts @@ -1109,6 +1109,7 @@ export const settingsDict = { 'settings.openchamber.keyboardShortcuts.error.exactConflict': '이 조합은 이미 {action}에서 사용합니다.', 'settings.openchamber.keyboardShortcuts.action.open_draft_project_picker.label': '초안 프로젝트 선택기 열기', 'settings.openchamber.keyboardShortcuts.action.open_draft_worktree_picker.label': '초안 워크트리 선택기 열기', + 'settings.openchamber.keyboardShortcuts.action.open_session_list.label': '세션 목록 열기', 'settings.openchamber.keyboardShortcuts.action.toggle_dictation.label': '음성 입력', 'settings.projects.sidebar.total': '총 {count}개', 'settings.projects.sidebar.actions.addProject': '프로젝트 추가', diff --git a/packages/ui/src/lib/i18n/messages/pl.settings.ts b/packages/ui/src/lib/i18n/messages/pl.settings.ts index ee8c127b..45453328 100644 --- a/packages/ui/src/lib/i18n/messages/pl.settings.ts +++ b/packages/ui/src/lib/i18n/messages/pl.settings.ts @@ -1377,6 +1377,7 @@ export const settingsDict = { 'settings.openchamber.keyboardShortcuts.error.exactConflict': 'Ta kombinacja jest już używana przez {action}.', 'settings.openchamber.keyboardShortcuts.action.open_draft_project_picker.label': 'Otwórz wybór projektu szkicu', 'settings.openchamber.keyboardShortcuts.action.open_draft_worktree_picker.label': 'Otwórz wybór worktree szkicu', + 'settings.openchamber.keyboardShortcuts.action.open_session_list.label': 'Otwórz listę sesji', 'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': 'Otwórz oś czasu rozmowy', 'settings.openchamber.keyboardShortcuts.action.toggle_dictation.label': 'Wprowadzanie głosowe', 'settings.projects.sidebar.total': 'Suma: {count}', diff --git a/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts b/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts index d54be549..86b58337 100644 --- a/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts +++ b/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts @@ -1109,6 +1109,7 @@ export const settingsDict = { "settings.openchamber.keyboardShortcuts.error.exactConflict": "Esta combinação já é usada por {action}.", "settings.openchamber.keyboardShortcuts.action.open_draft_project_picker.label": "Abrir seletor de projeto do rascunho", "settings.openchamber.keyboardShortcuts.action.open_draft_worktree_picker.label": "Abrir seletor de worktree do rascunho", + "settings.openchamber.keyboardShortcuts.action.open_session_list.label": "Abrir lista de sessões", "settings.openchamber.keyboardShortcuts.action.toggle_dictation.label": "Entrada por voz", "settings.projects.sidebar.total": "Total {count}", "settings.projects.sidebar.actions.addProject": "Adicionar projeto", diff --git a/packages/ui/src/lib/i18n/messages/uk.settings.ts b/packages/ui/src/lib/i18n/messages/uk.settings.ts index 62ecc7b0..3eb4cf88 100644 --- a/packages/ui/src/lib/i18n/messages/uk.settings.ts +++ b/packages/ui/src/lib/i18n/messages/uk.settings.ts @@ -1109,6 +1109,7 @@ export const settingsDict = { "settings.openchamber.keyboardShortcuts.error.exactConflict": "Цю комбінацію вже використовує {action}.", "settings.openchamber.keyboardShortcuts.action.open_draft_project_picker.label": "Відкрити вибір проєкту чернетки", "settings.openchamber.keyboardShortcuts.action.open_draft_worktree_picker.label": "Відкрити вибір worktree чернетки", + "settings.openchamber.keyboardShortcuts.action.open_session_list.label": "Відкрити список сесій", "settings.openchamber.keyboardShortcuts.action.toggle_dictation.label": "Голосове введення", "settings.projects.sidebar.total": "Усього {count}", "settings.projects.sidebar.actions.addProject": "Додати проєкт", diff --git a/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts b/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts index ae6cc3b1..5048c4a9 100644 --- a/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts +++ b/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts @@ -1109,6 +1109,7 @@ export const settingsDict = { 'settings.openchamber.keyboardShortcuts.error.exactConflict': '此组合已被 {action} 使用。', 'settings.openchamber.keyboardShortcuts.action.open_draft_project_picker.label': '打开草稿项目选择器', 'settings.openchamber.keyboardShortcuts.action.open_draft_worktree_picker.label': '打开草稿工作树选择器', + 'settings.openchamber.keyboardShortcuts.action.open_session_list.label': '打开会话列表', 'settings.openchamber.keyboardShortcuts.action.toggle_dictation.label': '语音输入', 'settings.projects.sidebar.total': '总计 {count}', 'settings.projects.sidebar.actions.addProject': '添加项目', diff --git a/packages/ui/src/lib/i18n/messages/zh-TW.settings.ts b/packages/ui/src/lib/i18n/messages/zh-TW.settings.ts index a5b3d6ad..90e9e345 100644 --- a/packages/ui/src/lib/i18n/messages/zh-TW.settings.ts +++ b/packages/ui/src/lib/i18n/messages/zh-TW.settings.ts @@ -1016,6 +1016,7 @@ 'settings.openchamber.keyboardShortcuts.error.exactConflict': '此組合已由 {action} 使用。', 'settings.openchamber.keyboardShortcuts.action.open_draft_project_picker.label': '開啟草稿專案選擇器', 'settings.openchamber.keyboardShortcuts.action.open_draft_worktree_picker.label': '開啟草稿 worktree 選擇器', + 'settings.openchamber.keyboardShortcuts.action.open_session_list.label': '開啟工作階段列表', 'settings.openchamber.keyboardShortcuts.action.toggle_dictation.label': '語音輸入', 'settings.projects.sidebar.total': '總計 {count}', 'settings.projects.sidebar.actions.addProject': '新增專案', diff --git a/packages/ui/src/lib/shortcuts/DOCUMENTATION.md b/packages/ui/src/lib/shortcuts/DOCUMENTATION.md index bf5d6582..a44150c4 100644 --- a/packages/ui/src/lib/shortcuts/DOCUMENTATION.md +++ b/packages/ui/src/lib/shortcuts/DOCUMENTATION.md @@ -27,7 +27,7 @@ Component interaction keys that are not application commands, such as list navig Bindings remain persisted as `Record`. Each binding has one chord or at most two space-separated chords, such as `mod+s p`. `normalizeCombo`, `parseShortcut`, `formatShortcutForDisplay`, and `getShortcutConflict` provide the shared parsing and validation behavior. A single chord conflicts with a sequence sharing its first chord; sibling sequences are valid. -Contextual internal commands may deliberately share a sequence leader. The single-chord handler gets the first chance to handle the event; returning `false` lets the dispatcher start the sequence. The active file editor therefore owns `mod+s` for saving, while a mounted but unfocused editor yields `mod+s p` and `mod+s g` to the draft target pickers. +Contextual internal commands may deliberately share a sequence leader. The single-chord handler gets the first chance to handle the event; returning `false` lets the dispatcher start the sequence. The active file editor therefore owns `mod+s` for saving, while a mounted but unfocused editor yields `mod+s p`, `mod+s g`, and `mod+s l` to the draft target pickers and session list. Runtime-specific commands may also share an exact binding when their handlers are mutually exclusive. `open_diff_panel` handles `mod+2` on desktop, while `switch_tab_2` handles it on mobile; each returns `false` outside its runtime so the dispatcher can try the next registered action. diff --git a/packages/ui/src/lib/shortcuts/config.ts b/packages/ui/src/lib/shortcuts/config.ts index 810d5a4e..5c657a0c 100644 --- a/packages/ui/src/lib/shortcuts/config.ts +++ b/packages/ui/src/lib/shortcuts/config.ts @@ -55,6 +55,13 @@ const SHORTCUT_GROUPS = { settingsLabelKey: 'settings.openchamber.keyboardShortcuts.action.open_draft_worktree_picker.label', }, + { + id: 'open_session_list', + defaultBinding: 'mod+s l', + customizable: true, + settingsLabelKey: + 'settings.openchamber.keyboardShortcuts.action.open_session_list.label', + }, { id: 'new_chat_worktree', defaultBinding: 'mod+shift+n', diff --git a/packages/ui/src/lib/shortcuts/schema.test.ts b/packages/ui/src/lib/shortcuts/schema.test.ts index 08f75353..463b92a0 100644 --- a/packages/ui/src/lib/shortcuts/schema.test.ts +++ b/packages/ui/src/lib/shortcuts/schema.test.ts @@ -48,9 +48,10 @@ describe('shortcut schema', () => { ))).toBe(true); }); - test('includes draft prefix bindings and session metadata', () => { + test('includes session prefix bindings and metadata', () => { expect(getShortcutAction('open_draft_project_picker')?.defaultBinding).toBe('mod+s p'); expect(getShortcutAction('open_draft_worktree_picker')?.defaultBinding).toBe('mod+s g'); + expect(getShortcutAction('open_session_list')?.defaultBinding).toBe('mod+s l'); expect(getShortcutAction('focus_input')?.category).toBe('session'); });