feat(ui): redesign the default shortcut layout around a mod+k leader

Single chords stay for everyday actions; open/go actions move to two-step
mod+k sequences; held mod+digit switches header session tabs and held
mod+alt+digit switches context panel surfaces. Rare actions leave the
shortcut schema for the command palette, every remaining action ships with
a default binding, and stored overrides from the old layout reset once.
Key matching now follows the physical key on non-Latin layouts and for
Option-modified digits on macOS, including in the recording dialog.
This commit is contained in:
Bohdan Triapitsyn
2026-08-26 14:52:31 +03:00
parent 76f977e580
commit 7977842e1e
44 changed files with 389 additions and 322 deletions
+7 -5
View File
@@ -20,7 +20,6 @@ import { useAgentMemorySync } from '@/hooks/useAgentMemorySync';
import { usePwaInstallPrompt } from '@/hooks/usePwaInstallPrompt';
import { useWindowTitle } from '@/hooks/useWindowTitle';
import { useConfigStore } from '@/stores/useConfigStore';
import { useKeybind } from '@/hooks/useKeybind';
import { isDesktopLocalOriginActive, isDesktopShell, restartDesktopApp, invokeDesktop } from '@/lib/desktop';
import {
getInjectedBootOutcome,
@@ -723,10 +722,13 @@ function App({ apis }: AppProps) {
useSessionStatusBootstrap({ enabled: embeddedBackgroundWorkEnabled });
useKeybind('toggle_memory_debug', () => {
if (embeddedSessionChat) return false;
setShowMemoryDebug((previous) => !previous);
});
// Palette-only action: the memory debug panel has no keyboard shortcut.
React.useEffect(() => {
if (embeddedSessionChat) return;
const handleToggle = () => setShowMemoryDebug((previous) => !previous);
window.addEventListener('openchamber:memory-debug-toggle', handleToggle);
return () => window.removeEventListener('openchamber:memory-debug-toggle', handleToggle);
}, [embeddedSessionChat]);
React.useEffect(() => {
if (embeddedSessionChat) {
@@ -1471,17 +1471,6 @@ export const Header: React.FC = () => {
setIsDesktopServicesOpen(true);
void refreshCurrentInstanceLabel();
},
// The desktop menu holds one destination now, so this shortcut opens it
// rather than cycling. The binding is kept: it is user-configurable and
// silently dropping it would break existing setups.
cycle_services_tab: () => {
if (servicesTabs.length === 0) return false;
setIsDesktopServicesOpen(true);
void refreshCurrentInstanceLabel();
},
toggle_context_plan: () => {
handleOpenContextPlan();
},
});
const desktopSidebarActions = (
@@ -53,17 +53,19 @@ export const KeyboardShortcutsSettings: React.FC = () => {
persist(nextOverrides);
};
const shortcutDisplay = (action: CustomizableShortcutAction): string => {
const isSurfaceSwitch = action.id === 'switch_context_surface';
const combo = isSurfaceSwitch
const isPrefixStyle = 'prefixStyle' in action && action.prefixStyle;
const combo = isPrefixStyle
? getEffectiveShortcutPrefix(action.id, shortcutOverrides)
: getEffectiveShortcutCombo(action.id, shortcutOverrides);
const formatted = formatShortcutForDisplay(
combo,
t('settings.openchamber.keyboardShortcuts.unassigned'),
);
return isSurfaceSwitch && combo && combo !== UNASSIGNED_SHORTCUT
? `${formatted}${t('settings.openchamber.keyboardShortcuts.action.switch_context_surface.suffix')}`
: formatted;
if (!isPrefixStyle || !combo || combo === UNASSIGNED_SHORTCUT) return formatted;
const suffix = action.id === 'switch_session_tab'
? t('settings.openchamber.keyboardShortcuts.action.switch_session_tab.suffix')
: t('settings.openchamber.keyboardShortcuts.action.switch_context_surface.suffix');
return `${formatted}${suffix}`;
};
return (
@@ -104,15 +106,17 @@ export const KeyboardShortcutsSettings: React.FC = () => {
>
{t('settings.openchamber.keyboardShortcuts.actions.edit')}
</Button>
<Button
type="button"
variant="ghost"
size="xs"
className="!font-normal"
onClick={() => resetOne(action.id)}
>
{t('settings.common.actions.reset')}
</Button>
{action.id in shortcutOverrides ? (
<Button
type="button"
variant="ghost"
size="xs"
className="!font-normal"
onClick={() => resetOne(action.id)}
>
{t('settings.common.actions.reset')}
</Button>
) : null}
</SettingsFieldRow>
))}
</div>
@@ -4,7 +4,8 @@ import { settleShortcutRecordingState, updateShortcutRecordingState } from './Sh
const emptyState = { chords: [], livePreview: null, settled: false };
function keyEvent(key: string, modifiers: Partial<Record<'altKey' | 'ctrlKey' | 'metaKey' | 'shiftKey', boolean>> = {}) {
return { key, repeat: false, isComposing: false, altKey: false, ctrlKey: false, metaKey: false, shiftKey: false, ...modifiers };
const code = /^[a-z]$/i.test(key) ? `Key${key.toUpperCase()}` : /^[0-9]$/.test(key) ? `Digit${key}` : key;
return { key, code, repeat: false, isComposing: false, altKey: false, ctrlKey: false, metaKey: false, shiftKey: false, ...modifiers };
}
describe('ShortcutRecordingDialog recording state', () => {
@@ -13,6 +13,7 @@ import {
getShortcutBindingConflicts,
isRiskyBrowserShortcut,
keyToShortcutToken,
resolveShortcutEventKey,
normalizeCombo,
type ShortcutActionId,
type ShortcutBindingConflict,
@@ -27,6 +28,7 @@ const SECOND_CHORD_TIMEOUT_MS = 3000;
interface RecordingKeyboardEvent {
altKey: boolean;
code: string;
ctrlKey: boolean;
isComposing: boolean;
key: string;
@@ -84,7 +86,7 @@ function keyboardEventToCombo(event: RecordingKeyboardEvent): ShortcutCombo | nu
if (MODIFIER_KEYS.has(event.key.toLowerCase())) return null;
if (getPhysicalKeyCount(event, true) > MAX_SHORTCUT_KEY_COUNT) return null;
const key = keyToShortcutToken(event.key);
const key = keyToShortcutToken(resolveShortcutEventKey(event));
if (!key) return null;
const parts: string[] = [];
@@ -200,7 +202,8 @@ export const ShortcutRecordingDialog: React.FC<ShortcutRecordingDialogProps> = (
event.preventDefault();
event.stopPropagation();
if (phase === 'keyup' && action?.id === 'switch_context_surface' && recording.chords.length === 0) {
const isPrefixStyleAction = Boolean(action && 'prefixStyle' in action && action.prefixStyle);
if (phase === 'keyup' && isPrefixStyleAction && recording.chords.length === 0) {
const modifierCombo = modifierKeyUpToCombo(event);
if (modifierCombo) {
setRecording({ chords: [modifierCombo], livePreview: null, settled: true });
@@ -209,6 +212,7 @@ export const ShortcutRecordingDialog: React.FC<ShortcutRecordingDialogProps> = (
}
const nextRecording = updateShortcutRecordingState(recording, {
altKey: event.altKey,
code: event.nativeEvent.code,
ctrlKey: event.ctrlKey,
isComposing: event.nativeEvent.isComposing,
key: event.key,
@@ -216,7 +220,7 @@ export const ShortcutRecordingDialog: React.FC<ShortcutRecordingDialogProps> = (
repeat: event.repeat,
shiftKey: event.shiftKey,
}, phase);
setRecording(action?.id === 'switch_context_surface' && nextRecording.chords.length > 1
setRecording(isPrefixStyleAction && nextRecording.chords.length > 1
? recording
: nextRecording);
};
@@ -26,6 +26,21 @@ export const useSessionSearchEffects = ({
return () => window.cancelAnimationFrame(raf);
}, [enabled, isSessionSearchOpen, sessionSearchInputRef]);
// The open_session_list shortcut lands here when the sidebar is visible:
// the session list is already on screen, so the shortcut opens its search.
React.useEffect(() => {
if (!enabled || typeof window === 'undefined') {
return;
}
const handleOpenRequest = () => {
setIsSessionSearchOpen(true);
sessionSearchInputRef.current?.focus();
sessionSearchInputRef.current?.select();
};
window.addEventListener('openchamber:sidebar-session-search', handleOpenRequest);
return () => window.removeEventListener('openchamber:sidebar-session-search', handleOpenRequest);
}, [enabled, setIsSessionSearchOpen, sessionSearchInputRef]);
React.useEffect(() => {
if (!enabled || !isSessionSearchOpen || typeof document === 'undefined') {
return;
@@ -37,7 +37,8 @@ import { toast } from '@/components/ui';
import { FileTypeIcon } from '@/components/icons/FileTypeIcon';
import type { Session } from '@opencode-ai/sdk/v2';
import { createWorktreeSession } from '@/lib/worktreeSessionCreator';
import { formatShortcutForDisplay, getEffectiveShortcutCombo } from '@/lib/shortcuts';
import { formatShortcutForDisplay, getEffectiveShortcutCombo, shortcutRegistry } from '@/lib/shortcuts';
import { showOpenCodeStatus } from '@/lib/openCodeStatus';
import { canUseElectronDesktopIPC, invokeDesktop, isDesktopShell, isVSCodeRuntime, isWebRuntime } from '@/lib/desktop';
import { SETTINGS_PAGE_METADATA, type SettingsRuntimeContext } from '@/lib/settings/metadata';
@@ -230,6 +231,25 @@ export const CommandPalette: React.FC = () => {
if (currentDirectory) openContextOverview(currentDirectory);
}),
},
{
id: 'cycle-theme',
title: t('commandPalette.item.cycleTheme'),
icon: <Icon name="palette" className="mr-2 h-4 w-4" />,
shortcutId: 'cycle_theme',
searchText: t('commandPalette.item.cycleTheme'),
onSelect: run(() => {
shortcutRegistry.invoke('cycle_theme');
}),
},
{
id: 'open-status',
title: t('commandPalette.item.showOpenCodeStatus'),
icon: <Icon name="pulse" className="mr-2 h-4 w-4" />,
searchText: t('commandPalette.item.showOpenCodeStatus'),
onSelect: run(() => {
void showOpenCodeStatus();
}),
},
{
id: 'open-settings',
title: t('commandPalette.item.openSettings'),
@@ -239,6 +259,15 @@ export const CommandPalette: React.FC = () => {
onSelect: run(() => setSettingsDialogOpen(true)),
},
];
list.push({
id: 'toggle-memory-debug',
title: t('commandPalette.item.toggleMemoryDebug'),
icon: <Icon name="bug" className="mr-2 h-4 w-4" />,
searchText: t('commandPalette.item.toggleMemoryDebug'),
onSelect: run(() => {
window.dispatchEvent(new CustomEvent('openchamber:memory-debug-toggle'));
}),
});
if (canUseElectronDesktopIPC()) {
list.splice(1, 0, {
id: 'new-mini-chat',
+13 -31
View File
@@ -10,6 +10,7 @@ import { Icon } from "@/components/icon/Icon";
import { useUIStore } from "@/stores/useUIStore";
import {
getEffectiveShortcutCombo,
getEffectiveShortcutPrefix,
getShortcutAction,
formatShortcutForDisplay,
type ShortcutActionId,
@@ -156,24 +157,6 @@ export const HelpDialog: React.FC = () => {
{
categoryKey: "helpDialog.section.panels",
items: [
{
id: 'toggle_right_sidebar',
descriptionKey: 'helpDialog.item.toggleRightSidebar',
icon: "layout-right",
keys: '',
},
{
id: 'open_right_sidebar_git',
descriptionKey: 'helpDialog.item.openRightSidebarGitTab',
icon: "git-branch",
keys: '',
},
{
id: 'open_right_sidebar_files',
descriptionKey: 'helpDialog.item.openRightSidebarFilesTab',
icon: "layout-right",
keys: '',
},
{
id: 'toggle_terminal',
descriptionKey: 'helpDialog.item.toggleTerminalDock',
@@ -187,14 +170,13 @@ export const HelpDialog: React.FC = () => {
keys: '',
},
{
id: 'toggle_context_plan',
descriptionKey: 'helpDialog.item.togglePlanContextPanel',
icon: "time",
keys: '',
keys: [`${formatShortcutForDisplay(getEffectiveShortcutPrefix('switch_context_surface', shortcutOverrides))} + 1...0`],
descriptionKey: "helpDialog.item.switchContextSurface",
icon: "layout-right",
},
{
keys: [`${formatShortcutForDisplay('mod')} + 1...0`],
descriptionKey: "helpDialog.item.switchContextSurface",
keys: [`${formatShortcutForDisplay(getEffectiveShortcutPrefix('switch_session_tab', shortcutOverrides))} + 1...9`],
descriptionKey: "helpDialog.item.switchSessionTab",
icon: "layout-right",
},
],
@@ -214,12 +196,6 @@ export const HelpDialog: React.FC = () => {
icon: "stack",
keys: '',
},
{
id: 'cycle_services_tab',
descriptionKey: 'helpDialog.item.cycleServicesTab',
icon: "stack",
keys: '',
},
{
id: 'open_settings',
descriptionKey: "helpDialog.item.openSettings",
@@ -258,6 +234,12 @@ export const HelpDialog: React.FC = () => {
const descriptionKey = shortcut.descriptionKey
?? (action?.customizable ? action.settingsLabelKey : undefined);
if (!descriptionKey) return null;
// This dialog lists what the keyboard can do right now;
// an action without a binding belongs to the command
// palette and Settings, not here.
if (shortcut.id && !getEffectiveShortcutCombo(shortcut.id, shortcutOverrides)) {
return null;
}
const displayKeys = shortcut.id
? renderShortcut(
shortcut.id,
@@ -320,7 +302,7 @@ export const HelpDialog: React.FC = () => {
{t('helpDialog.proTips.recentSessions')}
</li>
<li>
{t('helpDialog.proTips.themeCycling')}
{t('helpDialog.proTips.leaderSequences')}
</li>
</ul>
</div>
@@ -2,16 +2,18 @@ import type React from 'react';
import { isIMECompositionEvent } from '@/lib/ime';
function getDropdownNavigationKey(event: Pick<KeyboardEvent, 'key' | 'ctrlKey' | 'metaKey' | 'altKey' | 'shiftKey'>): 'ArrowDown' | 'ArrowUp' | null {
function getDropdownNavigationKey(event: Pick<KeyboardEvent, 'key' | 'code' | 'ctrlKey' | 'metaKey' | 'altKey' | 'shiftKey'>): 'ArrowDown' | 'ArrowUp' | null {
if (!event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) return null;
if (event.key.toLowerCase() === 'n') return 'ArrowDown';
if (event.key.toLowerCase() === 'p') return 'ArrowUp';
// `code` covers non-Latin layouts, where `key` is the layout's own letter.
if (event.key.toLowerCase() === 'n' || event.code === 'KeyN') return 'ArrowDown';
if (event.key.toLowerCase() === 'p' || event.code === 'KeyP') return 'ArrowUp';
return null;
}
type DropdownNavigationEvent = Pick<
React.KeyboardEvent<HTMLElement>,
| 'altKey'
| 'code'
| 'ctrlKey'
| 'defaultPrevented'
| 'isPropagationStopped'
+37 -28
View File
@@ -1,7 +1,7 @@
import React from 'react';
import { isTerminalEventTarget } from '@/lib/terminalFocus';
import { useSessionUIStore } from '@/sync/session-ui-store';
import { closeSessionTabAndActivateNeighbour } from '@/lib/sessionTabs';
import { activateSessionTabByIndex, closeSessionTabAndActivateNeighbour } from '@/lib/sessionTabs';
import { useSelectionStore } from '@/sync/selection-store';
import * as sessionActions from '@/sync/session-actions';
import { normalizeContextPanelDirectoryKey, useUIStore } from '@/stores/useUIStore';
@@ -11,13 +11,14 @@ import { useKeybinds } from '@/hooks/useKeybind';
import { createWorktreeSession } from '@/lib/worktreeSessionCreator';
import { useConfigStore } from '@/stores/useConfigStore';
import { canUseElectronDesktopIPC, invokeDesktop, isVSCodeRuntime } from '@/lib/desktop';
import { showOpenCodeStatus } from '@/lib/openCodeStatus';
import {
eventMatchesShortcut,
eventMatchesShortcutPrefix,
getEffectiveShortcutCombo,
getEffectiveShortcutPrefix,
normalizeCombo,
resolveShortcutEventDigit,
resolveShortcutEventKey,
ShortcutDispatcher,
shortcutRegistry,
type ShortcutActionId,
@@ -123,8 +124,18 @@ export const useKeyboardShortcuts = () => {
},
open_session_list: () => {
const state = useUIStore.getState();
if (state.isMobile) state.setSessionSwitcherOpen(true);
else state.setSessionDropdownOpen(true);
if (state.isMobile) {
state.setSessionSwitcherOpen(true);
return;
}
// The switcher dropdown only mounts while the sidebar is collapsed;
// with the sidebar visible the list is already on screen, so the
// shortcut opens the sidebar's session search instead.
if (state.isSidebarOpen) {
window.dispatchEvent(new CustomEvent('openchamber:sidebar-session-search'));
return;
}
state.setSessionDropdownOpen(true);
},
toggle_prompt_navigator: () => {
const state = useUIStore.getState();
@@ -146,9 +157,6 @@ export const useKeyboardShortcuts = () => {
}
state.togglePromptNavigatorPanel();
},
open_status: () => {
void showOpenCodeStatus();
},
open_help: () => {
useUIStore.getState().toggleHelpDialog();
},
@@ -230,25 +238,6 @@ export const useKeyboardShortcuts = () => {
useSelectionStore.getState().saveSessionAgentSelection(sessionId, next);
}
},
toggle_right_sidebar: () => {
const state = useUIStore.getState();
if (state.isMobile || !currentDirectory) return false;
const directory = normalizeContextPanelDirectoryKey(currentDirectory);
const panel = state.contextPanelByDirectory[directory];
if (panel?.isOpen) state.closeContextPanel(directory);
else if (panel?.activeTabId) state.setActiveContextPanelTab(directory, panel.activeTabId);
else state.openContextSurface(directory, 'git');
},
open_right_sidebar_git: () => {
const state = useUIStore.getState();
if (state.isMobile || !currentDirectory) return false;
state.openContextSurface(normalizeContextPanelDirectoryKey(currentDirectory), 'git');
},
open_right_sidebar_files: () => {
const state = useUIStore.getState();
if (state.isMobile || !currentDirectory) return false;
state.openContextSurface(normalizeContextPanelDirectoryKey(currentDirectory), 'file');
},
toggle_terminal: () => {
if (useUIStore.getState().isMobile) return false;
return toggleTerminalSurface();
@@ -482,8 +471,9 @@ export const useKeyboardShortcuts = () => {
return;
}
const switchSurfaceDigit = event.key.length === 1 && event.key >= '0' && event.key <= '9'
? (event.key === '0' ? 10 : Number(event.key))
const rawDigit = resolveShortcutEventDigit(event);
const switchSurfaceDigit = rawDigit !== null
? (rawDigit === '0' ? 10 : Number(rawDigit))
: null;
const switchSurfacePrefix = getEffectiveShortcutPrefix(
'switch_context_surface',
@@ -514,13 +504,32 @@ export const useKeyboardShortcuts = () => {
}
}
const sessionTabDigit = rawDigit !== null && rawDigit !== '0' ? Number(rawDigit) : null;
if (
sessionTabDigit !== null
&& !event.repeat
&& !isVSCodeRuntime()
&& useUIStore.getState().sessionTabsEnabled
&& eventMatchesShortcutPrefix(
event,
getEffectiveShortcutPrefix('switch_session_tab', useUIStore.getState().shortcutOverrides),
heldKeysRef.current,
)
&& activateSessionTabByIndex(sessionTabDigit - 1)
) {
event.preventDefault();
return;
}
if (dispatcher.dispatch(event)) event.preventDefault();
};
const handleKeyHoldDown = (event: KeyboardEvent) => {
heldKeysRef.current.add(event.key.toLowerCase());
heldKeysRef.current.add(resolveShortcutEventKey(event).toLowerCase());
};
const handleKeyUp = (event: KeyboardEvent) => {
heldKeysRef.current.delete(event.key.toLowerCase());
heldKeysRef.current.delete(resolveShortcutEventKey(event).toLowerCase());
};
const handleBlur = () => {
heldKeysRef.current.clear();
@@ -1080,9 +1080,8 @@ export const settingsDict = {
'settings.openchamber.keyboardShortcuts.action.toggle_terminal_expanded.label': 'Terminal erweitert umschalten',
'settings.openchamber.keyboardShortcuts.action.add_selection_to_chat.label': 'Auswahl zum Chat hinzufügen',
'settings.openchamber.keyboardShortcuts.action.toggle_sidebar.label': 'Seitenleiste umschalten',
'settings.openchamber.keyboardShortcuts.action.toggle_right_sidebar.label': 'Rechte Seitenleiste umschalten',
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_git.label': 'Git-Tab der rechten Seitenleiste öffnen',
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_files.label': 'Datei-Tab der rechten Seitenleiste öffnen',
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.label': 'Sitzungs-Tab wechseln',
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.suffix': ' + 1…9',
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.label': 'Kontextpanel-Oberfläche wechseln',
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.suffix': ' + 1…0',
'settings.openchamber.keyboardShortcuts.action.new_chat.label': 'Neue Sitzung',
@@ -1090,9 +1089,7 @@ export const settingsDict = {
'settings.openchamber.keyboardShortcuts.action.new_chat_worktree.label': 'Neuer Worktree-Entwurf',
'settings.openchamber.keyboardShortcuts.action.new_mini_chat.label': 'Neues Mini-Chat-Fenster',
'settings.openchamber.keyboardShortcuts.action.open_help.label': 'Tastenkürzel öffnen',
'settings.openchamber.keyboardShortcuts.action.toggle_context_plan.label': 'Plan-Kontextpanel umschalten',
'settings.openchamber.keyboardShortcuts.action.toggle_services_menu.label': 'Dienstemenü umschalten',
'settings.openchamber.keyboardShortcuts.action.cycle_services_tab.label': 'Dienste-Tab durchschalten',
'settings.openchamber.keyboardShortcuts.action.cycle_theme.label': 'Thema wechseln',
'settings.openchamber.keyboardShortcuts.action.cycle_agent.label': 'Agent wechseln',
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label': 'Favorites Modell vorwärts durchschalten',
+5 -6
View File
@@ -1684,22 +1684,18 @@ export const dict = {
'helpDialog.item.focusChatInput': 'Chat-Eingabe fokussieren',
'helpDialog.item.togglePromptNavigator': 'Aufforderungs-Navigator umschalten',
'helpDialog.item.abortActiveRun': 'Aktuelle Ausführung abbrechen (Doppeltaste)',
'helpDialog.item.toggleRightSidebar': 'Rechte Seitenleiste umschalten',
'helpDialog.item.openRightSidebarGitTab': 'Git-Registerkarte der rechten Seitenleiste öffnen',
'helpDialog.item.openRightSidebarFilesTab': 'Datei-Registerkarte der rechten Seitenleiste öffnen',
'helpDialog.item.toggleTerminalDock': 'Terminal-Dock umschalten',
'helpDialog.item.toggleTerminalExpanded': 'Terminal erweitert umschalten',
'helpDialog.item.togglePlanContextPanel': 'Plan-Kontext-Panel umschalten',
'helpDialog.item.cycleTheme': 'Thema wechseln (Hell → Dunkel → System)',
'helpDialog.item.switchSessionTab': 'Sitzungs-Tab wechseln',
'helpDialog.item.switchContextSurface': 'Kontextpanel-Oberfläche wechseln (Zahlentaste)',
'helpDialog.item.toggleServicesMenu': 'Dienstemenü umschalten',
'helpDialog.item.cycleServicesTab': 'Dienste-Registerkarte durchgehen',
'helpDialog.item.openSettings': 'Einstellungen öffnen',
'helpDialog.keyCombiner.or': 'oder',
'helpDialog.proTips.title': 'Pro-Tipps:',
'helpDialog.proTips.commandPalette': 'Verwenden Sie die Befehlspalette ({shortcut}), um schnell auf alle Aktionen zuzugreifen',
'helpDialog.proTips.recentSessions': 'Die 5 zuletzt verwendeten Sitzungen erscheinen in der Befehlspalette',
'helpDialog.proTips.themeCycling': 'Themenwechsel merken sich Ihre Einstellung über Sitzungen hinweg',
'helpDialog.proTips.leaderSequences': 'Zweistufige Kürzel: erst die Kombination, dann die zweite Taste — Esc bricht ab',
'header.actions.rightSidebarWithShortcut': 'Rechte Seitenleiste ({shortcut})',
'header.actions.toggleRightSidebarAria': 'Rechte Seitenleiste umschalten',
'header.actions.openAppMenu': 'OpenChamber-Menü',
@@ -2293,6 +2289,9 @@ export const dict = {
'commandPalette.item.toggleSidebar': 'Seitenleiste umschalten',
'commandPalette.item.showContextUsage': 'Kontextnutzung anzeigen',
'commandPalette.item.toggleTerminal': 'Terminal umschalten',
'commandPalette.item.cycleTheme': 'Thema wechseln',
'commandPalette.item.showOpenCodeStatus': 'OpenCode-Status anzeigen',
'commandPalette.item.toggleMemoryDebug': 'Memory-Debug-Panel umschalten',
'commandPalette.item.openSettings': 'Einstellungen öffnen...',
'commandPalette.session.untitled': 'Unbenannte Sitzung',
'openCodeStatusDialog.title': 'OpenCode-Status',
@@ -1142,9 +1142,8 @@ export const settingsDict = {
'settings.openchamber.keyboardShortcuts.action.toggle_terminal_expanded.label': 'Toggle terminal expanded',
'settings.openchamber.keyboardShortcuts.action.add_selection_to_chat.label': 'Add selection to chat',
'settings.openchamber.keyboardShortcuts.action.toggle_sidebar.label': 'Toggle sidebar',
'settings.openchamber.keyboardShortcuts.action.toggle_right_sidebar.label': 'Toggle context panel',
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_git.label': 'Open Git surface',
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_files.label': 'Open Files surface',
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.label': 'Switch session tab',
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.suffix': ' + 1…9',
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.label': 'Switch context panel surface',
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.suffix': ' + 1…0',
'settings.openchamber.keyboardShortcuts.action.new_chat.label': 'New session',
@@ -1152,9 +1151,7 @@ export const settingsDict = {
'settings.openchamber.keyboardShortcuts.action.new_chat_worktree.label': 'New worktree draft',
'settings.openchamber.keyboardShortcuts.action.new_mini_chat.label': 'New Mini Chat window',
'settings.openchamber.keyboardShortcuts.action.open_help.label': 'Open keyboard shortcuts',
'settings.openchamber.keyboardShortcuts.action.toggle_context_plan.label': 'Toggle plan context panel',
'settings.openchamber.keyboardShortcuts.action.toggle_services_menu.label': 'Toggle services menu',
'settings.openchamber.keyboardShortcuts.action.cycle_services_tab.label': 'Cycle services tab',
'settings.openchamber.keyboardShortcuts.action.cycle_theme.label': 'Cycle theme',
'settings.openchamber.keyboardShortcuts.action.cycle_agent.label': 'Cycle agent',
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label': 'Cycle favorite model forward',
+5 -6
View File
@@ -1858,22 +1858,18 @@ export const dict = {
'helpDialog.item.focusChatInput': 'Focus Chat Input',
'helpDialog.item.togglePromptNavigator': 'Toggle Prompt Navigator',
'helpDialog.item.abortActiveRun': 'Abort active run (double press)',
'helpDialog.item.toggleRightSidebar': 'Toggle context panel',
'helpDialog.item.openRightSidebarGitTab': 'Open Git surface',
'helpDialog.item.openRightSidebarFilesTab': 'Open Files surface',
'helpDialog.item.toggleTerminalDock': 'Toggle Terminal Dock',
'helpDialog.item.toggleTerminalExpanded': 'Toggle Terminal Expanded',
'helpDialog.item.togglePlanContextPanel': 'Toggle Plan Context Panel',
'helpDialog.item.switchSessionTab': 'Switch Session Tab',
'helpDialog.item.switchContextSurface': 'Switch Context Panel Surface (number key)',
'helpDialog.item.cycleTheme': 'Cycle Theme (Light → Dark → System)',
'helpDialog.item.toggleServicesMenu': 'Toggle Services Menu',
'helpDialog.item.cycleServicesTab': 'Cycle Services Tab',
'helpDialog.item.openSettings': 'Open Settings',
'helpDialog.keyCombiner.or': 'or',
'helpDialog.proTips.title': 'Pro Tips:',
'helpDialog.proTips.commandPalette': 'Use Command Palette ({shortcut}) to quickly access all actions',
'helpDialog.proTips.recentSessions': 'The 5 most recent sessions appear in the Command Palette',
'helpDialog.proTips.themeCycling': 'Theme cycling remembers your preference across sessions',
'helpDialog.proTips.leaderSequences': 'Two-step shortcuts: press the first combo, then the second key — Esc cancels',
'header.actions.rightSidebarWithShortcut': 'Right sidebar ({shortcut})',
'header.actions.toggleRightSidebarAria': 'Toggle right sidebar',
'header.actions.openAppMenu': 'OpenChamber menu',
@@ -2483,6 +2479,9 @@ export const dict = {
'commandPalette.item.toggleSidebar': 'Toggle Sidebar',
'commandPalette.item.showContextUsage': 'Show Context Usage',
'commandPalette.item.toggleTerminal': 'Toggle Terminal',
'commandPalette.item.cycleTheme': 'Cycle theme',
'commandPalette.item.showOpenCodeStatus': 'Show OpenCode status',
'commandPalette.item.toggleMemoryDebug': 'Toggle memory debug panel',
'commandPalette.item.openSettings': 'Open Settings...',
'commandPalette.session.untitled': 'Untitled Session',
'openCodeStatusDialog.title': 'OpenCode Status',
@@ -1110,9 +1110,8 @@ export const settingsDict = {
"settings.openchamber.keyboardShortcuts.action.toggle_terminal_expanded.label": "Expandir o contraer terminal",
"settings.openchamber.keyboardShortcuts.action.add_selection_to_chat.label": "Agregar selección al chat",
"settings.openchamber.keyboardShortcuts.action.toggle_sidebar.label": "Mostrar u ocultar barra lateral",
"settings.openchamber.keyboardShortcuts.action.toggle_right_sidebar.label": 'Alternar panel de contexto',
"settings.openchamber.keyboardShortcuts.action.open_right_sidebar_git.label": 'Abrir superficie de Git',
"settings.openchamber.keyboardShortcuts.action.open_right_sidebar_files.label": 'Abrir superficie de archivos',
"settings.openchamber.keyboardShortcuts.action.switch_session_tab.label": "Cambiar pestaña de sesión",
"settings.openchamber.keyboardShortcuts.action.switch_session_tab.suffix": " + 1…9",
"settings.openchamber.keyboardShortcuts.action.switch_context_surface.label": "Cambiar superficie del panel de contexto",
"settings.openchamber.keyboardShortcuts.action.switch_context_surface.suffix": " + 1…0",
"settings.openchamber.keyboardShortcuts.action.new_chat.label": "Nueva sesión",
@@ -1120,9 +1119,7 @@ export const settingsDict = {
"settings.openchamber.keyboardShortcuts.action.new_chat_worktree.label": "Nuevo borrador de worktree",
"settings.openchamber.keyboardShortcuts.action.new_mini_chat.label": "Nueva ventana Mini Chat",
"settings.openchamber.keyboardShortcuts.action.open_help.label": "Abrir atajos de teclado",
"settings.openchamber.keyboardShortcuts.action.toggle_context_plan.label": "Alternar panel de plan de contexto",
"settings.openchamber.keyboardShortcuts.action.toggle_services_menu.label": "Mostrar u ocultar menú de servicios",
"settings.openchamber.keyboardShortcuts.action.cycle_services_tab.label": "Cambiar pestaña de servicios",
"settings.openchamber.keyboardShortcuts.action.cycle_theme.label": "Cambiar tema",
"settings.openchamber.keyboardShortcuts.action.cycle_agent.label": "Cambiar agente",
"settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label": "Siguiente modelo favorito",
+5 -6
View File
@@ -1836,22 +1836,18 @@ export const dict: Record<I18nKey, string> = {
"helpDialog.item.focusChatInput": "Enfocar entrada de chat",
"helpDialog.item.togglePromptNavigator": "Mostrar u ocultar navegador de prompts",
"helpDialog.item.abortActiveRun": "Detener ejecución activa (doble presionar)",
"helpDialog.item.toggleRightSidebar": 'Alternar panel de contexto',
"helpDialog.item.openRightSidebarGitTab": 'Abrir superficie de Git',
"helpDialog.item.openRightSidebarFilesTab": 'Abrir superficie de archivos',
"helpDialog.item.toggleTerminalDock": "Mostrar u ocultar dock de terminal",
"helpDialog.item.toggleTerminalExpanded": "Expandir o contraer terminal",
"helpDialog.item.togglePlanContextPanel": "Alternar panel de contexto del plan",
"helpDialog.item.cycleTheme": "Cambiar tema (Claro → Oscuro → Sistema)",
"helpDialog.item.switchSessionTab": "Cambiar pestaña de sesión",
"helpDialog.item.switchContextSurface": "Cambiar superficie del panel de contexto (tecla numérica)",
"helpDialog.item.toggleServicesMenu": "Mostrar u ocultar menú de servicios",
"helpDialog.item.cycleServicesTab": "Cambiar pestaña de servicios",
"helpDialog.item.openSettings": "Abrir configuración",
"helpDialog.keyCombiner.or": "o",
"helpDialog.proTips.title": "Consejos:",
"helpDialog.proTips.commandPalette": "Usa la paleta de comandos ({shortcut}) para acceder rápidamente a todas las acciones",
"helpDialog.proTips.recentSessions": "Las cinco sesiones más recientes aparecen en la paleta de comandos",
"helpDialog.proTips.themeCycling": "El ciclo de tema recuerda tu preferencia entre sesiones",
"helpDialog.proTips.leaderSequences": "Atajos en dos pasos: pulsa la combinación y luego la segunda tecla; Esc cancela",
"header.actions.rightSidebarWithShortcut": "Barra lateral derecha ({shortcut})",
"header.actions.toggleRightSidebarAria": "Mostrar u ocultar barra lateral derecha",
"header.actions.openAppMenu": "Menú de OpenChamber",
@@ -2449,6 +2445,9 @@ export const dict: Record<I18nKey, string> = {
"commandPalette.item.toggleSidebar": "Mostrar u ocultar barra lateral",
"commandPalette.item.showContextUsage": "Mostrar uso del contexto",
"commandPalette.item.toggleTerminal": "Mostrar u ocultar terminal",
"commandPalette.item.cycleTheme": "Cambiar tema",
"commandPalette.item.showOpenCodeStatus": "Mostrar estado de OpenCode",
"commandPalette.item.toggleMemoryDebug": "Alternar panel de depuración de memoria",
"commandPalette.item.openSettings": "Abrir configuración...",
"commandPalette.session.untitled": "Sesión sin título",
"openCodeStatusDialog.title": "Estado de OpenCode",
@@ -1028,9 +1028,8 @@ export const settingsDict = {
'settings.openchamber.keyboardShortcuts.action.toggle_terminal_expanded.label': 'Terminal à bascule étendu',
'settings.openchamber.keyboardShortcuts.action.add_selection_to_chat.label': 'Ajouter la sélection au chat',
'settings.openchamber.keyboardShortcuts.action.toggle_sidebar.label': 'Basculer la barre latérale',
'settings.openchamber.keyboardShortcuts.action.toggle_right_sidebar.label': 'Afficher/masquer le panneau de contexte',
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_git.label': 'Ouvrir la surface Git',
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_files.label': 'Ouvrir la surface Fichiers',
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.label': 'Basculer longlet de session',
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.suffix': ' + 1…9',
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.label': 'Basculer la surface du panneau contextuel',
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.suffix': ' + 1…0',
'settings.openchamber.keyboardShortcuts.action.new_chat.label': 'Nouvelle session',
@@ -1038,9 +1037,7 @@ export const settingsDict = {
'settings.openchamber.keyboardShortcuts.action.new_chat_worktree.label': 'Nouvelle ébauche d\'worktree',
'settings.openchamber.keyboardShortcuts.action.new_mini_chat.label': 'Nouvelle fenêtre de mini-chat',
'settings.openchamber.keyboardShortcuts.action.open_help.label': 'Ouvrir les raccourcis clavier',
'settings.openchamber.keyboardShortcuts.action.toggle_context_plan.label': 'Basculer le panneau contextuel du plan',
'settings.openchamber.keyboardShortcuts.action.toggle_services_menu.label': 'Basculer le menu des services',
'settings.openchamber.keyboardShortcuts.action.cycle_services_tab.label': 'Onglet Services vélo',
'settings.openchamber.keyboardShortcuts.action.cycle_theme.label': 'Thème du cycle',
'settings.openchamber.keyboardShortcuts.action.cycle_agent.label': 'Agent de cycle',
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label': 'Faire avancer le modèle favori',
+5 -6
View File
@@ -1616,22 +1616,18 @@ export const dict = {
'helpDialog.item.focusChatInput': 'Concentration sur la saisie du chat',
'helpDialog.item.togglePromptNavigator': 'Afficher ou masquer le navigateur de prompts',
'helpDialog.item.abortActiveRun': 'Abandonner lexécution active (double pression)',
'helpDialog.item.toggleRightSidebar': 'Afficher/masquer le panneau de contexte',
'helpDialog.item.openRightSidebarGitTab': 'Ouvrir la surface Git',
'helpDialog.item.openRightSidebarFilesTab': 'Ouvrir la surface Fichiers',
'helpDialog.item.toggleTerminalDock': 'Basculer la station d\'accueil du terminal',
'helpDialog.item.toggleTerminalExpanded': 'Terminal à bascule étendu',
'helpDialog.item.togglePlanContextPanel': 'Toggle Panneau contextuel du plan',
'helpDialog.item.cycleTheme': 'Basculer le thème (clair → sombre → système)',
'helpDialog.item.switchSessionTab': 'Basculer longlet de session',
'helpDialog.item.switchContextSurface': 'Basculer la surface du panneau contextuel (touche numérique)',
'helpDialog.item.toggleServicesMenu': 'Basculer le menu des services',
'helpDialog.item.cycleServicesTab': 'Onglet Services de vélo',
'helpDialog.item.openSettings': 'Ouvrir les paramètres',
'helpDialog.keyCombiner.or': 'ou',
'helpDialog.proTips.title': 'Conseils de pro :',
'helpDialog.proTips.commandPalette': 'Utilisez la palette de commandes ({shortcut}) pour accéder rapidement à toutes les actions',
'helpDialog.proTips.recentSessions': 'Les 5 sessions les plus récentes apparaissent dans la palette de commandes',
'helpDialog.proTips.themeCycling': 'Le cyclisme thématique mémorise vos préférences au fil des sessions',
'helpDialog.proTips.leaderSequences': 'Raccourcis en deux temps : appuyez sur la combinaison, puis sur la seconde touche — Échap annule',
'header.actions.rightSidebarWithShortcut': 'Barre latérale droite ({shortcut})',
'header.actions.toggleRightSidebarAria': 'Basculer la barre latérale droite',
'header.actions.openAppMenu': 'Menu de OpenChamber',
@@ -2187,6 +2183,9 @@ export const dict = {
'commandPalette.item.toggleSidebar': 'Basculer la barre latérale',
'commandPalette.item.showContextUsage': 'Afficher l\'utilisation du contexte',
'commandPalette.item.toggleTerminal': 'Basculer le terminal',
'commandPalette.item.cycleTheme': 'Changer de thème',
'commandPalette.item.showOpenCodeStatus': 'Afficher le statut OpenCode',
'commandPalette.item.toggleMemoryDebug': 'Basculer le panneau de débogage mémoire',
'commandPalette.item.openSettings': 'Ouvrez les paramètres...',
'commandPalette.session.untitled': 'Session sans titre',
'openCodeStatusDialog.title': 'Statut OpenCode',
@@ -1143,9 +1143,8 @@ export const settingsDict = {
'settings.openchamber.keyboardShortcuts.action.toggle_terminal_expanded.label': 'ターミナル拡大の切替',
'settings.openchamber.keyboardShortcuts.action.add_selection_to_chat.label': '選択範囲をチャットに追加',
'settings.openchamber.keyboardShortcuts.action.toggle_sidebar.label': 'サイドバーの切替',
'settings.openchamber.keyboardShortcuts.action.toggle_right_sidebar.label': 'コンテキストパネルの表示切替',
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_git.label': 'Git サーフェスを開く',
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_files.label': 'ファイルサーフェスを開く',
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.label': 'セッションタブを切り替え',
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.suffix': ' + 1…9',
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.label': 'コンテキストパネルのサーフェスを切り替え',
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.suffix': ' + 1…0',
'settings.openchamber.keyboardShortcuts.action.new_chat.label': '新しい Session',
@@ -1153,9 +1152,7 @@ export const settingsDict = {
'settings.openchamber.keyboardShortcuts.action.new_chat_worktree.label': '新しい Worktree 下書き',
'settings.openchamber.keyboardShortcuts.action.new_mini_chat.label': '新しいミニチャットウィンドウ',
'settings.openchamber.keyboardShortcuts.action.open_help.label': 'キーボードショートカットを開く',
'settings.openchamber.keyboardShortcuts.action.toggle_context_plan.label': '計画コンテキストパネルの切替',
'settings.openchamber.keyboardShortcuts.action.toggle_services_menu.label': 'サービスメニューの切替',
'settings.openchamber.keyboardShortcuts.action.cycle_services_tab.label': 'サービスタブを順に切替',
'settings.openchamber.keyboardShortcuts.action.cycle_theme.label': 'テーマを順に切替',
'settings.openchamber.keyboardShortcuts.action.cycle_agent.label': 'Agent を順に切替',
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label': 'お気に入りモデルを次へ',
+5 -6
View File
@@ -1854,22 +1854,18 @@ export const dict: Record<I18nKey, string> = {
'helpDialog.item.focusChatInput': 'チャット入力にフォーカス',
'helpDialog.item.togglePromptNavigator': 'プロンプトナビゲーターの表示切替',
'helpDialog.item.abortActiveRun': 'アクティブな実行を中止(ダブルプレス)',
'helpDialog.item.toggleRightSidebar': 'コンテキストパネルの表示切替',
'helpDialog.item.openRightSidebarGitTab': 'Git サーフェスを開く',
'helpDialog.item.openRightSidebarFilesTab': 'ファイルサーフェスを開く',
'helpDialog.item.toggleTerminalDock': 'ターミナルドックの切り替え',
'helpDialog.item.toggleTerminalExpanded': 'ターミナル展開の切り替え',
'helpDialog.item.togglePlanContextPanel': '計画コンテキストパネルの切り替え',
'helpDialog.item.cycleTheme': 'テーマ切り替え(ライト→ダーク→システム)',
'helpDialog.item.switchSessionTab': 'セッションタブを切り替え',
'helpDialog.item.switchContextSurface': 'コンテキストパネルのサーフェスを切り替え(数字キー)',
'helpDialog.item.toggleServicesMenu': 'サービスの切り替え',
'helpDialog.item.cycleServicesTab': 'サービス変数の切り替え',
'helpDialog.item.openSettings': '設定を開く',
'helpDialog.keyCombiner.or': 'または',
'helpDialog.proTips.title': 'プロのヒント:',
'helpDialog.proTips.commandPalette': 'コマンドパレット({shortcut})を使うとすべての操作にすばやくアクセスできます',
'helpDialog.proTips.recentSessions': '最近の5つのセッションがコマンドパレットに表示されます',
'helpDialog.proTips.themeCycling': 'テーマの切り替えはセッション間で設定が記憶されます',
'helpDialog.proTips.leaderSequences': '2段階ショートカット:組み合わせを押してから2つ目のキーを押します(Escで取消)',
'header.actions.rightSidebarWithShortcut': '右サイドバー({shortcut}',
'header.actions.toggleRightSidebarAria': '右サイドバーの切り替え',
'header.actions.openAppMenu': 'OpenChamberメニュー',
@@ -2482,6 +2478,9 @@ export const dict: Record<I18nKey, string> = {
'commandPalette.item.toggleSidebar': 'サイドバーの切り替え',
'commandPalette.item.showContextUsage': 'コンテキスト使用量を表示',
'commandPalette.item.toggleTerminal': 'ターミナルの切り替え',
'commandPalette.item.cycleTheme': 'テーマを順に切替',
'commandPalette.item.showOpenCodeStatus': 'OpenCode のステータスを表示',
'commandPalette.item.toggleMemoryDebug': 'メモリデバッグパネルの切替',
'commandPalette.item.openSettings': '設定を開く...',
'commandPalette.session.untitled': '無題のセッション',
'openCodeStatusDialog.title': 'OpenCodeステータス',
@@ -1110,9 +1110,8 @@ export const settingsDict = {
'settings.openchamber.keyboardShortcuts.action.toggle_terminal_expanded.label': '터미널 확장 토글',
'settings.openchamber.keyboardShortcuts.action.add_selection_to_chat.label': '선택 내용을 채팅에 추가',
'settings.openchamber.keyboardShortcuts.action.toggle_sidebar.label': '사이드바 토글',
'settings.openchamber.keyboardShortcuts.action.toggle_right_sidebar.label': '컨텍스트 패널 표시 전환',
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_git.label': 'Git 서피스 열기',
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_files.label': '파일 서피스 열기',
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.label': '세션 탭 전환',
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.suffix': ' + 1…9',
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.label': '컨텍스트 패널 서피스 전환',
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.suffix': ' + 1…0',
'settings.openchamber.keyboardShortcuts.action.new_chat.label': '새 세션',
@@ -1120,9 +1119,7 @@ export const settingsDict = {
'settings.openchamber.keyboardShortcuts.action.new_chat_worktree.label': '새 worktree 초안',
'settings.openchamber.keyboardShortcuts.action.new_mini_chat.label': '새 Mini Chat 창',
'settings.openchamber.keyboardShortcuts.action.open_help.label': '키보드 단축키 열기',
'settings.openchamber.keyboardShortcuts.action.toggle_context_plan.label': '계획 컨텍스트 패널 토글',
'settings.openchamber.keyboardShortcuts.action.toggle_services_menu.label': '서비스 메뉴 토글',
'settings.openchamber.keyboardShortcuts.action.cycle_services_tab.label': '서비스 탭 순환',
'settings.openchamber.keyboardShortcuts.action.cycle_theme.label': '테마 순환',
'settings.openchamber.keyboardShortcuts.action.cycle_agent.label': '에이전트 순환',
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label': '즐겨찾기 모델 앞으로 순환',
+5 -6
View File
@@ -1860,22 +1860,18 @@ export const dict: Record<I18nKey, string> = {
'helpDialog.item.focusChatInput': '채팅 입력창으로 포커스 이동',
'helpDialog.item.togglePromptNavigator': '프롬프트 탐색기 표시/숨기기',
'helpDialog.item.abortActiveRun': '활성 실행 중단(두 번 누르기)',
'helpDialog.item.toggleRightSidebar': '컨텍스트 패널 표시 전환',
'helpDialog.item.openRightSidebarGitTab': 'Git 서피스 열기',
'helpDialog.item.openRightSidebarFilesTab': '파일 서피스 열기',
'helpDialog.item.toggleTerminalDock': '터미널 독 전환',
'helpDialog.item.toggleTerminalExpanded': '터미널 펼치기/접기',
'helpDialog.item.togglePlanContextPanel': '플랜 컨텍스트 패널 전환',
'helpDialog.item.cycleTheme': '테마 순환(라이트 → 다크 → 시스템)',
'helpDialog.item.switchSessionTab': '세션 탭 전환',
'helpDialog.item.switchContextSurface': '컨텍스트 패널 서피스 전환(숫자 키)',
'helpDialog.item.toggleServicesMenu': '서비스 메뉴 전환',
'helpDialog.item.cycleServicesTab': '서비스 탭 순환',
'helpDialog.item.openSettings': '설정 열기',
'helpDialog.keyCombiner.or': '또는',
'helpDialog.proTips.title': '팁:',
'helpDialog.proTips.commandPalette': '명령 팔레트({shortcut})로 모든 작업에 빠르게 접근하세요',
'helpDialog.proTips.recentSessions': '최근 세션 5개가 명령 팔레트에 표시됩니다',
'helpDialog.proTips.themeCycling': '테마 순환은 세션 간에도 선호 설정을 기억합니다',
'helpDialog.proTips.leaderSequences': '2단계 단축키: 조합을 누른 뒤 두 번째 키를 누르세요 (Esc로 취소)',
'header.actions.rightSidebarWithShortcut': '오른쪽 사이드바 ({shortcut})',
'header.actions.toggleRightSidebarAria': '오른쪽 사이드바 토글',
'header.actions.openAppMenu': 'OpenChamber 메뉴',
@@ -2483,6 +2479,9 @@ export const dict: Record<I18nKey, string> = {
'commandPalette.item.toggleSidebar': '토글 사이드바',
'commandPalette.item.showContextUsage': '컨텍스트 사용량 표시',
'commandPalette.item.toggleTerminal': '토글 터미널',
'commandPalette.item.cycleTheme': '테마 순환',
'commandPalette.item.showOpenCodeStatus': 'OpenCode 상태 표시',
'commandPalette.item.toggleMemoryDebug': '메모리 디버그 패널 토글',
'commandPalette.item.openSettings': '설정... 열기',
'commandPalette.session.untitled': '제목 없는 세션',
'openCodeStatusDialog.title': 'OpenCode 상태',
@@ -818,7 +818,6 @@ export const settingsDict = {
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_backward.label': 'Przełącz ulubiony model wstecz',
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': 'Otwórz wybór modelu',
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label': 'Przełącz ulubiony model w przód',
'settings.openchamber.keyboardShortcuts.action.cycle_services_tab.label': 'Przełącz zakładkę usług',
'settings.openchamber.keyboardShortcuts.action.cycle_theme.label': 'Przełącz motyw',
'settings.openchamber.keyboardShortcuts.action.cycle_agent.label': 'Przełącz agenta',
'settings.openchamber.keyboardShortcuts.action.expand_input.label': 'Rozwiń pole wprowadzania',
@@ -831,13 +830,11 @@ export const settingsDict = {
'settings.openchamber.keyboardShortcuts.action.open_command_palette.label': 'Otwórz paletę poleceń',
'settings.openchamber.keyboardShortcuts.action.open_go_to_line.label': 'Przejdź do linii (edytor plików)',
'settings.openchamber.keyboardShortcuts.action.open_help.label': 'Otwórz skróty klawiszowe',
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_files.label': 'Otwórz powierzchnię plików',
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.label': 'Przełącz kartę sesji',
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.suffix': ' + 1…9',
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.label': 'Przełącz powierzchnię panelu kontekstu',
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.suffix': ' + 1…0',
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_git.label': 'Otwórz powierzchnię Git',
'settings.openchamber.keyboardShortcuts.action.open_settings.label': 'Otwórz ustawienia',
'settings.openchamber.keyboardShortcuts.action.toggle_context_plan.label': 'Przełącz panel kontekstu planu',
'settings.openchamber.keyboardShortcuts.action.toggle_right_sidebar.label': 'Przełącz panel kontekstu',
'settings.openchamber.keyboardShortcuts.action.toggle_services_menu.label': 'Przełącz menu usług',
'settings.openchamber.keyboardShortcuts.action.add_selection_to_chat.label': 'Dodaj zaznaczenie do czatu',
'settings.openchamber.keyboardShortcuts.action.toggle_sidebar.label': 'Przełącz pasek boczny',
+5 -6
View File
@@ -1452,6 +1452,9 @@ export const dict: Record<I18nKey, string> = {
'commandPalette.item.showSessionSwitcher': 'Pokaż przełącznik sesji',
'commandPalette.item.toggleSidebar': 'Przełącz panel boczny',
'commandPalette.item.toggleTerminal': 'Przełącz terminal',
'commandPalette.item.cycleTheme': 'Przełącz motyw',
'commandPalette.item.showOpenCodeStatus': 'Pokaż status OpenCode',
'commandPalette.item.toggleMemoryDebug': 'Przełącz panel debugowania pamięci',
'commandPalette.session.untitled': 'Nienazwana sesja',
'commandPalette.title': 'Paleta poleceń',
'contextPanel.actions.closePanel': 'Zamknij panel',
@@ -2452,7 +2455,6 @@ export const dict: Record<I18nKey, string> = {
'helpDialog.item.createNewSession': 'Utwórz nową sesję',
'helpDialog.item.createNewWorktreeDraft': 'Utwórz nowy szkic drzewa pracy',
'helpDialog.item.cycleAgent': 'Przełącz agenta (w polu czatu)',
'helpDialog.item.cycleServicesTab': 'Przełącz kartę usług',
'helpDialog.item.cycleTheme': 'Przełącz motyw (Jasny → Ciemny → Systemowy)',
'helpDialog.item.cycleThinkingVariant': 'Przełącz wariant myślenia (skrót globalny)',
'helpDialog.item.focusChatInput': 'Ustaw fokus na polu czatu',
@@ -2461,13 +2463,10 @@ export const dict: Record<I18nKey, string> = {
'helpDialog.item.newWindow': 'Nowe okno (tylko desktop)',
'helpDialog.item.openCommandPalette': 'Otwórz paletę poleceń',
'helpDialog.item.openModelSelector': 'Otwórz selektor modeli',
'helpDialog.item.openRightSidebarFilesTab': 'Otwórz powierzchnię plików',
'helpDialog.item.openRightSidebarGitTab': 'Otwórz powierzchnię Git',
'helpDialog.item.openSettings': 'Otwórz ustawienia',
'helpDialog.item.showKeyboardShortcuts': 'Pokaż skróty klawiaturowe (to okno)',
'helpDialog.item.switchSessionTab': 'Przełącz kartę sesji',
'helpDialog.item.switchContextSurface': 'Przełącz powierzchnię panelu kontekstu (klawisz liczbowy)',
'helpDialog.item.togglePlanContextPanel': 'Przełącz panel kontekstu planu',
'helpDialog.item.toggleRightSidebar': 'Przełącz panel kontekstu',
'helpDialog.item.toggleServicesMenu': 'Przełącz menu usług',
'helpDialog.item.toggleSessionSidebar': 'Przełącz panel sesji',
'helpDialog.item.addSelectionToChat': 'Dodaj zaznaczenie do czatu',
@@ -2476,7 +2475,7 @@ export const dict: Record<I18nKey, string> = {
'helpDialog.keyCombiner.or': 'lub',
'helpDialog.proTips.commandPalette': 'Użyj Palety poleceń ({shortcut}), aby szybko uzyskać dostęp do wszystkich akcji',
'helpDialog.proTips.recentSessions': '5 ostatnich sesji pojawia się w Palecie poleceń',
'helpDialog.proTips.themeCycling': 'Przełączanie motywów zapamiętuje twoje preferencje między sesjami',
'helpDialog.proTips.leaderSequences': 'Skróty dwustopniowe: naciśnij kombinację, potem drugi klawisz — Esc anuluje',
'helpDialog.proTips.title': 'Wskazówki:',
'helpDialog.section.interface': 'Interfejs',
'helpDialog.section.navigationCommands': 'Nawigacja i polecenia',
@@ -1110,9 +1110,8 @@ export const settingsDict = {
"settings.openchamber.keyboardShortcuts.action.toggle_terminal_expanded.label": "Expandir ou recolher terminal",
"settings.openchamber.keyboardShortcuts.action.add_selection_to_chat.label": "Adicionar seleção ao chat",
"settings.openchamber.keyboardShortcuts.action.toggle_sidebar.label": "Mostrar ou ocultar barra lateral",
"settings.openchamber.keyboardShortcuts.action.toggle_right_sidebar.label": 'Alternar painel de contexto',
"settings.openchamber.keyboardShortcuts.action.open_right_sidebar_git.label": 'Abrir superfície do Git',
"settings.openchamber.keyboardShortcuts.action.open_right_sidebar_files.label": 'Abrir superfície de arquivos',
"settings.openchamber.keyboardShortcuts.action.switch_session_tab.label": "Alternar aba de sessão",
"settings.openchamber.keyboardShortcuts.action.switch_session_tab.suffix": " + 1…9",
"settings.openchamber.keyboardShortcuts.action.switch_context_surface.label": "Alternar superfície do painel de contexto",
"settings.openchamber.keyboardShortcuts.action.switch_context_surface.suffix": " + 1…0",
"settings.openchamber.keyboardShortcuts.action.new_chat.label": "Nova sessão",
@@ -1120,9 +1119,7 @@ export const settingsDict = {
"settings.openchamber.keyboardShortcuts.action.new_chat_worktree.label": "Novo rascunho de worktree",
"settings.openchamber.keyboardShortcuts.action.new_mini_chat.label": "Nova janela Mini Chat",
"settings.openchamber.keyboardShortcuts.action.open_help.label": "Abrir atalhos de teclado",
"settings.openchamber.keyboardShortcuts.action.toggle_context_plan.label": "Alternar painel de plano de contexto",
"settings.openchamber.keyboardShortcuts.action.toggle_services_menu.label": "Mostrar ou ocultar menu de serviços",
"settings.openchamber.keyboardShortcuts.action.cycle_services_tab.label": "Alternar aba de serviços",
"settings.openchamber.keyboardShortcuts.action.cycle_theme.label": "Alternar tema",
"settings.openchamber.keyboardShortcuts.action.cycle_agent.label": "Alternar agente",
"settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label": "Próximo modelo favorito",
+5 -6
View File
@@ -1836,22 +1836,18 @@ export const dict: Record<I18nKey, string> = {
"helpDialog.item.focusChatInput": "Focar entrada do chat",
"helpDialog.item.togglePromptNavigator": "Mostrar ou ocultar navegador de prompts",
"helpDialog.item.abortActiveRun": "Interromper execução ativa (duplo clique)",
"helpDialog.item.toggleRightSidebar": 'Alternar painel de contexto',
"helpDialog.item.openRightSidebarGitTab": 'Abrir superfície do Git',
"helpDialog.item.openRightSidebarFilesTab": 'Abrir superfície de arquivos',
"helpDialog.item.toggleTerminalDock": "Mostrar ou ocultar dock de terminal",
"helpDialog.item.toggleTerminalExpanded": "Expandir ou recolher o terminal",
"helpDialog.item.togglePlanContextPanel": "Alternar painel de contexto do plano",
"helpDialog.item.cycleTheme": "Alternar tema (Claro → Escuro → Sistema)",
"helpDialog.item.switchSessionTab": "Alternar aba de sessão",
"helpDialog.item.switchContextSurface": "Alternar superfície do painel de contexto (tecla numérica)",
"helpDialog.item.toggleServicesMenu": "Mostrar ou ocultar menu de serviços",
"helpDialog.item.cycleServicesTab": "Alternar aba de serviços",
"helpDialog.item.openSettings": "Abrir configurações",
"helpDialog.keyCombiner.or": "ou",
"helpDialog.proTips.title": "Dicas:",
"helpDialog.proTips.commandPalette": "Use a paleta de comandos ({shortcut}) para acessar rapidamente todas as ações",
"helpDialog.proTips.recentSessions": "As cinco sessões mais recentes aparecem na paleta de comandos",
"helpDialog.proTips.themeCycling": "A alternância de tema lembra sua preferência entre sessões",
"helpDialog.proTips.leaderSequences": "Atalhos em duas etapas: pressione a combinação e depois a segunda tecla — Esc cancela",
"header.actions.rightSidebarWithShortcut": "Barra lateral direita ({shortcut})",
"header.actions.toggleRightSidebarAria": "Mostrar ou ocultar barra lateral direita",
"header.actions.openAppMenu": "Menu do OpenChamber",
@@ -2449,6 +2445,9 @@ export const dict: Record<I18nKey, string> = {
"commandPalette.item.toggleSidebar": "Mostrar ou ocultar barra lateral",
"commandPalette.item.showContextUsage": "Mostrar uso do contexto",
"commandPalette.item.toggleTerminal": "Mostrar ou ocultar terminal",
"commandPalette.item.cycleTheme": "Alternar tema",
"commandPalette.item.showOpenCodeStatus": "Mostrar status do OpenCode",
"commandPalette.item.toggleMemoryDebug": "Alternar painel de depuração de memória",
"commandPalette.item.openSettings": "Abrir configurações...",
"commandPalette.session.untitled": "Sessão sem título",
"openCodeStatusDialog.title": "Status do OpenCode",
@@ -1110,9 +1110,8 @@ export const settingsDict = {
"settings.openchamber.keyboardShortcuts.action.toggle_terminal_expanded.label": "Розгорнути або згорнути термінал",
"settings.openchamber.keyboardShortcuts.action.add_selection_to_chat.label": "Додати виділення в чат",
"settings.openchamber.keyboardShortcuts.action.toggle_sidebar.label": "Перемкнути бічну панель",
"settings.openchamber.keyboardShortcuts.action.toggle_right_sidebar.label": 'Перемкнути контекстну панель',
"settings.openchamber.keyboardShortcuts.action.open_right_sidebar_git.label": 'Відкрити поверхню Git',
"settings.openchamber.keyboardShortcuts.action.open_right_sidebar_files.label": 'Відкрити поверхню файлів',
"settings.openchamber.keyboardShortcuts.action.switch_session_tab.label": "Перемкнути вкладку сесії",
"settings.openchamber.keyboardShortcuts.action.switch_session_tab.suffix": " + 1…9",
"settings.openchamber.keyboardShortcuts.action.switch_context_surface.label": "Перемкнути поверхню панелі контексту",
"settings.openchamber.keyboardShortcuts.action.switch_context_surface.suffix": " + 1…0",
"settings.openchamber.keyboardShortcuts.action.new_chat.label": "Нова сесія",
@@ -1120,9 +1119,7 @@ export const settingsDict = {
"settings.openchamber.keyboardShortcuts.action.new_chat_worktree.label": "Нова чернетка worktree",
"settings.openchamber.keyboardShortcuts.action.new_mini_chat.label": "Нове вікно Mini Chat",
"settings.openchamber.keyboardShortcuts.action.open_help.label": "Відкрити комбінації клавіш",
"settings.openchamber.keyboardShortcuts.action.toggle_context_plan.label": "Перемкнути контекстну панель плану",
"settings.openchamber.keyboardShortcuts.action.toggle_services_menu.label": "Перемкнути меню сервісів",
"settings.openchamber.keyboardShortcuts.action.cycle_services_tab.label": "Перемкнути вкладку сервісів",
"settings.openchamber.keyboardShortcuts.action.cycle_theme.label": "Перемкнути тему",
"settings.openchamber.keyboardShortcuts.action.cycle_agent.label": "Перемкнути агента",
"settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label": "Перемкнути улюблену модель вперед",
+5 -6
View File
@@ -1836,22 +1836,18 @@ export const dict: Record<I18nKey, string> = {
"helpDialog.item.focusChatInput": "Фокус на полі вводу чату",
"helpDialog.item.togglePromptNavigator": "Показати або приховати навігатор промптів",
"helpDialog.item.abortActiveRun": "Перервати активний запуск (подвійне натискання)",
"helpDialog.item.toggleRightSidebar": 'Перемкнути контекстну панель',
"helpDialog.item.openRightSidebarGitTab": 'Відкрити поверхню Git',
"helpDialog.item.openRightSidebarFilesTab": 'Відкрити поверхню файлів',
"helpDialog.item.toggleTerminalDock": "Перемкнути панель терміналу",
"helpDialog.item.toggleTerminalExpanded": "Розгорнути або згорнути термінал",
"helpDialog.item.togglePlanContextPanel": "Перемкнути панель контексту плану",
"helpDialog.item.cycleTheme": "Перемкнути тему (Світла → Темна → Системна)",
"helpDialog.item.switchSessionTab": "Перемкнути вкладку сесії",
"helpDialog.item.switchContextSurface": "Перемкнути поверхню панелі контексту (цифрова клавіша)",
"helpDialog.item.toggleServicesMenu": "Перемкнути меню сервісів",
"helpDialog.item.cycleServicesTab": "Перемкнути вкладку сервісів",
"helpDialog.item.openSettings": "Відкрити налаштування",
"helpDialog.keyCombiner.or": "або",
"helpDialog.proTips.title": "Поради:",
"helpDialog.proTips.commandPalette": "Використовуйте палітру команд ({shortcut}), щоб швидко перейти до будь-якої дії",
"helpDialog.proTips.recentSessions": "5 останніх сесій відображаються на панелі команд",
"helpDialog.proTips.themeCycling": "Перемикання теми запам’ятовує ваші переваги протягом сесій",
"helpDialog.proTips.leaderSequences": "Двокрокові шорткати: натисни комбінацію, потім другу клавішу — Esc скасовує",
"header.actions.rightSidebarWithShortcut": "Права бічна панель ({shortcut})",
"header.actions.toggleRightSidebarAria": "Перемкнути праву бічну панель",
"header.actions.openAppMenu": "Меню OpenChamber",
@@ -2449,6 +2445,9 @@ export const dict: Record<I18nKey, string> = {
"commandPalette.item.toggleSidebar": "Перемкнути бічну панель",
"commandPalette.item.showContextUsage": "Показати використання контексту",
"commandPalette.item.toggleTerminal": "Перемкнути термінал",
"commandPalette.item.cycleTheme": "Перемкнути тему",
"commandPalette.item.showOpenCodeStatus": "Показати статус OpenCode",
"commandPalette.item.toggleMemoryDebug": "Показати/сховати панель memory debug",
"commandPalette.item.openSettings": "Відкрити налаштування...",
"commandPalette.session.untitled": "Сесія без назви",
"openCodeStatusDialog.title": "Статус OpenCode",
@@ -1110,9 +1110,8 @@ export const settingsDict = {
'settings.openchamber.keyboardShortcuts.action.toggle_terminal_expanded.label': '切换终端展开',
'settings.openchamber.keyboardShortcuts.action.add_selection_to_chat.label': '将选中内容添加到聊天',
'settings.openchamber.keyboardShortcuts.action.toggle_sidebar.label': '切换侧边栏',
'settings.openchamber.keyboardShortcuts.action.toggle_right_sidebar.label': '切换上下文面板',
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_git.label': '打开 Git 界面',
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_files.label': '打开文件界面',
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.label': '切换会话标签页',
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.suffix': ' + 1…9',
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.label': '切换上下文面板界面',
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.suffix': ' + 1…0',
'settings.openchamber.keyboardShortcuts.action.new_chat.label': '新建会话',
@@ -1120,9 +1119,7 @@ export const settingsDict = {
'settings.openchamber.keyboardShortcuts.action.new_chat_worktree.label': '新建工作树草稿',
'settings.openchamber.keyboardShortcuts.action.new_mini_chat.label': '新建 Mini Chat 窗口',
'settings.openchamber.keyboardShortcuts.action.open_help.label': '打开键盘快捷键',
'settings.openchamber.keyboardShortcuts.action.toggle_context_plan.label': '切换上下文面板中的计划',
'settings.openchamber.keyboardShortcuts.action.toggle_services_menu.label': '切换服务菜单',
'settings.openchamber.keyboardShortcuts.action.cycle_services_tab.label': '轮换服务菜单标签',
'settings.openchamber.keyboardShortcuts.action.cycle_theme.label': '轮换主题',
'settings.openchamber.keyboardShortcuts.action.cycle_agent.label': '轮换智能体',
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label': '向前轮换收藏模型',
+5 -6
View File
@@ -1824,22 +1824,18 @@ export const dict: Record<I18nKey, string> = {
'helpDialog.item.focusChatInput': '聚焦聊天输入框',
'helpDialog.item.togglePromptNavigator': '显示或隐藏提示词导航',
'helpDialog.item.abortActiveRun': '中止当前运行(双击)',
'helpDialog.item.toggleRightSidebar': '切换上下文面板',
'helpDialog.item.openRightSidebarGitTab': '打开 Git 界面',
'helpDialog.item.openRightSidebarFilesTab': '打开文件界面',
'helpDialog.item.toggleTerminalDock': '切换终端停靠栏',
'helpDialog.item.toggleTerminalExpanded': '切换终端展开状态',
'helpDialog.item.togglePlanContextPanel': '切换计划上下文面板',
'helpDialog.item.cycleTheme': '循环切换主题(浅色 → 深色 → 跟随系统)',
'helpDialog.item.switchSessionTab': '切换会话标签页',
'helpDialog.item.switchContextSurface': '切换上下文面板界面(数字键)',
'helpDialog.item.toggleServicesMenu': '切换服务菜单',
'helpDialog.item.cycleServicesTab': '循环服务标签',
'helpDialog.item.openSettings': '打开设置',
'helpDialog.keyCombiner.or': '或',
'helpDialog.proTips.title': '使用提示:',
'helpDialog.proTips.commandPalette': '使用命令面板({shortcut})可快速访问所有操作',
'helpDialog.proTips.recentSessions': '最近 5 个会话会显示在命令面板中',
'helpDialog.proTips.themeCycling': '主题循环会记住你在各会话中的偏好',
'helpDialog.proTips.leaderSequences': '两段式快捷键:先按组合键,再按第二个键(Esc 取消)',
'header.actions.rightSidebarWithShortcut': '右侧边栏({shortcut}',
'header.actions.toggleRightSidebarAria': '切换右侧边栏',
'header.actions.openAppMenu': 'OpenChamber 菜单',
@@ -2449,6 +2445,9 @@ export const dict: Record<I18nKey, string> = {
'commandPalette.item.toggleSidebar': '切换侧边栏',
'commandPalette.item.showContextUsage': '显示上下文用量',
'commandPalette.item.toggleTerminal': '切换终端',
'commandPalette.item.cycleTheme': '轮换主题',
'commandPalette.item.showOpenCodeStatus': '显示 OpenCode 状态',
'commandPalette.item.toggleMemoryDebug': '切换内存调试面板',
'commandPalette.item.openSettings': '打开设置...',
'commandPalette.session.untitled': '未命名会话',
'openCodeStatusDialog.title': 'OpenCode 状态',
@@ -1017,9 +1017,8 @@ export const settingsDict = {
'settings.openchamber.keyboardShortcuts.action.toggle_terminal_expanded.label': '切換終端機展開',
'settings.openchamber.keyboardShortcuts.action.add_selection_to_chat.label': '將選取內容加入聊天',
'settings.openchamber.keyboardShortcuts.action.toggle_sidebar.label': '切換側邊欄',
'settings.openchamber.keyboardShortcuts.action.toggle_right_sidebar.label': '切換上下文面板',
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_git.label': '開啟 Git 介面',
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_files.label': '開啟檔案介面',
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.label': '切換工作階段分頁',
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.suffix': ' + 1…9',
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.label': '切換上下文面板介面',
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.suffix': ' + 1…0',
'settings.openchamber.keyboardShortcuts.action.new_chat.label': '新建工作階段',
@@ -1027,9 +1026,7 @@ export const settingsDict = {
'settings.openchamber.keyboardShortcuts.action.new_chat_worktree.label': '新增 worktree 草稿',
'settings.openchamber.keyboardShortcuts.action.new_mini_chat.label': '新建 Mini Chat 視窗',
'settings.openchamber.keyboardShortcuts.action.open_help.label': '開啟鍵盤快速鍵',
'settings.openchamber.keyboardShortcuts.action.toggle_context_plan.label': '切換上下文面板中的計畫',
'settings.openchamber.keyboardShortcuts.action.toggle_services_menu.label': '切換服務選單',
'settings.openchamber.keyboardShortcuts.action.cycle_services_tab.label': '輪換服務選單分頁',
'settings.openchamber.keyboardShortcuts.action.cycle_theme.label': '輪換主題',
'settings.openchamber.keyboardShortcuts.action.cycle_agent.label': '輪換 agent',
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label': '向前輪換收藏模型',
+5 -6
View File
@@ -1828,22 +1828,18 @@ export const dict: Record<I18nKey, string> = {
'helpDialog.item.focusChatInput': '聚焦聊天輸入框',
'helpDialog.item.togglePromptNavigator': '顯示或隱藏提示詞導覽',
'helpDialog.item.abortActiveRun': '中止目前執行(連按兩下)',
'helpDialog.item.toggleRightSidebar': '切換上下文面板',
'helpDialog.item.openRightSidebarGitTab': '開啟 Git 介面',
'helpDialog.item.openRightSidebarFilesTab': '開啟檔案介面',
'helpDialog.item.toggleTerminalDock': '切換終端機停靠欄',
'helpDialog.item.toggleTerminalExpanded': '切換終端機展開狀態',
'helpDialog.item.togglePlanContextPanel': '切換計畫上下文面板',
'helpDialog.item.cycleTheme': '循環切換主題(淺色 → 深色 → 跟隨系統)',
'helpDialog.item.switchSessionTab': '切換工作階段分頁',
'helpDialog.item.switchContextSurface': '切換上下文面板介面(數字鍵)',
'helpDialog.item.toggleServicesMenu': '切換服務選單',
'helpDialog.item.cycleServicesTab': '循環服務標籤',
'helpDialog.item.openSettings': '開啟設定',
'helpDialog.keyCombiner.or': '或',
'helpDialog.proTips.title': '使用提示:',
'helpDialog.proTips.commandPalette': '使用命令面板({shortcut})可快速存取所有操作',
'helpDialog.proTips.recentSessions': '最近 5 個會话會顯示在命令面板中',
'helpDialog.proTips.themeCycling': '主題循環會記住你在各會話中的偏好',
'helpDialog.proTips.leaderSequences': '兩段式快捷鍵:先按組合鍵,再按第二個鍵(Esc 取消)',
'header.actions.rightSidebarWithShortcut': '右側邊欄({shortcut}',
'header.actions.toggleRightSidebarAria': '切換右側邊欄',
'header.actions.openAppMenu': 'OpenChamber 選單',
@@ -2453,6 +2449,9 @@ export const dict: Record<I18nKey, string> = {
'commandPalette.item.toggleSidebar': '切換側邊欄',
'commandPalette.item.showContextUsage': '顯示上下文用量',
'commandPalette.item.toggleTerminal': '切換終端機',
'commandPalette.item.cycleTheme': '輪換主題',
'commandPalette.item.showOpenCodeStatus': '顯示 OpenCode 狀態',
'commandPalette.item.toggleMemoryDebug': '切換記憶體偵錯面板',
'commandPalette.item.openSettings': '開啟設定...',
'commandPalette.session.untitled': '未命名會話',
'openCodeStatusDialog.title': 'OpenCode 狀態',
+17
View File
@@ -9,6 +9,23 @@ import { useSessionUIStore } from '@/sync/session-ui-store';
* count as neighbours the same rule the strip uses for rendering. The
* session itself is never touched.
*/
/**
* Activate the nth (0-based) header session tab, counting only tabs whose
* session is present in the loaded session list the same rule the strip
* uses for rendering, so the digit matches what the user sees.
*/
export const activateSessionTabByIndex = (index: number): boolean => {
const { tabIds } = useSessionTabsStore.getState();
const sessionsById = new Map(
useGlobalSessionsStore.getState().activeSessions.map((session) => [session.id, session] as const),
);
const renderable = tabIds.filter((id) => sessionsById.has(id));
const session = renderable[index] ? sessionsById.get(renderable[index]) : null;
if (!session) return false;
useSessionUIStore.getState().setCurrentSession(session.id, resolveGlobalSessionDirectory(session));
return true;
};
export const closeSessionTabAndActivateNeighbour = (sessionId: string): void => {
const { tabIds, closeTab } = useSessionTabsStore.getState();
if (!tabIds.includes(sessionId)) return;
@@ -25,9 +25,9 @@ Component interaction keys that are not application commands, such as list navig
# Binding rules
Bindings remain persisted as `Record<string, string>`. Each binding has one chord or at most two space-separated chords, such as `mod+s p`. `mod` is the platform-neutral primary modifier (Command on macOS, Control elsewhere), while `alt` is the platform-neutral alternate modifier (Option on macOS, Alt elsewhere); `command`, `cmd`, `meta`, and `option` are accepted input aliases but normalize to those canonical tokens. `normalizeCombo`, `parseShortcut`, `formatShortcutForDisplay`, and `getShortcutConflict` provide the shared parsing and validation behavior. Display formatting uses macOS keyboard symbols (`⌘`, `⌥`, `⌃`, `⇧`) on macOS and named modifiers (`Ctrl`, `Alt`, `Shift`) elsewhere, including tooltip and accessible text consumers. A single chord conflicts with a sequence sharing its first chord; sibling sequences are valid.
Bindings remain persisted as `Record<string, string>`. Each binding has one chord or at most two space-separated chords, such as `mod+k p`. `mod` is the platform-neutral primary modifier (Command on macOS, Control elsewhere), while `alt` is the platform-neutral alternate modifier (Option on macOS, Alt elsewhere); `command`, `cmd`, `meta`, and `option` are accepted input aliases but normalize to those canonical tokens. `normalizeCombo`, `parseShortcut`, `formatShortcutForDisplay`, and `getShortcutConflict` provide the shared parsing and validation behavior. Display formatting uses macOS keyboard symbols (`⌘`, `⌥`, `⌃`, `⇧`) on macOS and named modifiers (`Ctrl`, `Alt`, `Shift`) elsewhere, including tooltip and accessible text consumers. 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`, `mod+s g`, and `mod+s l` to the draft target pickers and session list.
The default layout follows three modes: single chords for everyday actions, the `mod+k` leader for open/go actions (`mod+k p`, `mod+k g`, `mod+k l`, `mod+k t`, `mod+k n`, `mod+k i`, `mod+k h`), and held digit prefixes — held `mod` + digit switches header session tabs, held `mod+alt` + digit switches context panel surfaces. Every schema action ships with a default binding; palette-only commands (context surfaces, OpenCode status, memory debug) live outside the schema and the palette invokes their owning modules directly. Single-chord handlers still get the first chance at a leader's chord; returning `false` lets the dispatcher arm the sequence.
The internal `switch_tab_*` bindings remain available to mobile handlers. Desktop numeric context-surface switching is resolved by the configurable `switch_context_surface` prefix before normal dispatcher matching and falls through on mobile.
+32 -2
View File
@@ -1,6 +1,7 @@
import { describe, expect, test } from 'bun:test';
import {
eventMatchesShortcut,
eventMatchesShortcutPrefix,
formatShortcutForDisplay,
getEffectiveShortcutPrefix,
@@ -9,12 +10,13 @@ import {
isShortcutPrefixHeld,
normalizeCombo,
parseShortcut,
resolveShortcutEventDigit,
UNASSIGNED_SHORTCUT,
} from './index';
describe('getEffectiveShortcutPrefix', () => {
test('falls back to the action default (bare mod) when unset', () => {
expect(getEffectiveShortcutPrefix('switch_context_surface', {})).toBe('mod');
test('falls back to the action default (bare mod+alt) when unset', () => {
expect(getEffectiveShortcutPrefix('switch_context_surface', {})).toBe('mod+alt');
});
test('honors modifier + key overrides', () => {
@@ -126,3 +128,31 @@ describe('platform shortcut labels', () => {
expect(formatShortcutForDisplay('alt', 'Unassigned', 'other')).toBe('Alt');
});
});
describe('layout-independent key matching', () => {
const event = (overrides: Partial<KeyboardEvent>): KeyboardEvent =>
// SAFETY: the matcher only reads the modifier flags, key, and code
// provided here; a full KeyboardEvent is not constructible in bun tests.
({ altKey: false, ctrlKey: false, metaKey: false, shiftKey: false, key: '', code: '', ...overrides }) as KeyboardEvent;
test('a non-Latin layout letter matches through the physical key code', () => {
expect(eventMatchesShortcut(event({ ctrlKey: true, key: 'л', code: 'KeyK' }), 'mod+k')).toBe(true);
expect(eventMatchesShortcut(event({ key: 'з', code: 'KeyP' }), 'p')).toBe(true);
});
test('macOS Option symbol substitution matches through the digit code', () => {
expect(eventMatchesShortcut(event({ ctrlKey: true, altKey: true, key: '¡', code: 'Digit1' }), 'mod+alt+1')).toBe(true);
});
test('Latin layouts that move keys keep their key-based meaning', () => {
// Dvorak: physical KeyT produces "y"; the binding follows the character.
expect(eventMatchesShortcut(event({ ctrlKey: true, key: 'y', code: 'KeyT' }), 'mod+y')).toBe(true);
expect(eventMatchesShortcut(event({ ctrlKey: true, key: 'y', code: 'KeyT' }), 'mod+t')).toBe(false);
});
test('resolveShortcutEventDigit reads the digit from the code under Option', () => {
expect(resolveShortcutEventDigit({ key: '¡', code: 'Digit1' })).toBe('1');
expect(resolveShortcutEventDigit({ key: '5', code: 'Digit5' })).toBe('5');
expect(resolveShortcutEventDigit({ key: 'a', code: 'KeyA' })).toBe(null);
});
});
+45 -10
View File
@@ -247,6 +247,50 @@ export function isRiskyBrowserShortcut(combo: ShortcutCombo): boolean {
});
}
const CODE_KEY_MAP = new Map<string, string>([
['Comma', ','],
['Period', '.'],
['Slash', '/'],
['Backquote', '`'],
['BracketLeft', '['],
['BracketRight', ']'],
['Semicolon', ';'],
['Quote', "'"],
['Minus', '-'],
['Equal', '='],
]);
function keyFromEventCode(code: string): string | null {
if (code.startsWith('Key') && code.length === 4) return code.slice(3).toLowerCase();
if (code.startsWith('Digit') && code.length === 6) return code.slice(5);
return CODE_KEY_MAP.get(code) ?? null;
}
/**
* The character a physical key press should match against bindings. `key`
* carries the layout-produced character: Option on macOS substitutes symbols
* ("¡" for 1) and non-Latin layouts substitute their own alphabet ("л" for
* K). Both keep the physical key in `code`, so those two cases fall back to
* it; Latin layouts that MOVE keys (Dvorak, AZERTY) keep their `key`-based
* meaning untouched.
*/
export function resolveShortcutEventKey(
event: Pick<KeyboardEvent, 'key' | 'code' | 'altKey'>,
): string {
const raw = event.key;
if (event.altKey) return keyFromEventCode(event.code) ?? raw;
if (raw.length === 1 && raw.charCodeAt(0) > 127) return keyFromEventCode(event.code) ?? raw;
return raw;
}
/** The digit a press addresses, layout- and Option-proof via `code`. */
export function resolveShortcutEventDigit(
event: Pick<KeyboardEvent, 'key' | 'code'>,
): string | null {
if (event.code.startsWith('Digit') && event.code.length === 6) return event.code.slice(5);
return event.key.length === 1 && event.key >= '0' && event.key <= '9' ? event.key : null;
}
export function eventMatchesShortcut(
event: KeyboardEvent | React.KeyboardEvent,
combo: ShortcutCombo,
@@ -280,16 +324,7 @@ export function eventMatchesShortcut(
if (event.ctrlKey && !ctrlUsedAsMod) return false;
}
let eventKeyRaw = event.key;
if (event.altKey) {
if (event.code.startsWith('Key') && event.code.length === 4) {
eventKeyRaw = event.code.slice(3).toLowerCase();
} else if (event.code.startsWith('Digit') && event.code.length === 6) {
eventKeyRaw = event.code.slice(5);
}
}
return keyToShortcutToken(eventKeyRaw) === keyToShortcutToken(chord.key);
return keyToShortcutToken(resolveShortcutEventKey(event)) === keyToShortcutToken(chord.key);
}
export function isShortcutPrefixHeld(prefixCombo: ShortcutCombo, heldKeys: ReadonlySet<string>): boolean {
+26 -50
View File
@@ -5,7 +5,6 @@ type ShortcutCategory = 'session' | 'models' | 'panels' | 'navigation' | 'applic
type ShortcutConfig = {
id: string;
defaultBinding: ShortcutCombo;
allowsSequenceFallback?: true;
/** The binding is a bare-modifier chord prefix (completed by another key);
conflict resolution compares its prefix rather than a full combo. */
prefixStyle?: true;
@@ -17,12 +16,18 @@ type ShortcutConfig = {
}
);
// Default layout, unified around three modes:
// - Single chords for everyday actions.
// - The mod+k leader for "open/go" actions, second key mnemonic.
// - Held mod + digit switches header session tabs; held mod+alt + digit
// switches context panel surfaces (mod+shift+digit is reserved by macOS
// screenshots).
// Everything else lives only in the command palette, outside this schema.
const SHORTCUT_GROUPS = {
session: [
{
id: 'add_selection_to_chat',
defaultBinding: 'mod+l',
allowsSequenceFallback: true,
customizable: true,
settingsLabelKey:
'settings.openchamber.keyboardShortcuts.action.add_selection_to_chat.label',
@@ -35,7 +40,7 @@ const SHORTCUT_GROUPS = {
},
{
id: 'open_timeline_dialog',
defaultBinding: 'mod+t',
defaultBinding: 'mod+k t',
customizable: true,
settingsLabelKey:
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label',
@@ -54,21 +59,21 @@ const SHORTCUT_GROUPS = {
},
{
id: 'open_draft_project_picker',
defaultBinding: 'mod+s p',
defaultBinding: 'mod+k p',
customizable: true,
settingsLabelKey:
'settings.openchamber.keyboardShortcuts.action.open_draft_project_picker.label',
},
{
id: 'open_draft_worktree_picker',
defaultBinding: 'mod+s g',
defaultBinding: 'mod+k g',
customizable: true,
settingsLabelKey:
'settings.openchamber.keyboardShortcuts.action.open_draft_worktree_picker.label',
},
{
id: 'open_session_list',
defaultBinding: 'mod+s l',
defaultBinding: 'mod+k l',
customizable: true,
settingsLabelKey:
'settings.openchamber.keyboardShortcuts.action.open_session_list.label',
@@ -146,65 +151,45 @@ const SHORTCUT_GROUPS = {
},
{
id: 'toggle_sidebar',
defaultBinding: 'mod+alt+l',
defaultBinding: 'mod+b',
customizable: true,
settingsLabelKey: 'settings.openchamber.keyboardShortcuts.action.toggle_sidebar.label',
},
{
id: 'toggle_prompt_navigator',
defaultBinding: 'mod+alt+p',
defaultBinding: 'mod+k n',
customizable: true,
settingsLabelKey:
'settings.openchamber.keyboardShortcuts.action.toggle_prompt_navigator.label',
},
{
id: 'toggle_right_sidebar',
defaultBinding: 'mod+b',
customizable: true,
settingsLabelKey:
'settings.openchamber.keyboardShortcuts.action.toggle_right_sidebar.label',
},
{
id: 'open_right_sidebar_git',
defaultBinding: 'mod+shift+g',
customizable: true,
settingsLabelKey:
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_git.label',
},
{
id: 'open_right_sidebar_files',
defaultBinding: 'mod+shift+f',
customizable: true,
settingsLabelKey:
'settings.openchamber.keyboardShortcuts.action.open_right_sidebar_files.label',
},
{
id: 'switch_context_surface',
id: 'switch_session_tab',
defaultBinding: 'mod',
// The binding is a bare modifier acting as a chord prefix (completed by
// a digit); conflict resolution must compare its PREFIX, not a combo.
prefixStyle: true,
customizable: true,
settingsLabelKey:
'settings.openchamber.keyboardShortcuts.action.switch_session_tab.label',
},
{
id: 'switch_context_surface',
defaultBinding: 'mod+alt',
prefixStyle: true,
customizable: true,
settingsLabelKey:
'settings.openchamber.keyboardShortcuts.action.switch_context_surface.label',
},
{
id: 'toggle_context_plan',
defaultBinding: 'mod+shift+p',
customizable: true,
settingsLabelKey:
'settings.openchamber.keyboardShortcuts.action.toggle_context_plan.label',
},
{
id: 'toggle_services_menu',
defaultBinding: 'mod+shift+s',
defaultBinding: 'mod+k i',
customizable: true,
settingsLabelKey:
'settings.openchamber.keyboardShortcuts.action.toggle_services_menu.label',
},
],
navigation: [
{ id: 'save_file', defaultBinding: 'mod+s', customizable: false, allowsSequenceFallback: true },
{ id: 'save_file', defaultBinding: 'mod+s', customizable: false },
{ id: 'find_in_file', defaultBinding: 'mod+f', customizable: false },
{
id: 'open_go_to_line',
@@ -212,13 +197,6 @@ const SHORTCUT_GROUPS = {
customizable: true,
settingsLabelKey: 'settings.openchamber.keyboardShortcuts.action.open_go_to_line.label',
},
{
id: 'cycle_services_tab',
defaultBinding: 'mod+shift+[',
customizable: true,
settingsLabelKey:
'settings.openchamber.keyboardShortcuts.action.cycle_services_tab.label',
},
],
application: [
{
@@ -228,7 +206,6 @@ const SHORTCUT_GROUPS = {
settingsLabelKey:
'settings.openchamber.keyboardShortcuts.action.open_command_palette.label',
},
{ id: 'open_status', defaultBinding: 'mod+shift+o', customizable: false },
{
id: 'open_settings',
defaultBinding: 'mod+comma',
@@ -237,17 +214,16 @@ const SHORTCUT_GROUPS = {
},
{
id: 'open_help',
defaultBinding: 'mod+.',
defaultBinding: 'mod+k h',
customizable: true,
settingsLabelKey: 'settings.openchamber.keyboardShortcuts.action.open_help.label',
},
{
id: 'cycle_theme',
defaultBinding: 'mod+/',
defaultBinding: 'mod+k c',
customizable: true,
settingsLabelKey: 'settings.openchamber.keyboardShortcuts.action.cycle_theme.label',
},
{ id: 'toggle_memory_debug', defaultBinding: 'mod+shift+d', customizable: false },
],
} as const satisfies Record<ShortcutCategory, readonly ShortcutConfig[]>;
+2
View File
@@ -8,6 +8,8 @@ export {
keyToShortcutToken,
normalizeCombo,
parseShortcut,
resolveShortcutEventDigit,
resolveShortcutEventKey,
UNASSIGNED_SHORTCUT,
} from './bindings';
export type { ShortcutCombo } from './bindings';
@@ -39,6 +39,14 @@ export class ShortcutRegistry {
return this.handlers.get(actionId)?.[0]?.handler;
}
/** Runs an action outside keyboard dispatch (command palette). Bypasses
suspension: the invoking surface, not the keyboard, owns the gesture. */
invoke(actionId: ShortcutActionId): boolean {
const handler = this.handlers.get(actionId)?.[0]?.handler;
if (!handler) return false;
return handler(new KeyboardEvent('keydown')) !== false;
}
/** Temporarily disables every registered application shortcut. */
suspend(): () => void {
this.suspensionCount += 1;
+26 -11
View File
@@ -49,13 +49,31 @@ describe('shortcut schema', () => {
))).toBe(true);
});
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');
test('keeps the mod+k leader for open/go actions', () => {
expect(getShortcutAction('open_draft_project_picker')?.defaultBinding).toBe('mod+k p');
expect(getShortcutAction('open_draft_worktree_picker')?.defaultBinding).toBe('mod+k g');
expect(getShortcutAction('open_session_list')?.defaultBinding).toBe('mod+k l');
expect(getShortcutAction('open_timeline_dialog')?.defaultBinding).toBe('mod+k t');
expect(getShortcutAction('toggle_prompt_navigator')?.defaultBinding).toBe('mod+k n');
expect(getShortcutAction('toggle_services_menu')?.defaultBinding).toBe('mod+k i');
expect(getShortcutAction('open_help')?.defaultBinding).toBe('mod+k h');
expect(getShortcutAction('cycle_theme')?.defaultBinding).toBe('mod+k c');
expect(getShortcutAction('focus_input')?.category).toBe('session');
});
test('splits the held digit prefixes between session tabs and surfaces', () => {
expect(getShortcutAction('switch_session_tab')?.defaultBinding).toBe('mod');
expect(getShortcutAction('switch_context_surface')?.defaultBinding).toBe('mod+alt');
});
test('every action ships with a default binding', () => {
// Palette-only commands live outside this schema entirely; an action in
// the schema without a binding would be dead weight in Settings.
for (const action of SHORTCUT_SCHEMA) {
expect(getEffectiveShortcutCombo(action.id)).not.toBe('');
}
});
test('preserves valid overrides and falls back from malformed bindings', () => {
expect(getEffectiveShortcutCombo('new_chat', { new_chat: 'mod+k' })).toBe('mod+k');
expect(getEffectiveShortcutCombo('new_chat', { new_chat: 'mod+k x y' })).toBe('mod+n');
@@ -73,10 +91,8 @@ describe('shortcut schema', () => {
.find((conflict) => conflict.action.id === 'find_in_file');
const internalPrefixConflict = getShortcutBindingConflicts('new_chat', 'mod+s x')
.find((conflict) => conflict.action.id === 'save_file');
const contextualPrefixConflict = getShortcutBindingConflicts('focus_input', 'mod+l l')
.find((conflict) => conflict.action.id === 'add_selection_to_chat');
const contextualLeaderConflict = getShortcutBindingConflicts('add_selection_to_chat', 'mod+s')
.find((conflict) => conflict.action.id === 'open_draft_project_picker');
const leaderPrefixConflict = getShortcutBindingConflicts('new_chat', 'mod+k')
.find((conflict) => conflict.action.id === 'open_session_list');
const blockingPrefixConflict = getShortcutBindingConflicts('new_chat', 'mod+p x')
.find((conflict) => conflict.action.id === 'open_command_palette');
@@ -84,10 +100,9 @@ describe('shortcut schema', () => {
expect(customizableConflict?.action.customizable).toBe(true);
expect(internalConflict?.kind).toBe('exact');
expect(internalConflict?.action.customizable).toBe(false);
expect(internalPrefixConflict?.kind).toBe('contextual-prefix');
expect(internalPrefixConflict?.kind).toBe('prefix');
expect(internalPrefixConflict?.action.customizable).toBe(false);
expect(contextualPrefixConflict?.kind).toBe('contextual-prefix');
expect(contextualLeaderConflict?.kind).toBe('contextual-prefix');
expect(leaderPrefixConflict?.kind).toBe('prefix');
expect(blockingPrefixConflict?.kind).toBe('prefix');
});
});
+6 -25
View File
@@ -15,29 +15,14 @@ export type ShortcutAction = (typeof SHORTCUT_SCHEMA)[number];
export type ShortcutActionId = ShortcutAction['id'];
export type ShortcutCategory = ShortcutAction['category'];
export type CustomizableShortcutAction = Extract<ShortcutAction, { customizable: true }>;
/** 'contextual-prefix' is kept in the union for the recording dialog's
messaging even though no default layout produces it any more. */
export type ShortcutBindingConflictKind = ShortcutConflict | 'contextual-prefix';
export type ShortcutBindingConflict = {
action: ShortcutAction;
kind: ShortcutBindingConflictKind;
};
function allowsContextualPrefix(
action: ShortcutAction,
combo: ShortcutCombo,
candidate: ShortcutAction,
candidateCombo: ShortcutCombo,
): boolean {
const chordCount = parseShortcut(combo)?.chords.length;
const candidateChordCount = parseShortcut(candidateCombo)?.chords.length;
if (chordCount === 1 && candidateChordCount === 2) {
return 'allowsSequenceFallback' in action && action.allowsSequenceFallback;
}
if (chordCount === 2 && candidateChordCount === 1) {
return 'allowsSequenceFallback' in candidate && candidate.allowsSequenceFallback;
}
return false;
}
export function getShortcutAction(id: string): ShortcutAction | undefined {
return SHORTCUT_SCHEMA.find((action) => action.id === id);
}
@@ -54,7 +39,8 @@ export function getEffectiveShortcutCombo(
): ShortcutCombo {
const action = getShortcutAction(actionId);
if (!action) return '';
if (!action.customizable) return action.defaultBinding;
const defaultBinding = action.defaultBinding === UNASSIGNED_SHORTCUT ? '' : action.defaultBinding;
if (!action.customizable) return defaultBinding;
const override = overrides?.[actionId];
if (typeof override === 'string') {
@@ -63,7 +49,7 @@ export function getEffectiveShortcutCombo(
if (isValidShortcutCombo(normalized)) return normalized;
}
return action.defaultBinding;
return defaultBinding;
}
export function getEffectiveShortcutPrefix(
@@ -100,12 +86,7 @@ export function getShortcutBindingConflicts(
: getEffectiveShortcutCombo(candidate.id, overrides);
const kind = getShortcutConflict(combo, candidateCombo);
if (!kind) continue;
conflicts.push({
action: candidate,
kind: kind === 'prefix' && allowsContextualPrefix(action, combo, candidate, candidateCombo)
? 'contextual-prefix'
: kind,
});
conflicts.push({ action: candidate, kind });
}
return conflicts;
}
+10 -1
View File
@@ -2412,7 +2412,7 @@ export const useUIStore = create<UIStore>()(
{
name: 'ui-store',
storage: createDeferredSafeJSONStorage(),
version: 17,
version: 18,
migrate: (persistedState, version) => {
if (!persistedState || typeof persistedState !== 'object') {
return persistedState;
@@ -2433,6 +2433,15 @@ export const useUIStore = create<UIStore>()(
delete state.expandedEditorToolbar;
}
// v17 -> v18: the default shortcut layout was redesigned around the
// mod+k leader and the held digit prefixes. Old overrides were
// recorded against the previous defaults (e.g. a bare 'mod' surface
// prefix now collides with session tabs), so custom bindings start
// fresh on the new system.
if (version < 18) {
delete state.shortcutOverrides;
}
// v13 -> v14: the separate 'preview' surface merged into 'browser'.
// Stored preview tabs keep their URL and become browser tabs; their
// id encodes the mode, so it is rebuilt rather than left dangling.
@@ -172,12 +172,12 @@ checks:
preconditions:
- Open a draft session with project and worktree selectors mounted.
steps:
- Trigger Mod + S, P and verify the project picker opens.
- Trigger Mod + K, P and verify the project picker opens.
- Press Escape once and verify it closes.
- Trigger Mod + S, G and verify the worktree picker opens.
- Trigger Mod + K, G and verify the worktree picker opens.
- Press Escape once and verify it closes.
assertions:
- A contextual Mod + S owner yields when its context is inactive.
- The Mod + K leader arms without a visible menu and completes on the second key.
- Each sequence opens only its target picker.
- One non-IME Escape closes either controlled picker.
evidence: