docs(I2): plugin guide (API v2 + example), library spec (wcadlib JSON+ZIP), changelog 2.0, readme rewrite

This commit is contained in:
Agent Zero
2026-08-29 03:36:45 +02:00
parent a3d3732f85
commit 7eedddbc3f
4 changed files with 335 additions and 9 deletions
+54
View File
@@ -0,0 +1,54 @@
# Changelog
## 2.0.0 (2026-08-29)
Vollständige Neuentwicklung der Kern-Architektur (Phasen AH der Roadmap).
### Architektur
- **Plugin-System v2 (A1A4):** ToolExtensionV2/PanelExtension/LibraryProviderExtension-Registry mit Interaktions-Dispatcher, CADDocument-Fassade über Yjs mit echtem Y.UndoManager (1 Aktion = 1 Undo-Schritt), Undo-fähige transact-Bündelung, V2HistoryPanel. Alle 30+ Werkzeuge als V2-Plugins migriert.
- **PixiJS-Renderer (C1C4):** WebGL-Rendering als Default (Opt-out), inkrementelles Element-Rendering über Map-Diff, Snap-Marks + Selection-Handles im Overlay, MockAdapter für headless Tests.
- **Kernel:** React-freies `kernel/**` (Regel), documentService-Singleton, featureFlags.
### CAD-Werkzeuge (D1D12, vollständig)
- Ellipse (3-Klick, Rotation aus Zugrichtung, Bogen via Winkel-Optionen) + Spline (CV-basiert, Catmull-Rom durch alle Kontrollpunkte)
- Point/XLine/Ray auf Auto-angelegtem `defpoints`-Layer mit `construction:true`-Tag
- MTEXT (mehrzeilig, optionaler Rahmen in derselben Transaktion)
- Stretch, Break, Join, Explode, Align, Chamfer, Lengthen, PolylineEditor (verifiziert, Phase D complete)
- GripEditing: Vertex-/Center-Grips (Linien-Endpunkte, Polyline-Vertices, Kreis-Zentrum) mit Drag in einer Transaktion; BBox-Corner-Skalierung bleibt parallel
- Arrays: rect, polar, **path** (Kopien gleichmäßig entlang Pfadlänge)
- Snaps erweitert: Quadrant (4 Kreis-Quadranten), Perpendicular (Fußpunkt vom Referenzpunkt), Tangent (beide Tangentenpunkte via acos), fromOffset-Helfer
- BlockAttribute: `BlockAttrDef` auf Definitionen, `attrValues` auf Instanzen, Rendering als Text-Primitives, PropertiesPanel-Formular
### Event-Bestuhlung (E1E7, vollständig)
- Sitzreihen/-blöcke mit Drag-Interaktion, ArcRows (Stühle tangential rotiert), Vorlagenkatalog (Kino, Bankett, Stehtisch, Podest), CSV-Sitzlistent-Export, Gäste-Verwaltung mit Stuhl-Zuweisung
- **Neu:** Vorhang (wellige Polyline), Scheinwerfer (Kegel-Polygon mit Winkel + Farbe), Absperrung (Kettenglieder), Bühnenhöhen-Attribut
### Bibliothek & Import/Export (F1F8, vollständig)
- Globale Block-Bibliothek: Ordner, Suche, Favoriten, Thumbnails, Grid-Ansicht, Drag&Drop in die Zeichenfläche
- Export/Import als JSON (`.wcadlib`) **und** echtes ZIP-Paket (manifest.json + blocks.json + thumbs/)
- **DXF-Import erweitert:** ELLIPSE, SPLINE (Grad ≤ 3, de-Boor-Approximation), MTEXT, POINT, ATTDEF, MINSERT-Arrays, strukturierter skippedEntities-Fehlerreport
- **DXF-Export erweitert:** BLOCKS+INSERTS mit Namens-Sanitizing, ATTDEF/ATTRIB/SEQEND, AC1009 (R12) + AC1015, R12-korrektes POLYLINE+VERTEX+SEQEND; Roundtrip-Tests deckten und fixten 3 latente Konventions-Bugs (Circle-Zentrum in Writer und Parser, ARC-Winkel-Radian→Grad)
- **SVG-Import als echte Geometrie:** voller Path-Parser (M/L/H/V/C/S/Q/T/A/Z, absolut+relativ, Bézier-Flattening, Arc-Endpoint→Center), rect/circle-Konventions-Fixes, Ellipse als Polygon
- Builtin-Kataloge: Event/Architektur/Landschaft (je 15 Blöcke) über LibraryProvider, User- und Global-Library angebunden
### Layouts & Plot (G1G5, vollständig)
- Layout-Datenmodell in Yjs (undo-getrackt), LayoutBar mit Viewport-Minimap, zoom-to
- Titelblock mit attributierten Texten (Projekt/Datum/Maßstab/Autor), auto-insert als Layout-Frame
- **PDF-Export je Layout:** A4 landscape im Ausgabemaßstab, Linienbreiten + Dash-Patterns aus Layern, monochrome-Option, Titelblock-Mitzeichnung
- **Browser-Print:** @media print mit A4 landscape, UI-Chrome ausgeblendet, Print-Service mit Cleanup
### Modi & Rollen (H1H5, vollständig)
- simple/pro-Modus (localStorage-persistiert): simple zeigt nur basic-Tools, CommandLine pro-only, Topbar-Toggle
- Projekt-Templates: 6 Branchen-Presets (Halle/OpenAir/Saal/Messe/Büro/Garten) mit Grid/Layern/Plugin-Seed
- **Rollenschutz:** library-admin (an admin-Rolle gebunden) für alle 8 schreibenden Global-Library-Routen; Lesen/Export für alle authentifizierten Nutzer
- i18n-Grundlage: strings.ts de/en-Maps mit t()-Funktion, Persistenz, useT-Hook; LayoutBar + Modus umgestellt
- Flächenberechnung: Shoelace-polygonArea, formatArea (quadratische Skalierung), measure-area-Tool, PropertiesPanel-Anzeige
### Behobene Fehlerklassen
- **BUG-1-Klasse komplett geschlossen:** alle Date.now()-Auto-IDs im Backend durch UUID-Slice ersetzt (deterministische Tests mit eingefrorener Zeit beweisen die Kollisionsfreiheit)
- Circle-Center-Konvention in DXF-Writer, DXF-Parser und SVG-Import vereinheitlicht (Zentrum statt min-corner)
- ARC-Winkel-Konvertierung dxf-parser (intern Radiant) → App (Grad) gefixt
### Tests
- **Frontend: 697 Tests** (alle grün), **Backend: 287 Tests** (alle grün)
- Volle tsc-Typechecks und Production-Builds grün bei jedem Task
+62 -9
View File
@@ -1,21 +1,74 @@
# Web CAD Neu # WebCAD
Web-basiertes 2D-CAD für Event-Bestuhlungspläne. Web-basiertes 2D-CAD mit Kollaboration, Plugin-System und Fokus auf
Veranstaltungsplanung (Bestuhlung, Bühnen, Layouts).
## Features
- **Zeichnen:** Linien, Rechtecke, Kreise, Bögen, Ellipsen, Splines, Polylinien, Text, MTEXT, Bemaßungen, Hatch
- **Modifizieren:** Move/Copy/Rotate/Scale/Mirror, Trim/Extend/Fillet/Chamfer, Stretch/Break/Join/Explode/Align/Lengthen, Polyline-Editor, Grips (Vertex/Center), Arrays (rect/polar/path)
- **Event-Tools:** Stühle, Sitzreihen (Drag), Sitzblöcke, ArcRows, Tische, Bühnen (mit Höhen-Attribut), Vorhang, Scheinwerfer, Absperrungen, Vorlagenkatalog, Gäste-Verwaltung mit Sitzplatz-Zuweisung, CSV-Sitzlistent-Export
- **Snaps:** Endpoint, Midpoint, Center, Intersection, Quadrant, Perpendicular, Tangent, Grid, Polar-Tracking
- **Bibliothek:** Globale Block-Bibliothek mit Ordnern, Suche, Favoriten, Thumbnails; Builtin-Kataloge (Event/Architektur/Landschaft); Export/Import als .wcadlib (JSON oder ZIP)
- **Import/Export:** DXF (Import mit ELLIPSE/SPLINE/MTEXT/POINT/ATTDEF + Fehlerreport; Export R12/2000 mit BLOCKS/ATTR), SVG (echte Geometrie-Konvertierung mit Path-Parser), PDF (je Layout, maßstabsgetreu), PNG, JSON
- **Layouts & Plot:** Layout-Verwaltung mit Viewport-Minimap, Titelblock mit Attributen, PDF-Export, Browser-Print
- **Kollaboration:** Yjs CRDT, kollaborativ korrektes Undo/Redo (nur eigene Operationen)
- **Modi:** simple (Grundwerkzeuge) / pro (Vollpalette), localStorage-persistiert
- **Templates:** 6 Branchen-Presets (Halle, OpenAir, Saal, Messe, Büro, Garten)
- **i18n:** de/en (Grundlage, erweiterbar über `src/i18n/strings.ts`)
- **Rollen:** admin (library-admin, globaler Schreibzugriff) / planer (lesen + eigene Zeichnungen)
## Stack ## Stack
- Frontend: React + TypeScript + Vite + HTML Canvas
- Backend: Node.js + TypeScript + Fastify + SQLite - **Frontend:** React 18 + TypeScript + Vite + PixiJS v8 (WebGL-Renderer, Canvas2D-Fallback) + zustandsfreier Service-Layer
- Collaboration: Yjs CRDT - **Backend:** Node.js + TypeScript + Fastify 5 + SQLite (better-sqlite3)
- **Kollaboration:** Yjs + y-websocket
- **Tests:** Vitest (697 FE + 287 BE), Playwright E2E, tsc-Strict-Mode
## Entwicklung ## Entwicklung
```bash ```bash
npm install # Backend
npm run dev cd backend && npm install && npm run dev # http://localhost:3001
# Frontend
cd frontend && npm install && npm run dev # http://localhost:5173
```
### Tests
```bash
cd frontend && npm test # 697 Vitest-Tests
cd backend && npm test # 287 Vitest-Tests
cd frontend && npx tsc --noEmit # Typecheck
cd frontend && npm run build # Production-Build
``` ```
- Frontend: http://localhost:5173
- Backend: http://localhost:3001
## Docker ## Docker
```bash ```bash
docker-compose up --build docker-compose up --build
``` ```
## Plugin-Entwicklung
Siehe [docs/PLUGIN_GUIDE.md](docs/PLUGIN_GUIDE.md) Plugin-API v2 mit
Werkzeugen, Panels und Bibliotheks-Providern, inkl. Minimal-Beispiel.
## Bibliotheks-Pakete (.wcadlib)
Siehe [docs/LIBRARY_SPEC.md](docs/LIBRARY_SPEC.md) JSON- und ZIP-Format,
Validierungsregeln, Endpoints und CAD-Element-Konventionen.
## Architektur-Regeln
- `kernel/**` importiert niemals React
- `render/pixi/**` kennt keine React-Components
- React kennt den Renderer nur über CanvasArea
- Eine Werkzeug-Aktion = eine `doc.transact()` = ein Undo-Schritt
## Roadmap & Status
Alle funktionalen Phasen (AH) sind abgeschlossen. Aktueller Stand und
Task-Log: [ROADMAP.md](ROADMAP.md).
+80
View File
@@ -0,0 +1,80 @@
# .wcadlib Block-Bibliotheks-Paket-Spezifikation
WebCAD-Block-Bibliotheken können als `.wcadlib`-Paket geteilt werden.
Es gibt zwei Formate:
## Format 1: JSON-Paket (F1)
Ein einzelnes JSON-File mit folgender Struktur:
```json
{
"format": "wcadlib",
"version": 1,
"exported_at": "2026-08-29T00:00:00.000Z",
"folders": [
{ "id": "f1", "name": "Veranstaltungstechnik", "parent_id": null }
],
"blocks": [
{
"name": "Stuhl-Reihe",
"folder_id": "f1",
"block_data": "[{...CADElement-Array als JSON-String...]}",
"svg_data": "<svg ...>"
}
]
}
```
- `block_data` = JSON-String eines **CADElement-Arrays**
- `svg_data` = eigenständiges SVG (Thumbnail), optional
- Ordner-Hierarchie via `parent_id` (Import ordnet IDs neu zu)
**Endpoints:** `GET /api/global-blocks/export`, `POST /api/global-blocks/import`
(schreibend: nur library-admin/Rolle admin, siehe H3)
## Format 2: ZIP-Paket (F7)
Ein echtes ZIP-Archiv mit fester Struktur:
```
library.wcadlib.zip
├── manifest.json { "format": "wcadlib-zip", "version": 1,
│ "exported_at": "...", "block_count": N, "folder_count": M }
├── blocks.json { "folders": [...], "blocks": [
│ { "name", "folder_id", "block_data",
│ "thumbnail": "thumbs/<id>.svg" } ] }
└── thumbs/
└── <blockId>.svg Thumbnails, Referenz aus blocks.json
```
- `manifest.format` MUSS `wcadlib-zip` sein (sonst 400)
- `blocks.json` MUSS vorhanden sein (sonst 400); `blocks`-Array Pflicht
- `thumbnail` verweist relativ auf `thumbs/<id>.svg`
**Endpoints:**
- `GET /api/global-blocks/export-zip``application/zip`
- `POST /api/global-blocks/import-zip` (Body = ZIP als application/zip;
schreibend: nur library-admin)
## Validierungsregeln (Import)
| Fehler | HTTP |
|---|---|
| Kein valides ZIP (Format 2) | 400 |
| blocks.json fehlt / invalides JSON | 400 |
| manifest.format unbekannt | 400 |
| Kein JSON/blocks-Array (Format 1) | 400 |
| Nicht authentifiziert | 401 |
| Rolle != admin (schreibend) | 403 |
## CAD-Element-Konventionen (block_data)
- `rect`: `x/y` = **Zentrum** der BBox
- `circle`/`arc`: `el.x/el.y` = **Zentrum**, Winkel in Grad (0° = 3 Uhr, CCW)
- `line`: `properties.x1/y1/x2/y2`
- `polyline`/`polygon`: `properties.points`
- `block_instance`: `properties.blockId` + optional `attrValues` (D12)
Beim Drop auf die Zeichenfläche werden Element-IDs neu generiert und
die Position per Drop-Punkt übersetzt der Bestand bleibt unverändert.
+139
View File
@@ -0,0 +1,139 @@
# 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);
```