Codelet logoCodelet

Extensions API

What activate gets handed, what the vscode namespace covers, and where the rest of this section lives

An extension adds to a workbench: a view, a command, a language feature, a filesystem. The guide covers passing extensions to a Workbench; this section is about writing the extensions themselves.

Read more in Guide > Extensions.

#A namespace, not a module

codelet hands your activate function an object shaped like VS Code's own vscode namespace — a strict subset of it. If you've written a VS Code extension, window.createStatusBarItem(), workspace.onDidChangeTextDocument, TreeDataProvider all read exactly the same here.

There's no import * as vscode from "vscode" to write, because there's no module named vscode to resolve. It's an argument:

function activate(context, vscode, codelet) {
  vscode.window.createStatusBarItem();
}

For the members codelet implements, the portability runs one way: a TreeDataProvider or a WebviewViewProvider written for codelet is also valid VS Code extension code. The reverse isn't true — plenty of the real namespace isn't here at all.

#codelet, the third argument

VS Code's extension host is a separate process talking to a workbench UI it never touches directly; codelet's runs in the same page as the workbench, so a handful of calls — filling a pane with a real component instead of a frame, reading a view's filter box, opening a second workspace over the reader's own — have no VS Code equivalent to sit on. Those live on codelet instead, so every codelet-specific call is visible at its call site rather than hidden inside a member the real namespace also has.

#What's missing

Some of VS Code is absent outright: debug, notebooks, authentication, comments, tests, lm, chat, l10n. There's no multi-root workspace support — a workbench holds one tree — and no file dialogs, since a browser tab has no filesystem of its own to open one onto.

Plenty more is narrowed rather than missing entirely — read-only carets, one editor group, a single settings layer. Full reference is the full list of what's here, what isn't, and why.

#A minimal extension

import { defineExtension } from "codelet/extensions";

export const wordCount = defineExtension({
  manifest: {
    name: "word-count",
    contributes: {
      commands: [{ command: "wordCount.show", title: "Show Word Count" }],
    },
  },

  activate(context, vscode) {
    const item = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
    const update = () => {
      const text = vscode.window.activeTextEditor?.document.getText().trim() ?? "";
      item.text = `${text ? text.split(/\s+/).length : 0} words`;
    };
    update();
    item.show();

    context.subscriptions.push(
      item,
      vscode.window.onDidChangeActiveTextEditor(update),
      vscode.workspace.onDidChangeTextDocument(update),
      vscode.commands.registerCommand("wordCount.show", () =>
        vscode.window.showQuickPick([{ label: item.text }]),
      ),
    );
  },
});

Pass it to a workbench like any built-in:

import { Workbench } from "codelet/workbench";

new Workbench({
  parent: document.getElementById("app")!,
  extensions: [wordCount],
});

That's the whole shape: a manifest describing what the extension contributes, and an activate function that wires it up once the workbench mounts.

#Where to go next