Skip to content

vimeejs/vimee

vimee

vimee

A headless vim engine for the web

CI CodSpeed License: MIT


vimee gif

vimee is a framework-agnostic, pure-function Vim engine that you can plug into any editor UI. The core engine has zero runtime dependencies β€” it takes a keystroke and returns state transitions. Framework bindings (React, etc.) are thin wrappers that turn those transitions into reactive state.

Packages

Package Description Version Size
@vimee/core Headless vim engine with pure function API npm bundle
@vimee/react React useVim hook npm bundle
@vimee/plugin-textarea Attach vim to any textarea npm bundle
@vimee/plugin-monaco Attach vim to any Monaco Editor npm bundle
@vimee/plugin-codemirror Attach vim to any CodeMirror 6 editor npm bundle
@vimee/shiki-editor Vim editor component with Shiki syntax highlighting npm bundle
@vimee/testkit Test utilities for Vim operations npm bundle

Quick Start

With Shiki Editor (recommended)

npm install @vimee/core @vimee/react @vimee/shiki-editor shiki
import { use } from "react";
import { Vim } from "@vimee/shiki-editor";
import "@vimee/shiki-editor/styles.css";
import { createHighlighter } from "shiki";

const highlighterPromise = createHighlighter({
  themes: ["catppuccin-mocha"],
  langs: ["typescript"],
});

function App() {
  const highlighter = use(highlighterPromise);

  return (
    <Vim
      content={`const greeting = "Hello, vim!";`}
      highlighter={highlighter}
      lang="typescript"
      theme="catppuccin-mocha"
      onChange={(c) => console.log("Changed:", c)}
      onSave={(c) => console.log("Saved:", c)}
    />
  );
}

With React (custom UI)

npm install @vimee/core @vimee/react
import { useVim } from "@vimee/react";

function Editor() {
  const { content, cursor, mode, handleKeyDown } = useVim({
    content: "Hello, vim!",
    onChange: (c) => console.log("Changed:", c),
  });

  return (
    <div tabIndex={0} onKeyDown={handleKeyDown}>
      <div>Mode: {mode}</div>
      <pre>{content}</pre>
      <div>
        Cursor: {cursor.line}:{cursor.col}
      </div>
    </div>
  );
}

With Monaco Editor

npm install @vimee/core @vimee/plugin-monaco
import { attach } from "@vimee/plugin-monaco";

// Assumes `editor` is a monaco.editor.IStandaloneCodeEditor instance
const vim = attach(editor, {
  onChange: (value) => console.log("Changed:", value),
  onModeChange: (mode) => console.log("Mode:", mode),
});

// Later...
vim.destroy();

With CodeMirror 6

npm install @vimee/core @vimee/plugin-codemirror
import { EditorView, basicSetup } from "codemirror";
import { javascript } from "@codemirror/lang-javascript";
import { attach } from "@vimee/plugin-codemirror";

const view = new EditorView({
  doc: 'console.log("Hello, vim!");',
  extensions: [basicSetup, javascript()],
  parent: document.getElementById("editor")!,
});

const vim = attach(view, {
  onChange: (value) => console.log("Changed:", value),
  onModeChange: (mode) => console.log("Mode:", mode),
});

// Later...
vim.destroy();

Core engine only

npm install @vimee/core
import {
  TextBuffer,
  createInitialContext,
  processKeystroke,
} from "@vimee/core";

const buffer = new TextBuffer("Hello, world!");
let ctx = createInitialContext({ line: 0, col: 0 });

// Type "dd" to delete a line
const r1 = processKeystroke("d", ctx, buffer);
ctx = r1.newCtx;
const r2 = processKeystroke("d", ctx, buffer);
ctx = r2.newCtx;

console.log(buffer.getContent()); // ""

Architecture

processKeystroke(key, ctx, buffer, ctrlKey?, readOnly?)
  β†’ { newCtx: VimContext, actions: VimAction[] }

The engine is a pure function β€” no side effects, no DOM, no framework dependency. All state transitions are explicit and testable. The VimAction[] array tells the UI layer what happened (cursor moved, content changed, mode switched, etc.).

Supported Vim Features

Modes: Normal, Insert, Visual, Visual-Line, Visual-Block, Command-Line

Motions

