task(A3): pilot v2 tools select+line

This commit is contained in:
Agent Zero
2026-08-26 21:30:54 +02:00
parent ddfeadac58
commit 09a120d800
6 changed files with 340 additions and 1 deletions
@@ -0,0 +1,95 @@
/**
* core-drawing Built-in V2-Plugin (Task A3 Pilot).
*
* Werkzeuge: line (Migrations-Pilot aus dem Legacy-Switch).
* Elemente werden über CADDocument committed → kollaborativ korrektes Undo.
*
* Sitzungszustand (Startpunkt der aktuellen Linie) lebt als Modul-Member,
* NICHT in options: Options sind geteilter UI-Store-Zustand.
*/
import type { PluginV2, ToolExtensionV2, FullToolContext } from '../../types';
import type { CADElement } from '../../../types/cad.types';
import type { Pt } from '../../../tools/modification/geometry';
const uid = (prefix: string): string =>
`${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 9)}`;
/** Startpunkt der laufenden Linie (nur innerhalb einer down→up-Sitzung gesetzt). */
let lineStart: Pt | null = null;
function buildLine(start: Pt, end: Pt, ctx: FullToolContext): CADElement {
const stroke = (ctx.options.strokeColor as string | undefined) ?? '#e0e0f0';
const layerId = (ctx.options.layerId as string | undefined) ?? 'layer-0';
return {
id: uid('line'),
type: 'line',
layerId,
x: (start.x + end.x) / 2,
y: (start.y + end.y) / 2,
width: Math.abs(end.x - start.x),
height: Math.abs(end.y - start.y),
properties: { x1: start.x, y1: start.y, x2: end.x, y2: end.y, stroke, strokeWidth: 1 },
} as unknown as CADElement;
}
/**
* line-Werkzeug: down setzt Startpunkt, move zeigt Live-Vorschau,
* up committet via doc.transact (1 Linie = 1 Undo-Schritt).
*/
export const lineTool: ToolExtensionV2 = {
manifest: {
id: 'line',
label: 'Linie',
icon: '\u2571',
ribbonTab: 'canvas',
tags: ['basic'],
description: 'Zeichnet eine Linie (Klick Start, Klick Ende)',
},
optionsSchema: [
{ key: 'strokeColor', label: 'Farbe', type: 'color' },
],
handlers: {
down(e, ctx) {
lineStart = e.world;
ctx.setStatus(`Linie ab (${lineStart.x.toFixed(0)}, ${lineStart.y.toFixed(0)}) Ende wählen`);
},
move(e, ctx) {
if (!lineStart) return;
ctx.setPreview?.(buildLine(lineStart, e.world, ctx));
},
up(e, ctx) {
const c = ctx as FullToolContext;
if (!lineStart || !c.doc) {
lineStart = null;
return;
}
const el = buildLine(lineStart, e.world, c);
c.doc.transact(() => {
// addElement öffnet inneres transact — Yjs verschachtelt sie → EIN Undo-Schritt
c.doc!.addElement(el);
});
lineStart = null;
ctx.setPreview?.(null);
ctx.setStatus(`Linie erstellt (${el.id})`);
},
cancel(ctx) {
lineStart = null;
ctx.setPreview?.(null);
ctx.setStatus('Linie abgebrochen');
},
},
};
/** V2-Plugin: Kern-Zeichenwerkzeuge (Pilot: nur line). */
export const coreDrawingPlugin: PluginV2 = {
manifest: {
id: 'core-drawing',
name: 'Zeichnen (Kern)',
version: '1.0.0',
author: 'web-cad team',
description: 'Grundlegende Zeichenwerkzeuge (V2-Pilot: Linie).',
category: 'tools',
enabledByDefault: true,
},
tools: [lineTool],
};
@@ -0,0 +1,63 @@
/**
* core-modify Built-in V2-Plugin (Task A3 Pilot).
*
* Werkzeuge: select (Pilotportierung aus Legacy handleSelectDown).
* Pilot-Scope: Klick-Auswahl inkl. Group-Bewusstheit via SelectionBridge;
* Marquee/Box-Auswahl Migration folgt in A4 (Legacy bleibt parallel aktiv).
*/
import type { PluginV2, ToolExtensionV2 } from '../../types';
/**
* select-Werkzeug: Klick wählt Element direkt (Shift=additiv, Ctrl=subtraktiv).
*/
export const selectTool: ToolExtensionV2 = {
manifest: {
id: 'select',
label: 'Auswahl',
icon: '\u2196',
ribbonTab: 'canvas',
tags: ['basic'],
description: 'Elemente anklicken zum Auswählen',
},
optionsSchema: [],
handlers: {
down(e, ctx) {
const bridge = (ctx as import('../../types').FullToolContext).selection;
if (!bridge) return;
const additive = e.shift;
const subtractive = e.ctrl;
const hit = bridge.hitTest(e.world.x, e.world.y, 5);
if (!hit) {
// Leerer Klick: Selektion leeren (außer additiv)
if (!additive && !subtractive) bridge.setIds([]);
return;
}
let ids = new Set(bridge.getIds());
if (subtractive) {
ids.delete(hit.id);
} else {
ids.add(hit.id);
if (!additive) ids = new Set([hit.id]); // ohne Shift: nur das getroffene
}
bridge.setIds(Array.from(ids));
ctx.setStatus(`Selektion: ${ids.size} Element(e)`);
},
},
};
/** V2-Plugin: Kern-Bearbeitungswerkzeuge (Pilot: nur select). */
export const coreModifyPlugin: PluginV2 = {
manifest: {
id: 'core-modify',
name: 'Bearbeiten (Kern)',
version: '1.0.0',
author: 'web-cad team',
description: 'Auswahl und Bearbeitung (V2-Pilot: Selektion).',
category: 'tools',
enabledByDefault: true,
},
tools: [selectTool],
};
+7
View File
@@ -27,11 +27,18 @@ export type {
// Built-in plugins
export { eventToolsPlugin } from './builtin/eventTools';
export { coreDrawingPlugin } from './builtin/core-drawing';
export { coreModifyPlugin } from './builtin/core-modify';
import { pluginRegistry } from './PluginRegistry';
import { eventToolsPlugin } from './builtin/eventTools';
import { coreDrawingPlugin } from './builtin/core-drawing';
import { coreModifyPlugin } from './builtin/core-modify';
/** Register all built-in plugins */
export function registerBuiltinPlugins() {
pluginRegistry.register(eventToolsPlugin);
// API v2 (Task A3):
pluginRegistry.registerV2(coreDrawingPlugin);
pluginRegistry.registerV2(coreModifyPlugin);
}
+18
View File
@@ -153,6 +153,24 @@ export interface ToolContext {
setPreview?(el: CADElement | null): void;
}
/** Selektions-/Treffer-Bridge für Werkzeuge (ab A3, von CanvasArea injiziert). */
export interface ToolSelectionBridge {
/** Aktuell selektierte Element-IDs. */
getIds(): string[];
/** Setzt die Selektion vollständig. */
setIds(ids: string[]): void;
/** Element-Treffer an Weltposition (oder null). */
hitTest(worldX: number, worldY: number, tolerancePx: number): CADElement | null;
}
/** Erweiterter Kontext für Tools mit Dokument-/Selektionszugriff (A3). */
export interface FullToolContext extends ToolContext {
/** Dokumentzugriff (transact/add/update/delete). */
doc?: import('../kernel/document/CADDocument').CADDocument;
/** Selektion + HitTest-Bridge. */
selection?: ToolSelectionBridge;
}
/** V2-Werkzeug mit eigenen Pointer-Handlern (ersetzt langfristig ToolExtension). */
export interface ToolExtensionV2 {
manifest: ToolManifestPart & { description?: string };