feat(app): cycle agent modes with Shift+Tab
This commit is contained in:
@@ -12,7 +12,8 @@ export type MessageInputKeyboardActionKind =
|
||||
| "dictation-cancel"
|
||||
| "dictation-confirm"
|
||||
| "voice-toggle"
|
||||
| "voice-mute-toggle";
|
||||
| "voice-mute-toggle"
|
||||
| "mode-cycle";
|
||||
|
||||
export type KeyboardActionId =
|
||||
| "agent.interrupt"
|
||||
|
||||
@@ -9,6 +9,7 @@ export type KeyboardActionId =
|
||||
| "message-input.dictation-confirm"
|
||||
| "message-input.voice-toggle"
|
||||
| "message-input.voice-mute-toggle"
|
||||
| "message-input.mode-cycle"
|
||||
| "workspace.tab.new"
|
||||
| "workspace.tab.close-current"
|
||||
| "workspace.tab.navigate-index"
|
||||
@@ -39,6 +40,7 @@ export type KeyboardActionDefinition =
|
||||
| { id: "message-input.dictation-confirm"; scope: KeyboardActionScope }
|
||||
| { id: "message-input.voice-toggle"; scope: KeyboardActionScope }
|
||||
| { id: "message-input.voice-mute-toggle"; scope: KeyboardActionScope }
|
||||
| { id: "message-input.mode-cycle"; scope: KeyboardActionScope }
|
||||
| { id: "workspace.tab.new"; scope: KeyboardActionScope }
|
||||
| { id: "workspace.tab.close-current"; scope: KeyboardActionScope }
|
||||
| { id: "workspace.tab.navigate-index"; scope: KeyboardActionScope; index: number }
|
||||
|
||||
@@ -299,6 +299,13 @@ describe("keyboard-shortcuts", () => {
|
||||
action: "message-input.action",
|
||||
payload: { kind: "dictation-toggle" },
|
||||
},
|
||||
{
|
||||
name: "routes Shift+Tab to cycle agent mode from the message input",
|
||||
event: { key: "Tab", code: "Tab", shiftKey: true },
|
||||
context: { focusScope: "message-input" },
|
||||
action: "message-input.action",
|
||||
payload: { kind: "mode-cycle" },
|
||||
},
|
||||
{
|
||||
name: "routes space to voice mute toggle outside editable scopes",
|
||||
event: { key: " ", code: "Space" },
|
||||
@@ -427,6 +434,16 @@ describe("keyboard-shortcuts", () => {
|
||||
event: { key: "d", code: "KeyD", metaKey: true },
|
||||
context: { isMac: true, focusScope: "terminal" },
|
||||
},
|
||||
{
|
||||
name: "does not cycle agent mode outside the message input",
|
||||
event: { key: "Tab", code: "Tab", shiftKey: true },
|
||||
context: { focusScope: "other" },
|
||||
},
|
||||
{
|
||||
name: "does not repeat agent mode cycling while Shift+Tab is held",
|
||||
event: { key: "Tab", code: "Tab", shiftKey: true, repeat: true },
|
||||
context: { focusScope: "message-input" },
|
||||
},
|
||||
{
|
||||
name: "does not bind Cmd+Enter as a rebindable message queue shortcut",
|
||||
event: { key: "Enter", code: "Enter", metaKey: true },
|
||||
@@ -570,6 +587,7 @@ describe("keyboard-shortcut help sections", () => {
|
||||
"workspace-tab-close-current": ["alt", "shift", "W"],
|
||||
"workspace-pane-split-right": ["mod", "\\"],
|
||||
"workspace-pane-close": ["mod", "shift", "W"],
|
||||
"cycle-agent-mode": ["shift", "Tab"],
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -617,12 +635,14 @@ describe("keyboard-shortcut help sections", () => {
|
||||
const projects = sections.find((section) => section.id === "projects");
|
||||
const panels = sections.find((section) => section.id === "panels");
|
||||
const openProject = findRow(sections, "new-agent");
|
||||
const cycleAgentMode = findRow(sections, "cycle-agent-mode");
|
||||
const showShortcuts = findRow(sections, "show-shortcuts");
|
||||
|
||||
expect(projects?.titleKey).toBe("settings.shortcuts.sections.projects");
|
||||
expect(panels?.titleKey).toBe("settings.shortcuts.sections.panels");
|
||||
expect(openProject?.labelKey).toBe("settings.shortcuts.help.openProject");
|
||||
expect(openProject?.label).toBe("Open project");
|
||||
expect(cycleAgentMode?.labelKey).toBe("settings.shortcuts.help.cycleAgentMode");
|
||||
expect(showShortcuts?.noteKey).toBe("settings.shortcuts.helpNotes.showKeyboardShortcuts");
|
||||
});
|
||||
|
||||
|
||||
@@ -152,6 +152,7 @@ const SHORTCUT_HELP_LABEL_KEYS: Record<string, string> = {
|
||||
"toggle-focus": "settings.shortcuts.help.toggleFocusMode",
|
||||
"cycle-theme": "settings.shortcuts.help.cycleTheme",
|
||||
"focus-message-input": "settings.shortcuts.help.focusMessageInput",
|
||||
"cycle-agent-mode": "settings.shortcuts.help.cycleAgentMode",
|
||||
"voice-toggle": "settings.shortcuts.help.toggleVoiceMode",
|
||||
"dictation-toggle": "settings.shortcuts.help.startStopDictation",
|
||||
"agent-interrupt": "settings.shortcuts.help.interruptAgent",
|
||||
@@ -910,6 +911,20 @@ const SHORTCUT_BINDINGS: readonly ShortcutBinding[] = [
|
||||
keys: ["mod", "L"],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "message-input-mode-cycle-shift-tab",
|
||||
action: "message-input.action",
|
||||
combo: "Shift+Tab",
|
||||
repeat: false,
|
||||
when: { commandCenter: false, focusScope: "message-input" },
|
||||
payload: { type: "message-input", kind: "mode-cycle" },
|
||||
help: {
|
||||
id: "cycle-agent-mode",
|
||||
section: "agent-input",
|
||||
label: "Cycle agent mode",
|
||||
keys: ["shift", "Tab"],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "message-input-voice-toggle-cmd-shift-d-mac",
|
||||
action: "message-input.action",
|
||||
|
||||
@@ -276,6 +276,7 @@ describe("routeKeyboardShortcut — message-input.action", () => {
|
||||
["dictation-confirm", "message-input.dictation-confirm"],
|
||||
["voice-toggle", "message-input.voice-toggle"],
|
||||
["voice-mute-toggle", "message-input.voice-mute-toggle"],
|
||||
["mode-cycle", "message-input.mode-cycle"],
|
||||
] as const)("kind=%s → dispatch %s", (kind, id) => {
|
||||
expect(
|
||||
routeKeyboardShortcut({ action: "message-input.action", payload: { kind } }, makeCtx()),
|
||||
|
||||
@@ -82,6 +82,7 @@ const MESSAGE_INPUT_DISPATCH: Record<
|
||||
"dictation-confirm": { id: "message-input.dictation-confirm", scope: "message-input" },
|
||||
"voice-toggle": { id: "message-input.voice-toggle", scope: "message-input" },
|
||||
"voice-mute-toggle": { id: "message-input.voice-mute-toggle", scope: "message-input" },
|
||||
"mode-cycle": { id: "message-input.mode-cycle", scope: "message-input" },
|
||||
};
|
||||
|
||||
function hasPayloadKey<K extends "index" | "delta" | "kind">(
|
||||
|
||||
Reference in New Issue
Block a user