Key Description
h j k l Left / Down / Up / Right
w W Word forward (word / WORD)
b B Word backward (word / WORD)
e E Word end forward (word / WORD)
0 Start of line
^ First non-blank character
$ End of line
gg First line
G Last line (or {count}G to jump)
H M L Screen top / middle / bottom
f{char} F{char} Find char forward / backward
t{char} T{char} Till char forward / backward
; , Repeat / reverse last f / F / t / T
{ } Paragraph backward / forward
* # Search word under cursor forward / backward

Operators

All operators work with motions, text objects, counts, and visual selections.

Key Description
d Delete
y Yank (copy)
c Change (delete and enter insert mode)
> Indent right
< Indent left

Text Objects

Used after an operator or in visual mode (e.g. diw, ca").

Key Description
iw aw Inner / around word
i" a" Inner / around double quotes
i' a' Inner / around single quotes
i` a` Inner / around backticks
i( a( Inner / around parentheses
i[ a[ Inner / around square brackets
i{ a{ Inner / around curly braces
i< a< Inner / around angle brackets

Editing

Key Description
x Delete character under cursor
X Delete character before cursor
r{char} Replace character under cursor
s Substitute character (delete and enter insert mode)
S Substitute line
J Join lines
o O Open new line below / above
~ Toggle case
p P Paste after / before cursor
. Repeat last change
u Undo
Ctrl-R Redo
Ctrl-W Delete word backward (insert mode)

Search

Key Description
/pattern Forward search
?pattern Backward search
n Repeat search in same direction
N Repeat search in opposite direction

Command-Line

Command Description Action
:w Save { type: "save", content }
:q Quit { type: "quit", force: false }
:q! Force quit { type: "quit", force: true }
:wq / :x Save and quit save + quit
:noh / :nohlsearch Clear search highlight mode-change
:set number / :set nu Show line numbers { type: "set-option", option: "number", value: true }
:set nonumber / :set nonu Hide line numbers { type: "set-option", option: "number", value: false }
:{number} Jump to line cursor-move
:s/old/new/[gi] Substitute (current line) content-change
:%s/old/new/[gi] Substitute (all lines) content-change
:N,Ms/old/new/[gi] Substitute (line range) content-change

Macros

Key Description
q{a-z} Start recording macro into register
q Stop recording (while recording)
@{a-z} Playback macro from register
@@ Repeat last macro

Marks

Key Description
m{a-z} Set mark
'{a-z} Jump to mark

Registers

Key Description
"a – "z Named registers
"" Unnamed register (default)

Visual Block

Key Description
Ctrl-V Enter visual block mode
I Block insert (prepend to each line)
A Block append (append to each line)

Scroll

Key Description
Ctrl-U Half page up
Ctrl-D Half page down
Ctrl-B Full page up
Ctrl-F Full page down

Counts

All motions, operators, and editing commands support count prefixes (e.g. 3dd, 5j, 2dw).

Development

# Install
bun install

# Build all packages
bun run build

# Run all tests
bun run test

# Type check
bun run typecheck

# Lint
bun run lint

# Generate a changeset
bun run changeset:gen          # auto-detect from commits
bun run changeset:gen major    # force major bump

Debug App

A debug app is included for local development and E2E testing. It provides a minimal page for each plugin.

# Start the debug app dev server
bun run debug

The debug app uses workspace:* to reference local packages, so any changes you build in packages/* are reflected immediately.

Monorepo Structure

packages/
β”œβ”€β”€ core/              # @vimee/core β€” headless vim engine
β”œβ”€β”€ react/             # @vimee/react β€” React useVim hook
β”œβ”€β”€ plugin-textarea/   # @vimee/plugin-textarea β€” vim for any textarea
β”œβ”€β”€ plugin-monaco/     # @vimee/plugin-monaco β€” vim for any Monaco Editor
β”œβ”€β”€ plugin-codemirror/ # @vimee/plugin-codemirror β€” vim for any CodeMirror 6 editor
β”œβ”€β”€ shiki-editor/      # @vimee/shiki-editor β€” editor component with Shiki
└── testkit/           # @vimee/testkit β€” test utilities for Vim operations
debug/                 # Debug app for local development (Vite MPA)
e2e/                   # Playwright E2E tests

Built with Bun workspaces, tsup for bundling, and Vitest for testing.

License

MIT

About

πŸ‘» A headless vim engine for the web

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Contributors

Languages