# surfaces/ Source directories for installable extension surfaces. Each subdirectory contains a `manifest.json` plus `js/`, `css/`, and optionally `assets/` directories — matching the archive format expected by `POST /admin/surfaces/install`. ## Structure ``` surfaces/ build.sh ← packages each subdir into dist/.surface hello-dashboard/ ← sample surface (verifies /s/:slug pipeline) manifest.json js/main.js css/main.css icd-test-runner/ ← ICD contract test runner (dev/test tool) manifest.json js/ css/ ``` ## Building ```bash # Build all surfaces → dist/*.surface ./surfaces/build.sh # Build one ./surfaces/build.sh icd-test-runner ``` Output goes to `dist/` (gitignored). Each `.surface` file is a zip archive ready for upload via the admin UI or: ```bash curl -X POST https://host/api/v1/admin/surfaces/install \ -H "Authorization: Bearer $TOKEN" \ -F "file=@dist/icd-test-runner.surface" ``` ## Adding a surface 1. Create `surfaces//manifest.json` with at least `id` and `title` 2. Add `js/`, `css/`, `assets/` as needed 3. Run `./surfaces/build.sh ` to verify it packages 4. Install via admin UI or API See [EXTENSION-SURFACES.md](../docs/EXTENSION-SURFACES.md) for the full authoring guide including manifest schema, platform API, and CSS custom properties. ## Conventions - `id` in manifest.json must be unique across all surfaces (core + extension) - Core surfaces (`chat`, `admin`, `settings`, `notes`, `editor`) cannot be overridden — the install endpoint returns 409 - Surface routes are served at `/s/` (auto-derived from manifest) - Static assets are served from `/surfaces//js/`, `/surfaces//css/` - The `dist/` directory is gitignored — archives are build artifacts