Zweite Leichen-Runde: weitere tote Exporte + orphaned ribbonItems.ts entfernt

ts-prune nach dem ersten Durchgang erneut laufen lassen. compute/index.ts
(101 Z., PoC-Brücke zu einem nie gebauten Rust-Command, im eigenen
Kopfkommentar schon als ungenutzt markiert) komplett gelöscht. ribbonItems.ts
war nur noch für das bereits gelöschte RibbonBar.tsx da (kein anderer
Importeur mehr) — ebenfalls komplett weg.

Einzelne tote Typen/Konstanten: SiaLabel (roomArea.ts), GeoImportResult
(geoContext.ts, plus den dadurch verwaisten GeoOrigin-Import), die drei
MATERIAL_LIBRARY_*-Konstanten (library.ts), ResolveContext-Re-Export
(parametricWalls.ts), roofTotalThickness (types.ts — Duplikat, die echte
Rechnung läuft in toWalls3d/bands.ts inline), DropTarget (panelDrag.tsx).

resolveBackground (generatePlan/shared.ts) bewusst NICHT gelöscht: anders als
das analoge resolveForeground wird es nirgends aufgerufen, obwohl
backgroundSource für Wand/Decke im Modell und im Attribut-Panel existiert —
sieht nach einer echten Lücke aus (By-Layer-Hintergrundfarbe kommt nie im
Renderer an), nicht nach totem Code.

