Files
vibe-kanban/frontend/src/keyboard/registry.ts
Louis Knight-Webb fef06cf00e Done! I've successfully removed arrow key navigation while preserving hjkl shortcuts. Here's what changed: (#946)
## Changes Made:

1. **[frontend/src/keyboard/registry.ts](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/155e-remove-keyboard/frontend/src/keyboard/registry.ts)**
   - Removed arrow keys from navigation bindings, kept only hjkl
   - Fixed Enter key conflict by changing fullscreen toggle to ⌘/Ctrl+Enter

2. **[docs/configuration-customisation/keyboard-shortcuts.mdx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/155e-remove-keyboard/docs/configuration-customisation/keyboard-shortcuts.mdx)**
   - Updated Board Navigation section to show hjkl only
   - Added fullscreen toggle documentation

All type checks pass ✓
2025-10-06 10:51:26 +01:00

202 lines
4.2 KiB
TypeScript

export enum Scope {
GLOBAL = 'global',
DIALOG = 'dialog',
KANBAN = 'kanban',
PROJECTS = 'projects',
EDIT_COMMENT = 'edit-comment',
APPROVALS = 'approvals',
}
export enum Action {
EXIT = 'exit',
CREATE = 'create',
SUBMIT = 'submit',
FOCUS_SEARCH = 'focus_search',
NAV_UP = 'nav_up',
NAV_DOWN = 'nav_down',
NAV_LEFT = 'nav_left',
NAV_RIGHT = 'nav_right',
OPEN_DETAILS = 'open_details',
SHOW_HELP = 'show_help',
TOGGLE_FULLSCREEN = 'toggle_fullscreen',
DELETE_TASK = 'delete_task',
APPROVE_REQUEST = 'approve_request',
DENY_APPROVAL = 'deny_approval',
}
export interface KeyBinding {
action: Action;
keys: string | string[];
scopes?: Scope[];
description: string;
group?: string;
}
export const keyBindings: KeyBinding[] = [
// Exit/Close actions
{
action: Action.EXIT,
keys: 'esc',
scopes: [Scope.DIALOG],
description: 'Close dialog or blur input',
group: 'Dialog',
},
{
action: Action.EXIT,
keys: 'esc',
scopes: [Scope.KANBAN],
description: 'Close panel or navigate to projects',
group: 'Navigation',
},
{
action: Action.EXIT,
keys: 'esc',
scopes: [Scope.EDIT_COMMENT],
description: 'Cancel comment',
group: 'Comments',
},
// Creation actions
{
action: Action.CREATE,
keys: 'c',
scopes: [Scope.KANBAN],
description: 'Create new task',
group: 'Kanban',
},
{
action: Action.CREATE,
keys: 'c',
scopes: [Scope.PROJECTS],
description: 'Create new project',
group: 'Projects',
},
// Submit actions
{
action: Action.SUBMIT,
keys: 'enter',
scopes: [Scope.DIALOG],
description: 'Submit form or confirm action',
group: 'Dialog',
},
// Navigation actions
{
action: Action.FOCUS_SEARCH,
keys: 'slash',
scopes: [Scope.KANBAN],
description: 'Focus search',
group: 'Navigation',
},
{
action: Action.NAV_UP,
keys: 'k',
scopes: [Scope.KANBAN],
description: 'Move up within column',
group: 'Navigation',
},
{
action: Action.NAV_DOWN,
keys: 'j',
scopes: [Scope.KANBAN],
description: 'Move down within column',
group: 'Navigation',
},
{
action: Action.NAV_LEFT,
keys: 'h',
scopes: [Scope.KANBAN],
description: 'Move to previous column',
group: 'Navigation',
},
{
action: Action.NAV_RIGHT,
keys: 'l',
scopes: [Scope.KANBAN],
description: 'Move to next column',
group: 'Navigation',
},
{
action: Action.OPEN_DETAILS,
keys: 'enter',
scopes: [Scope.KANBAN],
description: 'Open selected task details',
group: 'Kanban',
},
// Global actions
{
action: Action.SHOW_HELP,
keys: 'shift+slash',
scopes: [Scope.GLOBAL],
description: 'Show keyboard shortcuts help',
group: 'Global',
},
// Task panel actions
{
action: Action.TOGGLE_FULLSCREEN,
keys: ['meta+enter', 'ctrl+enter'],
scopes: [Scope.KANBAN],
description: 'Toggle fullscreen view',
group: 'Task Details',
},
// Task actions
{
action: Action.DELETE_TASK,
keys: 'd',
scopes: [Scope.KANBAN],
description: 'Delete selected task',
group: 'Task Details',
},
// Approval actions
{
action: Action.APPROVE_REQUEST,
keys: 'enter',
scopes: [Scope.APPROVALS],
description: 'Approve pending approval request',
group: 'Approvals',
},
{
action: Action.DENY_APPROVAL,
keys: ['meta+enter', 'ctrl+enter'],
scopes: [Scope.APPROVALS],
description: 'Deny pending approval request',
group: 'Approvals',
},
];
/**
* Get keyboard bindings for a specific action and scope
*/
export function getKeysFor(action: Action, scope?: Scope): string[] {
const bindings = keyBindings
.filter(
(binding) =>
binding.action === action &&
(!scope || !binding.scopes || binding.scopes.includes(scope))
)
.flatMap((binding) =>
Array.isArray(binding.keys) ? binding.keys : [binding.keys]
);
return bindings;
}
/**
* Get binding info for a specific action and scope
*/
export function getBindingFor(
action: Action,
scope?: Scope
): KeyBinding | undefined {
return keyBindings.find(
(binding) =>
binding.action === action &&
(!scope || !binding.scopes || binding.scopes.includes(scope))
);
}