# Build a WebCAD plugin Create a complete WebCAD Plugin Package v1 using the schema and SDK below. Return one valid JSON object that can be saved as a `.webcad-plugin.json` file. Do not wrap it in Markdown fences. ## Package requirements - Use schemaVersion 1 and sdkVersion 1. - Give the package a stable reverse-domain id that you control, such as `com.example.door_checker`. Do not use the reserved `webcad.*` or `com.webcad.*` namespaces. - Use semantic versioning, a clear description, author name, SPDX license identifier, one category from `modeling | analysis | export | annotation | query | view | project | scripting`, and up to 20 useful tags. - Declare only needed permissions from `elements:read | elements:write | project:read | project:write | roomtypes:write | ui:ribbon | ui:modal | ui:notice | storage | export:download`. - Include 1–50 commands. Command ids must be lowercase function names containing only letters, numbers, and underscores, beginning with a letter. Keep each id within 64 characters. - Each command needs a label, description, JSON Schema object for parameters, and JavaScript source in `code`. Source is limited to 100 KB per command and 500 KB across the package. - Use `isQuery: true` when a command inspects or calculates information without changing the project. Set `ribbonTab` only when a visible ribbon action is useful; a command with `ribbonTab` must also declare `ui:ribbon`. ## SDK and runtime rules The host supplies an asynchronous `context` with `pluginId`, `pluginName`, `elements`, `selection`, `project`, `history`, `ui`, and `storage` APIs. **Await every SDK call before using its result or returning**; the host rejects commands that leave pending calls. The available v1 methods and permissions are: | API | Methods and permission | | --- | --- | | `context.elements` | `getAll()`, `getById(id)`, `getByCategory(ifcType)` — `elements:read`; `create(element)`, `update(id, patch)`, `delete(id)`, `setProperty(id, key, scalarValue)` — `elements:write`. Update cannot change an element's `id` or `ifcType`. | | `context.selection` | `getSelectedIds()`, `getSelectedElements()`, `select(ids)`, `clear()` — `elements:read`. | | `context.project` | `get()`, `getLevels()`, `getGrids()`, `getMaterials()`, `getFamilies()`, `getTypes()`, `getGlobalParameters()` — `project:read`; `addLevel(level)`, `addGrid(grid)`, `setProjectInfo(info)` — `project:write`. Project info accepts only string values for `projectNumber`, `clientName`, `address`, and `buildingName`. | | `context.storage` | `get(key, defaultValue?)`, `set(key, value)`, `remove(key)`, `clear()` — `storage`. Storage is scoped to this plugin. | | `context.ui` | `showNotice(message, type?, durationMs?)` — `ui:notice`; `downloadText(filename, content, mimeType?)` — `export:download`. | | `context.history` | `commit(description?)` is accepted for compatibility; host transactions already commit successful writes as one undo step. | Before creating an element, inspect actual elements of the same IFC category using `getByCategory` or `getAll`, then match the host's real shape, required base fields (`id`, `name`, `typeId`, `ifcType`, `levelId`, `createdAt`, `updatedAt`, `properties`) and category-specific fields. Use a unique, non-reserved id. Do not invent a partial element from memory. For project levels/grids, inspect `await context.project.getLevels()` / `await context.project.getGrids()` and follow their existing shapes. Return useful structured data from query commands. Write command bodies as async JavaScript statements with access to `context` and `params`. Return a value when the command produces a result. The host executes only a finite set of SDK calls in its isolated plugin runtime and validates declared capabilities at the host boundary. Do not use browser globals, the parent document, direct application stores, authentication/session data, or network requests. There is no direct fetch, DOM, or arbitrary file-system access. The v1 runtime does not expose persistent events, custom UI registration, modal UI, room-type mutations, stretch, or opening creation. Permissions `ui:modal` and `roomtypes:write` do not add those SDK methods. Ask for a missing SDK capability rather than reaching around the API. For any command that changes the model, declare the least permissions required, validate every input, resolve element ids from current project data, and explain what will change. The host simulates allowed writes and returns proposed changes during preview, then applies successful writes as one undo step. Command code must perform mutations only through the supplied SDK. Do not describe a read-only query as a model preview. Keep UI text and documentation in English. ## Complete example ```json { "schemaVersion": 1, "id": "com.example.wall_counter", "name": "Wall Counter", "version": "1.0.0", "description": "Counts walls in the active WebCAD project.", "author": { "name": "Example Studio" }, "license": "MIT", "category": "analysis", "tags": ["walls", "count"], "sdkVersion": 1, "permissions": ["elements:read"], "commands": [ { "id": "count_walls", "label": "Count walls", "description": "Returns the number of walls in the active project.", "parameters": { "type": "object", "properties": {}, "additionalProperties": false }, "isQuery": true, "code": "const elements = await context.elements.getAll(); const walls = elements.filter((element) => element.ifcType === 'IfcWall'); return { wallCount: walls.length };" } ] } ``` Before returning the package, check that its code matches the declared parameter schema, uses only the listed SDK surface, and reports assumptions and units accurately.