140 lines
4.4 KiB
Markdown
140 lines
4.4 KiB
Markdown
# Plugin Guide – WebCAD Plugin API v2
|
||
|
||
WebCAD Plugin API v2 erlaubt die Erweiterung des CAD um Werkzeuge,
|
||
Panels und Block-Bibliotheken – ohne Kerncode anzufassen.
|
||
|
||
## Plugin-Typ (PluginV2)
|
||
|
||
```ts
|
||
import type { PluginV2, ToolExtensionV2 } from './plugins';
|
||
|
||
export const myPlugin: PluginV2 = {
|
||
manifest: {
|
||
id: 'my-tools',
|
||
name: 'Meine Werkzeuge',
|
||
version: '1.0.0',
|
||
author: 'Du',
|
||
description: 'Beschreibung',
|
||
category: 'tools', // tools|elements|library|import-export|theme|other
|
||
enabledByDefault: true,
|
||
},
|
||
tools: [/* ToolExtensionV2[] */],
|
||
panels: [/* PanelExtension[] (optional) */],
|
||
libraryProviders: [/* LibraryProviderExtension[] (optional) */],
|
||
onInit?() {}, onActivate?() {}, onDeactivate?() {}, onDestroy?() {},
|
||
};
|
||
```
|
||
|
||
## Werkzeug (ToolExtensionV2)
|
||
|
||
Ein Werkzeug besteht aus Manifest, Options-Schema und Handlern:
|
||
|
||
```ts
|
||
export const myTool: ToolExtensionV2 = {
|
||
manifest: {
|
||
id: 'my-tool',
|
||
label: 'Mein Werkzeug',
|
||
icon: 'M',
|
||
ribbonTab: 'canvas', // canvas|insert|format
|
||
tags: ['basic'], // basic = sichtbar im simple-Mode, ['pro'] nur pro-Mode
|
||
description: 'Was es tut',
|
||
},
|
||
optionsSchema: [
|
||
{ key: 'width', label: 'Breite', type: 'number', min: 1, max: 500 },
|
||
{ key: 'color', label: 'Farbe', type: 'color' },
|
||
{ key: 'style', label: 'Stil', type: 'select',
|
||
options: [{ value: 'a', label: 'A' }, { value: 'b', label: 'B' }] },
|
||
{ key: 'frame', label: 'Rahmen', type: 'checkbox' },
|
||
],
|
||
handlers: {
|
||
down(e, ctx) {
|
||
// e.world = Weltkoordinaten, e.shift, e.ctrl
|
||
// ctx.doc = CADDocument (CRUD + transact + undo)
|
||
// ctx.setStatus('...'), ctx.setPreview?.(element)
|
||
const c = ctx as FullToolContext;
|
||
if (!c.doc) return;
|
||
c.doc.transact(() => c.doc!.addElement({ /* CADElement */ }));
|
||
},
|
||
move(e, ctx) { /* Live-Preview */ },
|
||
up(e, ctx) { /* Commit */ },
|
||
key(k, ctx) { /* return true = konsumiert (z. B. Enter) */ },
|
||
cancel(ctx) { /* ESC/Rechtsklick: Session-State zurücksetzen */ },
|
||
},
|
||
};
|
||
```
|
||
|
||
### Kernregeln
|
||
|
||
1. **Eine Aktion = eine `doc.transact(() => ...)`** = genau ein Undo-Schritt.
|
||
2. **Session-State als Modul-Member** (nicht in `options` – diese sind geteilter UI-State).
|
||
3. **`cancel` setzt den Session-State immer zurück.**
|
||
4. **Koordinaten-Konventionen:** rect `x/y` = Zentrum der BBox; circle/arc `el.x/el.y` = Zentrum; Winkel in Grad (0° = 3 Uhr, CCW).
|
||
5. **Undo-fähig schreiben** nur über `doc.addElement/updateElement/deleteElements`.
|
||
|
||
## Bibliotheks-Provider (LibraryProviderExtension)
|
||
|
||
```ts
|
||
const myProvider: LibraryProviderExtension = {
|
||
id: 'my-catalog',
|
||
label: 'Mein Katalog',
|
||
async listFolders() { return [{ id: 'f1', label: 'Ordner', parentId: null }]; },
|
||
async listBlocks(folderId) { return [/* LibBlock[] */]; },
|
||
async getBlock(id) { return /* LibBlock */; },
|
||
};
|
||
```
|
||
|
||
`LibBlock.payload` = JSON-String eines CADElement-Arrays → Drag&Drop
|
||
in die Zeichenfläche funktioniert automatisch über den bestehenden
|
||
Drop-Kanal. Thumbnail = eigenständiges SVG.
|
||
|
||
## Registrierung
|
||
|
||
Built-in-Plugins werden in `src/plugins/index.ts` per
|
||
`pluginRegistry.registerV2(plugin)` registriert; `enabledByDefault`
|
||
macht sie aktiv (alternativ per `pluginRegistry.enableV2(id)`).
|
||
|
||
## Beispiel: Minimal-Plugin
|
||
|
||
```ts
|
||
// plugins/builtin/stamp/index.ts
|
||
import type { PluginV2, ToolExtensionV2 } from '../../types';
|
||
|
||
const stampTool: ToolExtensionV2 = {
|
||
manifest: {
|
||
id: 'stamp', label: 'Stempel', icon: '\u25c6',
|
||
ribbonTab: 'insert', tags: ['basic'],
|
||
description: 'Platziert ein Quadrat',
|
||
},
|
||
optionsSchema: [{ key: 'size', label: 'Größe', type: 'number', min: 10, max: 200 }],
|
||
handlers: {
|
||
down(e, ctx) {
|
||
const c = ctx as any;
|
||
if (!c.doc) return;
|
||
const size = (ctx.options.size as number | undefined) ?? 50;
|
||
c.doc.transact(() => c.doc.addElement({
|
||
id: `stamp_${Date.now()}`,
|
||
type: 'rect', layerId: 'layer-0',
|
||
x: e.world.x, y: e.world.y,
|
||
width: size, height: size,
|
||
properties: {},
|
||
}));
|
||
ctx.setStatus('Stempel platziert');
|
||
},
|
||
},
|
||
};
|
||
|
||
export const stampPlugin: PluginV2 = {
|
||
manifest: {
|
||
id: 'stamp', name: 'Stempel', version: '1.0.0', author: 'WebCAD',
|
||
description: 'Beispielplugin', category: 'elements', enabledByDefault: true,
|
||
},
|
||
tools: [stampTool],
|
||
};
|
||
```
|
||
|
||
Registrieren in `src/plugins/index.ts`:
|
||
|
||
```ts
|
||
pluginRegistry.registerV2(stampPlugin);
|
||
```
|