# Build Your First Browser Extension This tutorial walks through building a browser extension that renders custom code blocks, modeled on the CSV Table Viewer that ships with Switchboard. **Prerequisites:** A running Switchboard instance, a text editor, and `zip`. ## Step 1: Create the Directory ```sh mkdir -p my-extension/js ``` ## Step 2: Write the Manifest Create `my-extension/manifest.json`: ```json { "id": "my-extension", "title": "My Extension", "version": "0.1.0", "type": "extension", "tier": "browser", "author": "you", "description": "Renders demo code blocks as styled HTML", "permissions": [], "settings": {} } ``` - **id** -- unique identifier, used as the install key - **type** -- `extension` for browser-only packages; `full` or `surface` for packages with backend routes - **tier** -- `browser` for client-side JS; `starlark` for server-side scripting ## Step 3: Write the Browser Script Create `my-extension/js/script.js`. Browser extensions use the IIFE pattern and register with the SDK through `sw.renderers`: ```js (function () { 'use strict'; function register() { if (!window.sw?.renderers) return; sw.renderers.register('demo-block', { type: 'block', priority: 10, match(lang) { return (lang || '').toLowerCase() === 'demo'; }, render(lang, code, container) { container.innerHTML = '
' + 'Demo: ' + code + '
'; } }); } if (window.sw?._sdk) { register(); } else { document.addEventListener('sw:ready', register, { once: true }); } })(); ``` The IIFE wrapper keeps variables out of global scope. `sw.renderers.register` takes a name and an options object: `type: 'block'` targets fenced code blocks, `match` checks the language tag, and `render` receives the language, raw code, and a container element. The `sw:ready` event fires once the SDK initializes; if already loaded, register immediately. Use CSS variables like `var(--bg-2)` and `var(--border)` to follow the active theme. ## Step 4: Package It ```sh cd my-extension zip -r ../my-extension.pkg manifest.json js/ ``` The `.pkg` format is a ZIP with `manifest.json` at the root. Optional directories: `js/`, `css/`, `assets/`. If working inside `packages/`, use the build script instead: `bash build.sh my-extension`. ## Step 5: Install It ```sh curl -X POST http://localhost:3000/api/v1/admin/packages/install \ -H "Authorization: Bearer " \ -F "file=@my-extension.pkg" ``` You can also install through the Admin UI under the Packages section. ## Step 6: Enable and Test 1. Open the admin panel, navigate to Packages, confirm "My Extension" is enabled. 2. Go to any markdown surface (Chat, Notes, etc.). 3. Enter a fenced code block with the `demo` language tag. You should see styled output instead of a plain code block. ## Going Further - **Add CSS** -- create a `css/` directory; stylesheets are injected automatically. - **Post-renderers** -- register with `type: 'post'` to run after block renderers finish (the Mermaid extension uses this for async SVG rendering). - **Settings** -- declare a `settings` object in the manifest for admin-configurable values. - **Server-side logic** -- set `tier: "starlark"` and add `script.star` for backend API routes and database tables. See `PACKAGE-FORMAT.md` for the full manifest spec and `EXTENSION-GUIDE.md` for advanced patterns.