cook.md

Build your first plugin

A Cook Editor plugin is a VS Code extension: a folder with a package.json that declares what it adds, and a TypeScript entry point with an activate() function. If you've written a VS Code extension before, everything you know applies.

What a plugin can do

  • Everything in the VS Code Extension API that Cook Editor supports (API 1.110): commands, menus, keybindings, settings, snippets, sidebar views and webviews, status bar items, file access.
  • Call the Cooklang API — parse shopping lists, resolve recipe references, build aisle-grouped lists — with the same Rust code the editor uses.
  • Add buttons and menu items to Cooklang screens through outlets, such as the recipe preview header.

Start from the example

First-party plugins live in github.com/cook-md/plugins, one folder per plugin. meal-journal is the smallest complete example; shopping-list shows the Cooklang API, outlets and a webview.

git clone https://github.com/cook-md/plugins.git
cd plugins
cp -r meal-journal my-plugin
cd my-plugin
npm install

Keep logic that doesn't need the vscode module in its own files — you can test those with plain mocha (npm test), as both examples do.

The manifest

Edit package.json. At minimum:

{
  "name": "my-plugin",
  "displayName": "My Plugin",
  "version": "0.1.0",
  "publisher": "your-namespace",
  "engines": { "vscode": "^1.100.0" },
  "main": "./out/extension.js",
  "activationEvents": ["onStartupFinished"],
  "contributes": {
    "commands": [{ "command": "myPlugin.hello", "title": "Say Hello", "category": "My Plugin" }],
    "menus": {
      "cooklang/recipePreview/toolbar": [{ "command": "myPlugin.hello", "group": "navigation@50" }]
    }
  }
}

That menus entry puts your command in the recipe preview header — see Outlets for all the places you can add to.

Calling Cooklang

The Cooklang API is a set of commands. Check the version when your plugin starts, then call what you need:

import * as vscode from 'vscode';

export async function activate(context: vscode.ExtensionContext) {
  const version = await vscode.commands.executeCommand<number>('cooklang.api.version');
  if (version !== 1) {
    vscode.window.showWarningMessage('My Plugin needs a newer Cook Editor.');
    return;
  }
  context.subscriptions.push(vscode.commands.registerCommand('myPlugin.hello', async (ctx: { path: string; scale: number }) => {
    const list = await vscode.commands.executeCommand('cooklang.api.generateShoppingList', {
      recipes: [{ path: ctx.path, scale: ctx.scale }],
    });
    vscode.window.showInformationMessage(`Needs ${JSON.stringify(list)}`);
  }));
}

Run it in Cook Editor

Plugins load from the plugins/ folder of a Cook Editor source checkout. The examples' npm run deploy builds the plugin and copies it to editor/plugins/<publisher>.<name>; then start (or restart) the editor with npm run start:electron to pick it up. The loop is: edit → npm run deploy → restart the editor.