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/plugin-api

v0.1.12

Published

SDK interfaces for 3rd-party plugins on the Vem editor

Downloads

1,346

Readme

@vemjs/plugin-api

npm version License: MIT

The official Plugin SDK and life-cycle registration layer for the Vem Editor. It allows third-party developers to register custom keybindings, hook into buffer updates, intercept mode transitions, and build complex chords or commands.

Features

  • Standardized Plugin Interface: Clean activation/deactivation lifecycles matching the @vemjs/core environment.
  • Keybinding Registration: Dynamically override editor shortcuts or register command chords in specific modes.
  • Event Observers: Hook into buffer creation (onDidOpenBuffer), text mutations (onDidChangeBuffer), and mode changes (onDidChangeMode).
  • Command Management: Define global editor commands that can be invoked via the Command Bar or mapped to shortcuts.

Installation

bun add @vemjs/plugin-api
# or via npm
npm install @vemjs/plugin-api

Quick Start

Create and activate a plugin that adds a custom keystroke in NORMAL mode:

import { VemEditorState } from '@vemjs/core';
import { PluginRegistry, type VemPlugin } from '@vemjs/plugin-api';

const editor = new VemEditorState('initial text');
const registry = new PluginRegistry(editor);

// Define a plugin
const myPlugin: VemPlugin = {
  name: 'my-custom-shortcuts',
  version: '1.0.0',
  activate(context) {
    // Register a command
    context.registerCommand('custom.hello', () => {
      console.log('Hello from command!');
    });

    // Bind keys (e.g. pressing 'gh' in NORMAL mode fires command)
    context.registerKeybinding('NORMAL', 'gh', 'custom.hello');
  },
};

// Register & Activate
registry.register(myPlugin);

// Pressing keys in sequence triggers the command
editor.input('g');
editor.input('h'); // Console prints: "Hello from command!"

API Reference

VemPlugin

Interface representing a loadable plugin.

  • name: string: Unique package identifier.
  • version: string: Semantic version string.
  • activate(context: PluginContext): void: Called by the engine when loading the plugin.
  • deactivate?(): void: Optional cleanup callback.

PluginContext

Provided to activate(context) to interact with the editor.

  • editorState: VemEditorState: Access to core editor properties.
  • registerCommand(commandName: string, callback: () => void): void: Registers a function callback globally.
  • registerKeybinding(mode: EditorMode, keys: string, commandName: string): void: Binds a specific key combination (or chord prefix) in the specified mode to a registered command.
  • onDidOpenBuffer(cb: () => void): void: Subscribes to buffer open events.
  • onDidChangeBuffer(cb: () => void): void: Subscribes to buffer content mutation events.
  • onDidChangeMode(cb: (mode: EditorMode) => void): void: Subscribes to mode change notifications.

PluginRegistry

Manager executing plugin installation.

  • constructor(editorState: VemEditorState): Creates the registry bound to an editor instance.
  • register(plugin: VemPlugin): void: Registers context callbacks and triggers the plugin's activate method.
  • executeCommand(name: string): void: Runs the callback associated with a command.

Plugin Development Guide

Naming Conventions

For consistency in the ecosystem, plugins should be named using the prefix vem-plugin- when published as standalone packages, or @vemjs/plugin-<name> if they are officially maintained packages:

  • Third-party: vem-plugin-format-on-save
  • Official: @vemjs/plugin-treesitter

Complete Example: Format-on-Save

Here is a full plugin implementation that automatically trims trailing whitespace whenever a buffer changes (an automated linter/formatter behavior):

import { type VemPlugin } from '@vemjs/plugin-api';

export const FormatOnSavePlugin: VemPlugin = {
  name: 'format-on-save',
  version: '1.0.0',
  activate(context) {
    let isFormatting = false;

    context.onDidChangeBuffer(() => {
      // Avoid recursive change loops
      if (isFormatting) return;

      const editor = context.editorState;
      const originalText = editor.getText();
      const lines = originalText.split('\n');

      // Trim trailing spaces
      const formattedLines = lines.map((line) => line.trimEnd());
      const formattedText = formattedLines.join('\n');

      if (formattedText !== originalText) {
        isFormatting = true;
        // Access buffer to set raw text
        editor.getBuffer().setText(formattedText);
        isFormatting = false;
        console.log('[FormatOnSave] Trimmed trailing whitespaces');
      }
    });
  },
};

Architecture

graph LR
    Engine[VemEditorState] <--> Context[PluginContext]
    Context --> Registry[PluginRegistry]
    Registry --> Plugin1[VemPlugin A]
    Registry --> Plugin2[VemPlugin B]

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.