# 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 Armature.

**Prerequisites:** A running Armature 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 =
                    '<div style="padding:12px;background:var(--bg-secondary);' +
                    'border:1px solid var(--border);border-radius:8px">' +
                    '<strong>Demo:</strong> ' + code +
                    '</div>';
            }
        });
    }

    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-secondary)`
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 <token>" \
  -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.
