import type { AutoCommitConfig, AutoRenameConfig, MultiRepoConfig, ResolvedKeymapConfig, } from './types' import * as actions from './actions' import { KeymapBuilder } from './keymap-builder' export const DEFAULT_AUTO_COMMIT_CONFIG: AutoCommitConfig = { enabled: false, models: { claude: 'claude-haiku-4-5', codex: 'gpt-5-mini', }, timeoutMs: 60_000, } export const DEFAULT_AUTO_RENAME_CONFIG: AutoRenameConfig = { enabled: true, models: { claude: 'claude-haiku-4-5', codex: 'gpt-5-mini', }, timeoutMs: 15_000, } export const DEFAULT_MULTI_REPO_CONFIG: MultiRepoConfig = { enabled: true, maxDepth: 1, } /** * Build the default keymap — 1:1 transcription of all existing handlers. */ export function getDefaultKeymapConfig(): ResolvedKeymapConfig { const kb = new KeymapBuilder() .leader('') .timeout(300) // ----------------------------------------------------------------------- // Navigation mode // ----------------------------------------------------------------------- .mode('navigation', (m) => m .map('', actions.quit, 'Quit') .map('', actions.newTab, 'New tab') .map('', actions.sessionPicker, 'Session picker') .map('', actions.ctrlZSidebar, 'Focus sidebar') .map('dd', actions.closeTab, 'Close tab') .map('', actions.toggleSidebar, 'Toggle sidebar') .map('', actions.restartTab, 'Restart tab') .map('', actions.snippetPicker, 'Snippet picker') .map('', actions.themePicker, 'Theme picker') .map('', actions.resizeSidebar(-2), 'Sidebar narrower') .map('', actions.resizeSidebar(2), 'Sidebar wider') .map('G', actions.toggleGitPane, 'Toggle git pane') .map('', actions.enterGitMode, 'Enter git mode') .map('', actions.resizeGitPane(-0.05), 'Git pane smaller') .map('', actions.resizeGitPane(0.05), 'Git pane larger') .map('j', actions.nextSidebarItem, 'Next workspace/worktree') .map('k', actions.prevSidebarItem, 'Prev workspace/worktree') .map('l', actions.nextTab, 'Next tab') .map('h', actions.prevTab, 'Prev tab') .map('L', actions.reorderTab(1), 'Move tab right') .map('H', actions.reorderTab(-1), 'Move tab left') .map('J', actions.reorderSession(1), 'Move workspace down') .map('K', actions.reorderSession(-1), 'Move workspace up') .map('r', actions.renameTab, 'Rename tab') .map('i', actions.enterInsert, 'Focus terminal') .map('S', actions.openFlashJump, 'Flash jump') .map('?', actions.helpModal(), 'Help') ) // ----------------------------------------------------------------------- // Terminal-input mode // ----------------------------------------------------------------------- .mode('terminal-input', (m) => m .map('', actions.leaveTerminalInput, 'Leave insert') .map('', actions.toggleSidebarFromInput, 'Toggle sidebar') .map('h', actions.focusPane('left'), 'Focus left') .map('j', actions.focusPane('down'), 'Focus down') .map('k', actions.focusPane('up'), 'Focus up') .map('l', actions.focusPane('right'), 'Focus right') .map('H', actions.resizePane(-1, 'vertical'), 'Shrink ←', { repeatable: true }) .map('L', actions.resizePane(1, 'vertical'), 'Grow →', { repeatable: true }) .map('K', actions.resizePane(-1, 'horizontal'), 'Shrink ↑', { repeatable: true }) .map('J', actions.resizePane(1, 'horizontal'), 'Grow ↓', { repeatable: true }) .map('|', actions.splitVertical, 'Split vertical') .map('-', actions.splitHorizontal, 'Split horizontal') .map('q', actions.closePane, 'Close pane') ) // ----------------------------------------------------------------------- // Session bar: same chords from nav and while typing in the terminal // ----------------------------------------------------------------------- .mode(['navigation', 'terminal-input'], (m) => m .map('b', actions.toggleSessionBar, 'Toggle session bar') .map('u', actions.toggleAIUsage, 'Toggle AI usage') .map('1', actions.switchTabByIndex(1), 'Tab 1') .map('2', actions.switchTabByIndex(2), 'Tab 2') .map('3', actions.switchTabByIndex(3), 'Tab 3') .map('4', actions.switchTabByIndex(4), 'Tab 4') .map('5', actions.switchTabByIndex(5), 'Tab 5') .map('6', actions.switchTabByIndex(6), 'Tab 6') .map('7', actions.switchTabByIndex(7), 'Tab 7') .map('8', actions.switchTabByIndex(8), 'Tab 8') .map('9', actions.switchTabByIndex(9), 'Tab 9') ) // ----------------------------------------------------------------------- // Git mode // ----------------------------------------------------------------------- .mode('git-mode', (m) => m .map('a', actions.gitStageSelected, 'Stage') .map('A', actions.gitStageAll, 'Stage all') .map('d', actions.gitDestructiveSelected, 'Unstage/delete') .map('D', actions.gitUnstageAll, 'Unstage all') .map('e', actions.gitToggleFoldAll, 'Expand/collapse all folds') .map('o', actions.openSelectedGitFileInEditor, 'Open in editor') .map('c', actions.gitCommitOpen, 'Commit') .map('m', actions.openWorktreeMove, 'Move worktree') .map('p', actions.gitPush, 'Push') .map('v', actions.toggleGitDiffView, 'Toggle split/stacked') .map('b', actions.toggleGitReviewBase, 'Review vs base') .map('t', actions.toggleGitFileListMode, 'Toggle flat/tree') .map('T', actions.toggleTreeCompaction, 'Toggle tree compaction') .map(']', actions.shiftGitHeadOffset(-1), 'Newer commit') .map('[', actions.shiftGitHeadOffset(1), 'Older commit') .map('?', actions.helpModal('git-mode'), 'Help') .map('', actions.exitGitMode, 'Exit git') .map('j', actions.selectGitFile(1), 'Next entry') .map('k', actions.selectGitFile(-1), 'Prev entry') .map('', actions.selectGitFileOnly(1), 'Next file') .map('', actions.selectGitFileOnly(-1), 'Prev file') .map('h', actions.toggleSelectedGitFolder, 'Toggle folder') .map('l', actions.toggleSelectedGitFolder, 'Toggle folder') .map('', actions.scrollGitDiff(20), 'Page down') .map('', actions.resizeGitDiffPane(-0.05), 'File bar narrower') .map('', actions.resizeGitDiffPane(-0.05), 'File bar narrower') .map('', actions.resizeGitDiffPane(0.05), 'File bar wider') .map('', actions.scrollGitDiff(-20), 'Page up') .map('', actions.collapseGitSelection, 'Collapse folder') .map('', actions.expandGitSelection, 'Expand folder') .map('', actions.scrollGitDiff(1), 'Scroll down') .map('', actions.scrollGitDiff(-1), 'Scroll up') ) // ----------------------------------------------------------------------- // Modal: help (always in filter mode via Picker) // ----------------------------------------------------------------------- .mode('modal.help.filtering', (m) => m .map('', actions.closeOverlayModal, 'Close') .map('', actions.moveModalSelection(1), 'Next') .map('', actions.moveModalSelection(-1), 'Prev') .map('', actions.moveModalSelection(1)) .map('', actions.moveModalSelection(-1)) .passthrough() ) // ----------------------------------------------------------------------- // Modal: theme-picker (always in filter mode via Picker) // ----------------------------------------------------------------------- .mode('modal.theme-picker.filtering', (m) => m .map('', actions.restoreTheme, 'Cancel') .map('', actions.previewTheme(1), 'Next') .map('', actions.previewTheme(-1), 'Prev') .map('', actions.previewTheme(1)) .map('', actions.previewTheme(-1)) .map('', actions.confirmTheme, 'Confirm') .map('', actions.toggleTransparent, 'Toggle transparent') .map('', actions.toggleMode, 'Toggle light/dark') .passthrough() ) // ----------------------------------------------------------------------- // Modal: ai-usage (info-only) // ----------------------------------------------------------------------- .mode('modal.ai-usage', (m) => m.map('', actions.closeModal, 'Close').map('u', actions.toggleAIUsage, 'Close') ) // ----------------------------------------------------------------------- // Modal: update-available // ----------------------------------------------------------------------- .mode('modal.update-available', (m) => m .map('', actions.closeModal, 'Cancel') .map('l', actions.moveModalSelection(1), 'Next') .map('h', actions.moveModalSelection(-1), 'Prev') .map('', actions.moveModalSelection(1)) .map('', actions.moveModalSelection(-1)) .map('', actions.moveModalSelection(1)) .map('', actions.moveModalSelection(-1)) .map('', actions.moveModalSelection(1)) .map('', actions.moveModalSelection(-1)) .map('', actions.confirmUpdateSelection, 'Confirm') ) // ----------------------------------------------------------------------- // Modal: worktree-move // ----------------------------------------------------------------------- .mode('modal.worktree-move', (m) => m .map('', actions.closeModal, 'Cancel') .map('j', actions.moveModalSelection(1), 'Next') .map('k', actions.moveModalSelection(-1), 'Prev') .map('', actions.moveModalSelection(1)) .map('', actions.moveModalSelection(-1)) .map('', actions.moveModalSelection(1)) .map('', actions.moveModalSelection(-1)) .map('d', actions.toggleWorktreeMoveDelete, 'Toggle delete source') .map('', actions.confirmWorktreeMove, 'Move') ) // ----------------------------------------------------------------------- // Modal: worktree-move recoverable-failure confirmation (stash / conflicts) // ----------------------------------------------------------------------- .mode('modal.worktree-move-confirm', (m) => m .map('', actions.closeModal, 'Cancel') .map('n', actions.closeModal, 'Cancel') .map('', actions.confirmWorktreeMoveRetry, 'Confirm') .map('y', actions.confirmWorktreeMoveRetry, 'Confirm') ) // ----------------------------------------------------------------------- // Modal: standalone worktree delete confirmation (sidebar "Remove worktree") // ----------------------------------------------------------------------- .mode('modal.worktree-delete-confirm', (m) => m .map('', actions.closeModal, 'Cancel') .map('n', actions.closeModal, 'Cancel') .map('', actions.confirmWorktreeDeleteModal, 'Delete worktree') .map('y', actions.confirmWorktreeDeleteModal, 'Delete worktree') ) // ----------------------------------------------------------------------- // Modal: flash-jump (flash.nvim-style label jump) // ----------------------------------------------------------------------- .mode('modal.flash-jump', (m) => m .map('', actions.closeModal, 'Cancel') .map('', actions.closeModal, 'Cancel') .passthrough() ) // ----------------------------------------------------------------------- // Modal: rename-tab // ----------------------------------------------------------------------- .mode('modal.rename-tab', (m) => m .map('', actions.closeModal, 'Cancel') .map('', actions.confirmRenameTab, 'Confirm') .passthrough() ) // ----------------------------------------------------------------------- // Modal: rename-worktree // ----------------------------------------------------------------------- .mode('modal.rename-worktree', (m) => m .map('', actions.closeModal, 'Cancel') .map('', actions.confirmRenameWorktree, 'Confirm') .passthrough() ) // ----------------------------------------------------------------------- // Modal: new-tab (always in filter mode via Picker) // ----------------------------------------------------------------------- .mode('modal.new-tab.command-edit', (m) => m .map('', actions.cancelNewTabModal, 'Cancel') .map('', actions.launchSelectedAssistant, 'Launch') .map('', actions.toggleNewTabWorktree, 'WT') .map('', actions.deleteSelectedWorktree, 'Delete WT') .map('', actions.switchField, 'Next field') .map('', actions.moveModalSelection(1), 'Next') .map('', actions.moveModalSelection(-1), 'Prev') .map('', actions.moveModalSelection(1)) .map('', actions.moveModalSelection(-1)) .map('', actions.editSelectedAssistant, 'Edit') .passthrough() ) .mode('modal.new-tab.editing-command', (m) => m .map('', actions.cancelEditCustomCommand, 'Cancel') .map('', actions.saveCustomCommand, 'Save') .passthrough() ) // ----------------------------------------------------------------------- // Modal: new-tab worktree delete confirmation dialog // ----------------------------------------------------------------------- .mode('modal.new-tab.worktree-delete-confirm', (m) => m .map('', actions.cancelDeleteWorktree, 'Cancel') .map('n', actions.cancelDeleteWorktree, 'Cancel') .map('', actions.confirmDeleteWorktree, 'Delete worktree') .map('y', actions.confirmDeleteWorktree, 'Delete worktree') ) // ----------------------------------------------------------------------- // Modal: session-picker (always in filter mode via Picker) // ----------------------------------------------------------------------- .mode('modal.session-picker.filtering', (m) => m .map('', actions.sessionPickerEscape, 'Cancel') .map('', actions.confirmSelectedSession, 'Open') .map('', actions.moveModalSelection(1), 'Next') .map('', actions.moveModalSelection(-1), 'Prev') .map('', actions.moveModalSelection(1)) .map('', actions.moveModalSelection(-1)) .passthrough() ) // ----------------------------------------------------------------------- // Modal: session-name // ----------------------------------------------------------------------- .mode('modal.session-name', (m) => m .map('', actions.backToSessionPicker, 'Cancel') .map('', actions.confirmSessionRename, 'Confirm') .passthrough() ) // ----------------------------------------------------------------------- // Modal: create-session // ----------------------------------------------------------------------- .mode('modal.create-session', (m) => m .map('', actions.createSessionEscape, 'Cancel') .map('', actions.switchField, 'Next field') .map('', actions.confirmCreateSession, 'Confirm') .map('', actions.moveModalSelection(1), 'Next') .map('', actions.moveModalSelection(-1), 'Prev') .map('', actions.moveModalSelection(1)) .map('', actions.moveModalSelection(-1)) .passthrough() ) // ----------------------------------------------------------------------- // Modal: snippet-picker (always in filter mode via Picker) // ----------------------------------------------------------------------- .mode('modal.snippet-picker.filtering', (m) => m .map('', actions.closeModal, 'Cancel') .map('', actions.snippetFilterPaste, 'Send') .map('', actions.snippetFilterPasteToGroup, 'Send to group') .map('', actions.moveModalSelection(1), 'Next') .map('', actions.moveModalSelection(-1), 'Prev') .map('', actions.openSelectedSnippetSourceInEditor, 'Open source file') .map('', actions.moveModalSelection(1)) .map('', actions.moveModalSelection(-1)) .passthrough() ) // ----------------------------------------------------------------------- // Modal: snippet-editor // ----------------------------------------------------------------------- .mode('modal.snippet-editor', (m) => m .map('', actions.backToSnippetPicker, 'Cancel') .map('', actions.switchField, 'Next field') .map('', actions.saveSnippetEditor, 'Save') .map('', actions.moveModalSelection(1), 'Next') .map('', actions.moveModalSelection(-1), 'Prev') .map('', actions.moveModalSelection(1)) .map('', actions.moveModalSelection(-1)) .passthrough() ) // ----------------------------------------------------------------------- // Modal: split-picker // ----------------------------------------------------------------------- .mode('modal.split-picker', (m) => m .map('', actions.closeModal, 'Cancel') .map('', actions.moveModalSelection(1), 'Next') .map('', actions.moveModalSelection(-1), 'Prev') .map('', actions.moveModalSelection(1)) .map('', actions.moveModalSelection(-1)) .map('', actions.confirmSplit, 'Confirm') .passthrough() ) // ----------------------------------------------------------------------- // Modal: git-commit // ----------------------------------------------------------------------- .mode('modal.git-commit', (m) => m .map('', actions.gitCommitCancel, 'Cancel') .map('', actions.gitCommitSubmit, 'Commit') .map('', actions.gitCommitEnterConfirm, 'Auto-commit (stage all)') .map('', actions.switchField, 'Next field') .map('', actions.gitCommitReturnKey, 'Newline / confirm') .passthrough() ) // ----------------------------------------------------------------------- // Modal: git-commit confirm (auto-commit stage all + commit) // ----------------------------------------------------------------------- .mode('modal.git-commit.confirm', (m) => m .map('', actions.gitCommitLeaveConfirm, 'Cancel auto-commit') .map('', actions.gitCommitAutoAccept, 'Stage all + commit') .map('', actions.gitCommitAutoAccept, 'Stage all + commit') .map('', actions.switchField, 'Next field') .passthrough() ) // ----------------------------------------------------------------------- // Modal: git-commit generating (waiting for LLM) // ----------------------------------------------------------------------- .mode('modal.git-commit.generating', (m) => m.map('', actions.gitCommitLeaveGenerating, 'Cancel generation') ) return kb._build() }