npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vemjs/core

v0.8.0

Published

Pure state machine for the Vem modal text editor

Readme

@vemjs/core

npm version License: MIT

The pure TypeScript, zero-dependency core state machine and Vim keybinding parser engine for the Vem Editor. It handles buffers, undo/redo states, custom keybindings, visual selections, and coordinates with Language Server Protocol (LSP) diagnostics.

Features

  • Pure Modal Editing Engine: Fully decoupled state tracking for NORMAL, INSERT, VISUAL, and COMMAND modes.
  • Vim Parser & Motions: Native parsing of Vim keyboard sequences. Supports counts, motion combinations (e.g. 2w, 3j), operators (d, y, c), and text objects (iw, aw, i", a", etc.).
  • Line-oriented Vim Buffer: High-performance buffer with transaction tracking and a deep undo/redo stack (UndoManager).
  • Reactive Hooks: Event triggers for buffer changes, mode switches, commands, and diagnostics to seamlessly integrate plugins and renderers.
  • Diagnostic Collection: Structured APIs for storing, querying, and subscribing to compilation and linting diagnostics (errors, warnings, hints).
  • Extensible Configuration: ESM-native configuration loading (ConfigLoader) for registering settings and keybindings.

Installation

bun add @vemjs/core
# or via npm
npm install @vemjs/core

Quick Start

Initialize the editor state machine and feed it keystrokes programmatically:

import { VemEditorState } from '@vemjs/core';

// Create a state machine preloaded with text
const editor = new VemEditorState('const greeting = "Hello World";\nconsole.log(greeting);\n');

// Subscribe to buffer changes
editor.onDidChangeBuffer(() => {
  console.log('Buffer updated! Current text:\n', editor.getText());
});

// Subscribe to mode changes
editor.onDidChangeMode((mode) => {
  console.log(`Switched to mode: ${mode}`);
});

// Currently in NORMAL mode; send keys to move cursor and insert text
editor.input('j'); // Move down to line 2
editor.input('$'); // Move cursor to end of line
editor.input('i'); // Transition to INSERT mode
editor.input(' '); // Type space
editor.input('/'); // Type comment character
editor.input('/'); // Type comment character
editor.input(' '); // Type space
editor.input('P'); // Type P
editor.input('r'); // Type r
editor.input('i'); // Type i
editor.input('n'); // Type n
editor.input('t'); // Type t
editor.input('<Esc>'); // Back to NORMAL mode

API Reference

VemEditorState

The central state machine coordinator.

  • constructor(initialText?: string): Creates a new editor state.
  • input(key: string): void: Processes a keypress string (e.g., 'a', 'h', '<Esc>', '<C-r>').
  • getMode(): EditorMode: Returns the current active mode.
  • getText(): string: Returns the complete content of the buffer.
  • getCursor(): Position: Returns current cursor position { line: number, character: number }.
  • getDiagnostics(): Diagnostic[]: Gets current collection of diagnostics.
  • setDiagnostics(diagnostics: Diagnostic[]): void: Replaces current diagnostics and notifies subscribers.
  • registerKeybinding(mode: EditorMode, keys: string, commandName: string): Binds a key sequence to a named command.
  • onDidChangeBuffer(cb: () => void): void: Fires whenever the text inside the buffer changes.
  • onDidOpenBuffer(cb: () => void): void: Fires when a new buffer opens.
  • onDidChangeMode(cb: (mode: EditorMode) => void): void: Fires on editor mode switches.
  • onPublishDiagnostics(cb: (diagnostics: Diagnostic[]) => void): void: Fires when new diagnostics are published.

VimBuffer

Encapsulates buffer rows. Accessible via editorState.getBuffer().

  • getText(): string: Get entire text.
  • setText(text: string): void: Overwrite text.
  • getLine(lineIndex: number): string: Get single line content.
  • getLinesCount(): number: Get total lines.

Vim Keybinding Reference

| Key | Mode | Description | | ------------------ | ------ | ------------------------------------------------------------ | | h, j, k, l | NORMAL | Move cursor Left, Down, Up, Right | | w, b, e | NORMAL | Move forward/backward/end-of-word | | 0, $ | NORMAL | Move to start/end of line | | gg, G | NORMAL | Move to first/last line | | i, a | NORMAL | Enter INSERT mode (before/after cursor) | | v | NORMAL | Enter VISUAL mode (character selection) | | : | NORMAL | Enter COMMAND mode | | d | NORMAL | Delete operator (e.g., dw deletes word, dd deletes line) | | y | NORMAL | Yank (copy) operator | | p | NORMAL | Paste text from yank register | | u, <C-r> | NORMAL | Undo / Redo | | <Esc> | ANY | Return to NORMAL mode |


Diagnostics API

Diagnostics are mapped to the standard LSP diagnostic severity levels:

import { VemEditorState, type Diagnostic } from '@vemjs/core';

const editor = new VemEditorState('let x: number = "hello";');

editor.onPublishDiagnostics((diagnostics) => {
  console.log(`Received ${diagnostics.length} diagnostics.`);
  for (const diag of diagnostics) {
    console.log(`[${diag.severity.toUpperCase()}] Line ${diag.line}: ${diag.message}`);
  }
});

// Setting diagnostics (typically done by @vemjs/lsp-client)
const errors: Diagnostic[] = [
  {
    line: 0,
    startCharacter: 16,
    endCharacter: 23,
    severity: 'error',
    message: "Type 'string' is not assignable to type 'number'.",
    source: 'typescript-lsp',
  },
];

editor.setDiagnostics(errors);

Architecture

The following diagram illustrates the relationship between @vemjs/core and the other packages:

graph TD
    subgraph Core Engine [@vemjs/core]
        VemEditorState --> VimBuffer
        VemEditorState --> UndoManager
    end

    subgraph Renderer [@vemjs/renderer-vecto]
        VectoRenderer --> VemEditorState
        VemEditorEntity --> VemEditorState
    end

    subgraph LSP Layer [@vemjs/lsp-client]
        LSPClient --> VemEditorState
        JsonRpcClient --> WebSocket
    end

    subgraph Plugins [@vemjs/plugin-api]
        PluginRegistry --> VemEditorState
    end

Contributing

Please review CONTRIBUTING.md for details on our workflow and engineering guidelines.

License

This package is licensed under the MIT License - see the LICENSE file for details.