Skip to content

vimeejs/vimee

vimee

vimee

A headless vim engine for the web

CI CodSpeed License: MIT


vimee.mp4

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/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 { Vim } from "@vimee/shiki-editor";
import "@vimee/shiki-editor/styles.css";
import { createHighlighter } from "shiki";

const highlighter = await createHighlighter({
  themes: ["vitesse-dark"],
  langs: ["typescript"],
});

function App() {
  return (
    <Vim
      content={`const greeting = "Hello, vim!";`}
      highlighter={highlighter}
      lang="typescript"
      theme="vitesse-dark"
      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>
  );
}

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: h j k l w W b B e E 0 $ ^ gg G f F t T ; , H M L { }
  • Operators: d y c > < with motions and text objects
  • Text Objects: iw aw i" a" i' a' i( a( i[ a[ i{ a{ i< a< i` a`
  • Search: /pattern ?pattern n N * #
  • Command-Line: :w :q :wq :{number} :set :s/pattern/replace/flags
  • Editing: x X r s S J o O ~ . u Ctrl-R
  • Registers: "a–"z named registers, unnamed register
  • Macros: q{a-z} record, @{a-z} playback, @@ repeat last
  • Marks: m{a-z} set mark, '{a-z} jump to mark
  • Counts: 3dd, 5j, 2dw, etc.
  • Visual Block: Ctrl-V, block I/A insert
  • Scroll: Ctrl-U Ctrl-D Ctrl-B Ctrl-F
  • Indent: >> << with configurable style/width

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

Playground

A live playground app is included as a git submodule.

# Clone with playground
git clone --recursive https://github.com/vimeejs/vimee.git

# Or, if you already cloned without --recursive
git submodule update --init

# Install dependencies (includes playground)
bun install

# Build all packages, then start the playground
bun run build
cd playground && bun run dev

The playground 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
β”œβ”€β”€ shiki-editor/      # @vimee/shiki-editor β€” editor component with Shiki
└── testkit/           # @vimee/testkit β€” test utilities for Vim operations
playground/            # Live demo app (git submodule)

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