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.