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, 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.

authentication is all there, but nothing ships a provider for it: an OAuth flow needs an address the authorization server can redirect back to, and a page owns none. So a workbench answers getSession with undefined until its host mounts a provider of its own — and once one is mounted, the Accounts item at the foot of the activity bar is where the reader signs in and out, and says which extensions may use the account.

Plenty more is narrowed rather than missing entirely — read-only carets, one active editor however many editor groups are open, 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