God-Files aufgeteilt (ResourceManager/generatePlan/toWalls3d/App.tsx-Handler), Totcode entfernt (geometry-Crate, OCCT/HLR-Spike), PDF/Bild-Import ergänzt.

ResourceManager.tsx, generatePlan.ts und toWalls3d.ts sind nach Bauteil-
Domäne in src/ui/resourceManager/, src/plan/generatePlan/ und
src/plan/toWalls3d/ aufgeteilt (reine Verschiebung, keine Verhaltens-
änderung). App.tsx verliert weitere Handler-Gruppen an eigene Hooks
(useExportHandlers, useImportHandling, useKeyboardShortcuts) sowie
Hit-Testing an src/viewport/planHitTest.ts.

Unbenutzte Rust-geometry-Crate (durch WASM-Joins abgelöst) sowie der alte
OCCT/HLR-Schnitt-Spike (hlr.ts/occt.ts, vite occt-Aliases) entfernt.

Neu: PDF-Import als Rasterbild (src/io/pdfImport.ts) + gemeinsamer
Bild-Asset-Helfer (src/io/imageAsset.ts) für Plan- und Layout-Bilder.

tsc -b sauber, vitest 891/891 grün.
This commit is contained in:
2026-08-19 22:45:41 +02:00
parent 06c0143d32
commit 1507c2bf00
85 changed files with 12461 additions and 11280 deletions
+48
View File
@@ -0,0 +1,48 @@
// Bild-Datei → eingebettete Data-URL + natürliche Pixelgrösse — gemeinsame
// Grundlage für „Bild einfügen" im Plan (Drawing2D `{shape:"image"}`) UND im
// Layout (LayoutAnnotation `kind:"image"`). Reine Browser-API (FileReader +
// Image), kein Tauri-fs nötig — funktioniert identisch im Web-Preview.
export interface LoadedImageAsset {
/** Data-URL (PNG/JPEG/…), direkt im Projekt einbettbar (kein externer Pfad). */
src: string;
naturalWidth: number;
naturalHeight: number;
}
/** Liest eine Bilddatei als Data-URL + ermittelt ihre natürliche Pixelgrösse. */
export function readImageFile(file: File): Promise<LoadedImageAsset> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onerror = () => reject(reader.error ?? new Error("read failed"));
reader.onload = () => {
const src = String(reader.result);
const img = new Image();
img.onload = () =>
resolve({ src, naturalWidth: img.naturalWidth || 1, naturalHeight: img.naturalHeight || 1 });
img.onerror = () => resolve({ src, naturalWidth: 1, naturalHeight: 1 });
img.src = src;
};
reader.readAsDataURL(file);
});
}
/** Default-Grösse (Modell-Meter) aus dem Pixel-Seitenverhältnis: feste Breite, Höhe folgt. */
export function defaultImageSizeM(
naturalWidth: number,
naturalHeight: number,
widthM = 2,
): { widthM: number; heightM: number } {
const ratio = naturalHeight / Math.max(1, naturalWidth);
return { widthM, heightM: Math.max(0.05, widthM * ratio) };
}
/** Default-Grösse (mm) fürs Layout-Blatt, analog mit anderer Default-Breite. */
export function defaultImageSizeMm(
naturalWidth: number,
naturalHeight: number,
widthMm = 60,
): { widthMm: number; heightMm: number } {
const ratio = naturalHeight / Math.max(1, naturalWidth);
return { widthMm, heightMm: Math.max(2, widthMm * ratio) };
}
+41
View File
@@ -0,0 +1,41 @@
// PDF-Import als Rasterbild: jede Seite wird auf ein `<canvas>` gerendert
// (pdfjs-dist, reine Browser-/Webview-API — kein Tauri-fs nötig) und als
// Data-URL zurückgegeben. Kein eigener PDF-Rendering-Pfad im Plan/Layout —
// eine importierte PDF-Seite ist danach ein ganz normales Bild-Element
// (Drawing2D `{shape:"image"}` bzw. LayoutAnnotation `kind:"image"`).
import * as pdfjsLib from "pdfjs-dist";
import type { LoadedImageAsset } from "./imageAsset";
// Worker-Bundle-URL über `import.meta.url` auflösen (Vite-Standardmuster für
// Asset-URLs) — braucht keine `?url`-Modul-Deklaration wie ein direkter Import.
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
"pdfjs-dist/build/pdf.worker.min.mjs",
import.meta.url,
).href;
/**
* Rastert alle Seiten einer PDF-Datei zu PNG-Data-URLs. `scale` steuert die
* Auflösung (2 ≈ Bildschirm-Retina-Schärfe bei üblichen Seitengrössen).
*/
export async function rasterizePdfPages(file: File, scale = 2): Promise<LoadedImageAsset[]> {
const buf = await file.arrayBuffer();
const pdf = await pdfjsLib.getDocument({ data: buf }).promise;
const pages: LoadedImageAsset[] = [];
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i);
const viewport = page.getViewport({ scale });
const canvas = document.createElement("canvas");
canvas.width = Math.max(1, Math.ceil(viewport.width));
canvas.height = Math.max(1, Math.ceil(viewport.height));
const ctx = canvas.getContext("2d");
if (!ctx) continue;
await page.render({ canvasContext: ctx, viewport, canvas }).promise;
pages.push({
src: canvas.toDataURL("image/png"),
naturalWidth: canvas.width,
naturalHeight: canvas.height,
});
}
return pages;
}