From 2c3c2a88e803479d90eb8c1e8b65c4a74eff36b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Almeida?= <57680069+zeval@users.noreply.github.com> Date: Thu, 17 Sep 2026 20:00:34 +0200 Subject: [PATCH] feat: make task actions keyboard accessible (#3767) --- apps/web/components/command-panel-dialog.tsx | 2 +- apps/web/components/command-panel-footer.tsx | 19 +- apps/web/components/command-panel-results.tsx | 16 +- .../command-panel-task-actions.test.tsx | 156 +++++++++++++ apps/web/components/command-panel.tsx | 88 +++++++- apps/web/components/session-commands.test.tsx | 15 +- apps/web/components/session-commands.tsx | 28 +-- .../components/task-command-choices.test.tsx | 31 +++ apps/web/components/task-command-choices.tsx | 184 ++++++++++++++++ apps/web/components/task-command-items.tsx | 93 ++++++++ apps/web/components/task-commands.test.tsx | 82 +++++++ apps/web/components/task-commands.tsx | 123 +++++++++++ .../task/task-move-context-menu.tsx | 16 +- apps/web/components/task/task-page-inner.tsx | 2 + .../task/task-palette-move.test.tsx | 50 +++++ .../task/task-row-action-availability.ts | 12 + .../components/task/task-session-sidebar.tsx | 10 +- .../task/task-switcher-context-menu-items.tsx | 11 +- .../task/use-workflow-move-submit.ts | 40 ++++ .../components/task/workflow-move-options.tsx | 46 ++-- .../task/workflow-move-shortcut.test.tsx | 78 +++++++ .../task/workflow-step-disclosure-actions.tsx | 18 +- ...workflow-step-disclosure-move-controls.tsx | 13 +- .../task/workflow-stepper-keyboard.test.tsx | 60 ++++- apps/web/components/task/workflow-stepper.tsx | 17 +- .../task/command-palette-archive.spec.ts | 8 + .../task/command-palette-task-actions.spec.ts | 145 ++++++++++++ ...obile-command-palette-task-actions.spec.ts | 70 ++++++ ...obile-workflow-step-move-overrides.spec.ts | 4 +- .../workflow-step-move-overrides.spec.ts | 4 +- apps/web/hooks/use-command-children.ts | 70 ++++++ apps/web/lib/commands/search.test.ts | 34 +++ apps/web/lib/commands/search.ts | 27 ++- apps/web/lib/commands/types.ts | 6 + docs/plans/task-keyboard-actions/plan.md | 208 ++++++++++++++++++ .../task-01-move-shortcut.md | 170 ++++++++++++++ .../task-02-palette-task-actions.md | 182 +++++++++++++++ docs/public/tasks-and-workflows.md | 21 ++ docs/public/use-kandev.md | 6 + .../requirements/task-keyboard-actions.md | 90 ++++++++ .../system-design/task-keyboard-actions.md | 125 +++++++++++ 41 files changed, 2256 insertions(+), 124 deletions(-) create mode 100644 apps/web/components/command-panel-task-actions.test.tsx create mode 100644 apps/web/components/task-command-choices.test.tsx create mode 100644 apps/web/components/task-command-choices.tsx create mode 100644 apps/web/components/task-command-items.tsx create mode 100644 apps/web/components/task-commands.test.tsx create mode 100644 apps/web/components/task-commands.tsx create mode 100644 apps/web/components/task/task-palette-move.test.tsx create mode 100644 apps/web/components/task/task-row-action-availability.ts create mode 100644 apps/web/components/task/use-workflow-move-submit.ts create mode 100644 apps/web/components/task/workflow-move-shortcut.test.tsx create mode 100644 apps/web/e2e/tests/task/command-palette-task-actions.spec.ts create mode 100644 apps/web/e2e/tests/task/mobile-command-palette-task-actions.spec.ts create mode 100644 apps/web/hooks/use-command-children.ts create mode 100644 docs/plans/task-keyboard-actions/plan.md create mode 100644 docs/plans/task-keyboard-actions/task-01-move-shortcut.md create mode 100644 docs/plans/task-keyboard-actions/task-02-palette-task-actions.md create mode 100644 docs/specs/tasks/requirements/task-keyboard-actions.md create mode 100644 docs/specs/tasks/system-design/task-keyboard-actions.md diff --git a/apps/web/components/command-panel-dialog.tsx b/apps/web/components/command-panel-dialog.tsx index 76e2f13979..1a8cf8a3ae 100644 --- a/apps/web/components/command-panel-dialog.tsx +++ b/apps/web/components/command-panel-dialog.tsx @@ -13,7 +13,7 @@ export function CommandPanelDialog(props: ComponentProps) {props.children} diff --git a/apps/web/components/command-panel-footer.tsx b/apps/web/components/command-panel-footer.tsx index bfe81200d0..f933906c13 100644 --- a/apps/web/components/command-panel-footer.tsx +++ b/apps/web/components/command-panel-footer.tsx @@ -28,6 +28,7 @@ import { CommandsListContent, FileSearchContent, MODE_COMMANDS, + MODE_COMMAND_CHILDREN, MODE_SEARCH_CONTENT, MODE_SEARCH_FILES, MODE_SEARCH_TASKS, @@ -37,6 +38,7 @@ import { export { MODE_COMMANDS, + MODE_COMMAND_CHILDREN, MODE_SEARCH_CONTENT, MODE_SEARCH_FILES, MODE_SEARCH_TASKS, @@ -67,7 +69,7 @@ function getEnterLabel(t: TFunction, mode: CommandPanelMode) { } function getModeLabel(t: TFunction, mode: CommandPanelMode, inputCommand: CommandItemType | null) { - if (mode === "input") return inputCommand?.label; + if (mode === "input" || mode === MODE_COMMAND_CHILDREN) return inputCommand?.label; if (mode === MODE_SEARCH_TASKS) return t("common:tasks"); if (mode === MODE_SEARCH_FILES) return t("common:files"); if (mode === MODE_SEARCH_CONTENT) return t("common:contents"); @@ -105,13 +107,14 @@ function CommandPanelFooter({ mode }: { mode: CommandPanelMode }) { {/* A key name, not copy — it labels the physical key. */} esc - {t("common:close")} + {t(mode === MODE_COMMAND_CHILDREN ? "common:back" : "common:close")} ); } export type CommandPanelViewProps = { + onEscapeKeyDown?: (event: KeyboardEvent) => void; open: boolean; setOpen: (open: boolean) => void; mode: CommandPanelMode; @@ -180,8 +183,8 @@ function CommandPanelInputHeader({ {!isTopLevelMode && (