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);
|
|||
|
|
```
|