+
+
Keyboard Shortcuts
+
+ Capture a new key combo, save it, and the runtime/help/palette bindings update together.
+
+
+
+
+ {actions.map((action) => {
+ const effective = getEffectiveShortcutCombo(action.id, shortcutOverrides);
+ const draft = draftByAction[action.id];
+ const displayCombo = draft ?? effective;
+ const hasDraft = typeof draft === 'string' && normalizeCombo(draft) !== normalizeCombo(effective);
+
+ return (
+
+
+
+
{action.label}
+ {action.description && (
+
{action.description}
+ )}
+
+
+ {
+ setCapturingActionId(action.id);
+ setErrorText('');
+ }}
+ onBlur={() => {
+ if (capturingActionId === action.id) {
+ setCapturingActionId(null);
+ }
+ }}
+ onKeyDown={(event) => {
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (event.key === 'Escape') {
+ setCapturingActionId(null);
+ return;
+ }
+
+ const combo = keyboardEventToCombo(event);
+ if (!combo) {
+ return;
+ }
+
+ setDraftByAction((current) => ({
+ ...current,
+ [action.id]: combo,
+ }));
+ setCapturingActionId(null);
+ setPendingOverwrite(null);
+ setErrorText('');
+ }}
+ className="w-52"
+ />
+ {
+ const next = draftByAction[action.id];
+ if (!next) {
+ setErrorText('Capture a shortcut first.');
+ return;
+ }
+ saveCombo(action.id, next);
+ }}
+ disabled={!hasDraft}
+ >
+ Save
+
+ resetOne(action.id)}>
+ Reset
+
+
+
+
+ );
+ })}
+
+
+ {pendingOverwrite && (
+
+
+ This combo is already used by another shortcut. Overwrite and clear that other mapping?
+
+
+ Overwrite
+ setPendingOverwrite(null)}>Cancel
+
+
+ )}
+
+ {errorText && (
+
+ {errorText}
+
+ )}
+
+ {warningText && (
+
+ {warningText}
+
+ )}
+
+
+ {
+ resetAllShortcutOverrides();
+ setDraftByAction({});
+ setPendingOverwrite(null);
+ setErrorText('');
+ setWarningText('');
+ }}
+ >
+ Reset all shortcuts
+
+
+
+ );
+};
diff --git a/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx b/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx
index 4b3490d2..a1d54552 100644
--- a/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx
+++ b/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx
@@ -10,6 +10,7 @@ import { NotificationSettings } from './NotificationSettings';
import { GitHubSettings } from './GitHubSettings';
import { VoiceSettings } from './VoiceSettings';
import { OpenCodeCliSettings } from './OpenCodeCliSettings';
+import { KeyboardShortcutsSettings } from './KeyboardShortcutsSettings';
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
import { useDeviceInfo } from '@/lib/device';
import { isVSCodeRuntime, isWebRuntime } from '@/lib/desktop';
@@ -65,6 +66,8 @@ export const OpenChamberPage: React.FC