/vim toggles a pane with a beginner cheat sheet for Neovim + NvChad keys. /vim <word> filters it.

A Claude Code pane with a beginner cheat sheet for Neovim + NvChad v2.5 (leader = Space).
/vim opens or closes the "Vim / NvChad keys" pane/vim <word> shows only the lines with that word, e.g. /vim bufferSections: modes and saving, moving, editing, search, files (NvChad), windows, terminal and help. About 40 columns wide.
/plugin install vim-keys --marketplace AliakbarNateghi/cc-vim-keys
Answer y to add the marketplace, then pick a scope.
hooks/register.tsx 111 lines1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4const PANE = 'vim-keys'
5const TITLE = 'Vim / NvChad keys'
6const filter = atom({ plugin: 'vim-keys', key: 'filter' } as const, '')
7
8// Checked against NvChad v2.5 lua/nvchad/mappings.lua. Leader = Space.
9export const SHEET: [string, [string, string][]][] = [
10 ['Modes and saving', [
11 ['i', 'insert mode'],
12 ['Esc', 'back to normal mode'],
13 [':w', 'save'],
14 [':q', 'quit'],
15 [':wq', 'save and quit'],
16 [':q!', 'quit, drop changes'],
17 ]],
18 ['Moving', [
19 ['h j k l', 'left down up right'],
20 ['w b', 'next / prev word'],
21 ['gg G', 'top / bottom of file'],
22 ['0 $', 'start / end of line'],
23 ['Ctrl-d Ctrl-u', 'half page down / up'],
24 ['%', 'jump to matching bracket'],
25 ]],
26 ['Editing', [
27 ['dd', 'delete (cut) line'],
28 ['yy', 'copy (yank) line'],
29 ['p', 'paste after cursor'],
30 ['u', 'undo'],
31 ['Ctrl-r', 'redo'],
32 ['.', 'repeat last change'],
33 ['ciw', 'change inner word'],
34 ['x', 'delete char'],
35 ['v / V', 'visual char / line'],
36 ]],
37 ['Search', [
38 ['/text', 'search for text'],
39 ['n N', 'next / prev match'],
40 ['*', 'search word under cursor'],
41 [':%s/a/b/g', 'replace a with b'],
42 ]],
43 ['Files (NvChad)', [
44 ['Space ff', 'find files'],
45 ['Space fw', 'live grep'],
46 ['Space fo', 'recent files'],
47 ['Ctrl-n', 'file tree'],
48 ['Tab', 'next buffer'],
49 ['Shift-Tab', 'prev buffer'],
50 ['Space x', 'close buffer'],
51 ]],
52 ['Windows', [
53 ['Ctrl-h/j/k/l', 'move between splits'],
54 [':vsp', 'vertical split'],
55 [':sp', 'horizontal split'],
56 ]],
57 ['Terminal and help', [
58 ['Space h', 'horizontal terminal'],
59 ['Space v', 'vertical terminal'],
60 ['Space ch', 'cheatsheet'],
61 ['Space th', 'themes'],
62 ]],
63]
64
65// Section headers (h) and key lines; a word keeps only the lines containing it.
66export function lines(word: string): { text: string; h?: boolean }[] {
67 const w = word.trim().toLowerCase()
68 return SHEET.flatMap(([title, rows]) => {
69 const hits = rows
70 .map(([key, what]) => `${key.padEnd(13)} ${what}`)
71 .filter(line => line.toLowerCase().includes(w))
72 return hits.length ? [{ text: title, h: true }, ...hits.map(text => ({ text }))] : []
73 })
74}
75
76export const register: Register = on => {
77 on('session.start', async ($, e, next) => {
78 await $.command.register({
79 name: 'vim',
80 description: 'Toggle the Vim / NvChad keys pane; /vim <word> filters it',
81 argumentHint: '[word]',
82 })
83 return next(e)
84 })
85
86 on('command.run', { command: 'vim' }, async ($, e) => {
87 const word = e.args.trim()
88 const isOpen = (await $.ui.panes()).some(p => p.id === PANE)
89 if (!word && isOpen) {
90 await $.ui.close({ id: PANE })
91 return { text: 'Vim keys pane closed.' }
92 }
93 await update($, filter, () => word)
94 await $.ui.open({ id: PANE, title: TITLE })
95 return { text: word ? `Vim keys filtered by "${word}".` : 'Vim keys pane opened.' }
96 })
97
98 on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {
99 const { Box, Text } = $.ui.resolve(e)
100 const word = await read($, filter)
101 const shown = lines(word)
102 return (
103 <Box flexDirection="column">
104 {word && <Text dimColor>filter: {word}</Text>}
105 {shown.length === 0 && <Text dimColor>No key matches "{word}".</Text>}
106 {shown.map(l => (l.h ? <Text bold>{l.text}</Text> : <Text>{l.text}</Text>))}
107 </Box>
108 )
109 })
110}
111types/index.d.ts 7 lines1declare module 'claude-code' {
2 interface PluginState {
3 'vim-keys': { filter: Filter }
4 }
5}
6export type Filter = string
7