tsc -b / vitest run (891/891) / npm run build grün.
This commit is contained in:
2026-08-20 20:01:26 +02:00
parent 003fde4320
commit bdbd87bf85
10 changed files with 4 additions and 260 deletions
-101
View File
@@ -1,101 +0,0 @@
// Compute-Grenze: der EINE Einstiegspunkt für rechenintensive Operationen.
// War als Brücke zu einem Rust-`#[tauri::command]` gedacht (PoC), aber
// ungenutzt: kein Aufrufer im Frontend, und die Rust-Seite (`compute_joins` +
// das `geometry`-Crate) wurde entfernt (ungenutzter Totcode, s. PENDENZEN.md).
// `computeJoins` läuft daher immer über den TS-Fallback; die übrigen
// Funktionen waren schon vorher reine TS-Pass-throughs.
import type { Project, Wall } from "../model/types";
import { getWallType, wallTypeThickness } from "../model/types";
import { wallReferenceOffset } from "../model/wall";
import { computeJoins as computeJoinsTS } from "../model/joins";
import type { WallCuts } from "../model/joins";
import type { Line } from "../model/geometry";
import { detectRooms as detectRoomsTS } from "../geometry/roomBoundary";
import type { WallSegment, DetectRoomsOptions } from "../geometry/roomBoundary";
import { parseDwg as parseDwgTS } from "../io/dwgParser";
import type { DxfImportResult } from "../io/dxfParser";
import type { Vec2 } from "../model/types";
// Tauri-invoke, wenn vorhanden — sonst null (Browser/kein Tauri). Dynamischer,
// GEGUARDETER Import, damit der Build ohne @tauri-apps/api grün bleibt.
async function tauriInvoke<T>(cmd: string, args: Record<string, unknown>): Promise<T | null> {
try {
// @ts-ignore optionales Paket
const mod = await import(/* @vite-ignore */ "@tauri-apps/api/core").catch(() => null);
if (!mod || typeof (mod as any).invoke !== "function") return null;
return await (mod as any).invoke(cmd, args) as T;
} catch {
return null;
}
}
/** Serialisierbare Wand-Repräsentation für den Rust-Kern. */
interface FlatWall {
id: string;
start: Vec2;
end: Vec2;
thickness: number;
referenceOffset: number;
}
/** Rohantwort des Rust-Befehls `compute_joins`. */
interface RustWallCut {
wallId: string;
startCut: Line | null;
endCut: Line | null;
}
/**
* Gehrungs-Schnittlinien pro Wand. Unter Tauri via Rust-Kern, sonst TS-Fallback.
* Die Signatur ist synchron-kompatibel zur TS-Impl, aber async (Rust-Aufruf).
*/
export async function computeJoins(
project: Project,
walls: Wall[],
): Promise<Map<string, WallCuts>> {
try {
const flattened: FlatWall[] = walls.map((w) => {
const thickness = wallTypeThickness(getWallType(project, w));
return {
id: w.id,
start: w.start,
end: w.end,
thickness,
referenceOffset: wallReferenceOffset(w, thickness),
};
});
const res = await tauriInvoke<RustWallCut[]>("compute_joins", {
input: { walls: flattened },
});
if (res != null) {
const map = new Map<string, WallCuts>();
for (const c of res) {
map.set(c.wallId, { startCut: c.startCut, endCut: c.endCut });
}
return map;
}
} catch {
// fällt unten in den TS-Pfad
}
console.warn("Rust compute_joins nicht verfügbar, TS-Fallback");
return computeJoinsTS(project, walls);
}
// ── Pass-throughs (noch reines TS; bereit für spätere Rust-Migration) ─────────
/** Raumerkennung aus Wandsegmenten. Aktuell TS; Grenze für spätere Migration. */
export async function detectRooms(
walls: WallSegment[],
options: DetectRoomsOptions = {},
): Promise<Vec2[][]> {
return detectRoomsTS(walls, options);
}
/** DWG-Import (LibreDWG-WASM). Aktuell TS; Grenze für spätere Migration. */
export async function parseShapeFromDwg(
data: ArrayBuffer,
): Promise<DxfImportResult> {
return parseDwgTS(data);
}
-8
View File
@@ -124,14 +124,6 @@ export const SIA_CATEGORIES: readonly SiaCategory[] = [
"AGF",
] as const;
/** Deutsche Bezeichnungen (Kurz + Lang) je SIA-416-Schlüssel. */
export interface SiaLabel {
/** Kürzel, z. B. „HNF". */
code: SiaKey;
/** Ausgeschriebene deutsche Bezeichnung. */
name: string;
}
const SIA_LABELS: Record<SiaKey, string> = {
HNF: "Hauptnutzfläche",
NNF: "Nebennutzfläche",
-7
View File
@@ -8,7 +8,6 @@
// Bezeichner englisch, Kommentare deutsch (CONVENTIONS.md).
import Delaunator from "delaunator";
import type { GeoOrigin } from "./lv95";
import type { ContextObject, Contour, ImportedMesh } from "../model/types";
/** Fachliche Kategorie einer importierten Kontext-Linie. */
@@ -39,12 +38,6 @@ export interface GeoFeature {
/** Default-Gebäudehöhe (m), wenn keine Höhe aus der Quelle vorliegt. */
export const DEFAULT_BUILDING_HEIGHT = 9;
/** Ergebnis eines Import-Laufs: Features + verwendete Origin. */
export interface GeoImportResult {
origin: GeoOrigin;
features: GeoFeature[];
}
/** Menschlicher Layer-/Objektname je Kategorie (deutsch, für die Kontext-Liste). */
const CATEGORY_LABEL: Record<GeoCategory, string> = {
building: "Gebäude",
+2 -2
View File
@@ -107,8 +107,8 @@ export type LockResult = { ok: true } | { ok: false; info: LockInfo | null };
/**
* Holt (ungeguardet) die Tauri-`invoke`-Funktion, oder `null` ohne Tauri-Core.
* Anders als das Muster in compute/index.ts bzw. plan/nativeSync.ts schluckt
* dieser Helfer KEINE Fehler aus dem eigentlichen Aufruf — ein `Err(...)` vom
* Anders als das Muster in plan/nativeSync.ts schluckt dieser Helfer KEINE
* Fehler aus dem eigentlichen Aufruf — ein `Err(...)` vom
* Rust-Command (hier: „schon gelockt", inkl. {@link LockInfo}-Payload) muss bis
* zum Aufrufer durchgereicht werden.
*/
-4
View File
@@ -24,10 +24,6 @@ export interface MaterialAsset {
maps: Partial<Record<MaterialMapKind, string>>;
}
export const MATERIAL_LIBRARY_SOURCE = "ambientCG (ambientcg.com)";
export const MATERIAL_LIBRARY_LICENSE = "CC0 1.0 Universal (Public Domain)";
export const MATERIAL_LIBRARY_RESOLUTION = "2K";
export const MATERIAL_LIBRARY: MaterialAsset[] = [
{
id: "Concrete048",
-4
View File
@@ -561,9 +561,5 @@ function isExteriorWall(wall: Wall, ctx: RuleCtx): boolean {
return isOnEdge(wall.start) && isOnEdge(wall.end);
}
// ── Re-Export der Kontexttypen für Unit-Tests / Integration ──────────────
// (ermöglicht Import ohne Direktverweis auf interne RuleCtx)
export type { ParametricContext as ResolveContext };
// DrawingLevel-Re-Export für Konsumenten, die keinen eigenen types-Import wollen.
export type { DrawingLevel };
-4
View File
@@ -2353,10 +2353,6 @@ export const roofLayers = (project: Project, roof: Roof): Layer[] => {
return [{ componentId: "", thickness: Math.max(0, roof.thickness) }];
};
/** Gesamtdicke eines Daches (Meter, senkrecht zur Fläche) = Summe der Schichten. */
export const roofTotalThickness = (project: Project, roof: Roof): number =>
roofLayers(project, roof).reduce((s, l) => s + Math.max(0, l.thickness), 0);
/**
* Löst den {@link DoorType} einer Tür-Öffnung auf, oder `undefined` (keine
* `typeId` bzw. unbekannt ⇒ Inline-Verhalten). Bewusst NICHT werfend — der
+2 -3
View File
@@ -172,9 +172,8 @@ export function ToolIcon({ id }: { id: ToolId }) {
// ── Kategorien ───────────────────────────────────────────────────────────────
// Werkzeuge/Befehle in drei Rubriken sortiert (Nutzerwunsch: „sortiere nach 2D
// und 3D und Bearbeiten"). Befehlsnamen spiegeln die Ribbon-Registry
// (ui/ribbon/ribbonItems.ts) — dort liegt die kanonische Liste; hier nur die
// Panel-Gruppierung (2D-Zeichnen · 3D/BIM-Bauteile · Bearbeiten-Aktionen).
// und 3D und Bearbeiten"): reine Panel-Gruppierung (2D-Zeichnen · 3D/BIM-
// Bauteile · Bearbeiten-Aktionen).
const TOOLS_2D: ToolId[] = ["line", "polyline", "rect", "circle", "arc", "text", "textbox", "image"];
const TOOLS_3D: ToolId[] = ["wall", "ceiling", "window", "door", "stair", "room"];
const COMMANDS_3D = ["column", "ceilingopening", "roof", "extrude", "sectionline", "viewline"];
-6
View File
@@ -36,12 +36,6 @@ const DRAG_THRESHOLD = 5;
export const DEFAULT_FLOAT_W = 300;
export const DEFAULT_FLOAT_H = 360;
/** Ein aufgelöstes Ablageziel beim Loslassen. */
export type DropTarget =
| { kind: "join"; dock: DockId; group: number; index?: number }
| { kind: "newgroup"; dock: DockId; at: number }
| { kind: "float"; x: number; y: number };
/** Laufender Drag-Zustand (für Geist + Hervorhebung). */
interface DragState {
panelId: string;
-121
View File
@@ -1,121 +0,0 @@
// Ribbon-Registry (docs/design/ribbon-ui-plan.md §Architektur) — EINE
// datengetriebene Beschreibung aller Ribbon-Elemente. Die Ribbon-Tabs UND eine
// spätere modulare Custom-Bar (Phase 4) sind bloß zwei Ansichten über dieselbe
// Registry, ohne Sonderweg pro Element.
//
// Ein `RibbonItem` ist entweder ein Werkzeug (`tool` → onSelectTool, wie die
// Werkzeug-Sidebar) oder ein Engine-Befehl (`command` → engine.start). Beide
// münden in exakt DENSELBEN Pfad wie Klick in der alten Sidebar bzw. das Tippen
// des Befehlsnamens — keine Duplikate.
//
// Bezeichner englisch, UI-Text/Kommentare deutsch (CONVENTIONS.md). Sichtbare
// Texte kommen als i18n-Schlüssel und werden erst in der Komponente über t(...)
// aufgelöst.
import type { ToolId } from "../../tools/types";
/** Ein aktivierbares Ribbon-Element. Bei `command` wird die Beschriftung aus der
* Befehls-Registry (`getCommand(name).labelKey`) abgeleitet — kein doppeltes
* Pflegen von Labels. */
export type RibbonItem =
| { kind: "tool"; id: ToolId }
| { kind: "command"; name: string };
/** Eine benannte Gruppe von Elementen innerhalb eines Tabs. */
export interface RibbonGroup {
titleKey: string;
items: RibbonItem[];
}
/** Kennung eines Ribbon-Tabs (fest, für Tab-State + i18n). */
export type RibbonTabId = "2d" | "bim" | "custom";
/** Ein Ribbon-Tab: Beschriftung + gruppierte Elemente. */
export interface RibbonTab {
id: RibbonTabId;
labelKey: string;
groups: RibbonGroup[];
}
const c = (name: string): RibbonItem => ({ kind: "command", name });
/**
* Die Ribbon-Tabs (2D·BIM·Eigene) — keine Tab-Reiter mehr in der UI (v12,
* siehe layout.ts): App rendert alle drei permanent nebeneinander (kein
* Umschalter). Reine Werkzeug-Elemente (`t(...)`, persistentes Werkzeug wie
* Wand/Linie/Fenster) leben AUSSCHLIESSLICH im ToolsPanel (kein Doppel-
* Auftritt). Reine Befehls-Elemente (`c(...)`, Einmal-Aktionen wie
* Verschieben/Kopieren/Säule setzen ohne eigenen Werkzeug-Modus) bleiben hier,
* da sie im Panel keine Entsprechung haben. Ansichts-/Zoom-/Darstellungs-
* Steuerung + Text-Formatierung sitzen direkt in App (ViewRibbonTab), nicht
* in dieser Registry.
*/
export const RIBBON_TABS: RibbonTab[] = [
{
id: "2d",
labelKey: "ribbon.tab.2d",
groups: [
{
titleKey: "ribbon.group.modify",
items: [
c("move"),
c("copy"),
c("rotate"),
c("mirror"),
c("offset"),
c("trim"),
c("join"),
c("measure"),
],
},
],
},
{
id: "bim",
labelKey: "ribbon.tab.bim",
groups: [
{
// "extrude" war vormals im (jetzt entfernten) 3D-Ribbon-Tab
// (Ribbon3dTab) — einziger Zugang zum Befehl, daher hier statt
// gestrichen.
titleKey: "ribbon.group.components",
items: [c("column"), c("ceilingopening"), c("roof"), c("extrude")],
},
{
titleKey: "ribbon.group.sections",
items: [c("sectionline"), c("viewline")],
},
],
},
{
// Modulare Custom-Bar (Phase 4): der Nutzer stellt sich die Elemente selbst
// zusammen (persistiert). Gerendert aus `customItems`, nicht aus `groups`.
id: "custom",
labelKey: "ribbon.tab.custom",
groups: [],
},
];
/** Stabiler Schlüssel eines Items für die Persistenz der Custom-Bar. */
export function itemKey(item: RibbonItem): string {
return item.kind === "tool" ? `tool:${item.id}` : `command:${item.name}`;
}
/** Parst einen Item-Schlüssel zurück (oder null bei unbekanntem Präfix). */
export function itemFromKey(key: string): RibbonItem | null {
const i = key.indexOf(":");
if (i < 0) return null;
const kind = key.slice(0, i);
const rest = key.slice(i + 1);
if (kind === "tool") return { kind: "tool", id: rest as ToolId };
if (kind === "command") return { kind: "command", name: rest };
return null;
}
/**
* Alle datengetriebenen Items (aus den Werkzeug-/Befehls-Tabs) — die wählbare
* Menge für den Custom-Bar-Picker. „views"/„custom" tragen keine `groups`.
*/
export const ALL_RIBBON_ITEMS: RibbonItem[] = RIBBON_TABS.flatMap((tab) =>
tab.groups.flatMap((g) => g.items),
);