Browser-BIM (cad): semantisches Modell, abgeleitete 2D/3D-Sichten, Zeichenwerkzeuge

Standalone-Browser-Port von DOSSIER. Enthaelt das semantische Modell mit
Plan-/3D-Ableitung, Zeichen- und Editierwerkzeuge, Rhino-artiges Befehlssystem,
dockbares Panel-System, Resource-Manager, DXF/.lin/.pat-Import, i18n (de/en)
sowie Projektdokumentation und Probe-Harness.
This commit is contained in:
2026-06-30 20:52:27 +02:00
commit ca859c4aa4
157 changed files with 37921 additions and 0 deletions
+176
View File
@@ -0,0 +1,176 @@
// Sichtbarkeits-Kombinationen — Persistenz (localStorage).
//
// Speichert benannte Sichtbarkeits-Sätze (Vectorworks: „Ebenenkombinationen"
// / „Zeichnungskombinationen") für zwei unabhängige Achsen:
// • Ebenen-Kombination (LayerCombo) — pro Kategorie-`code` → sichtbar?
// • Zeichnungs-Komb. (DrawingCombo) — pro DrawingLevel-`id` → sichtbar?
//
// Analog zu panels/layout.ts: reine Speicher-Helfer OHNE React-Abhängigkeit,
// sämtlicher localStorage-Zugriff in try/catch + storage()-Guard (SSR/Tests/
// Privatmodus). Bezeichner englisch, Kommentare deutsch (CONVENTIONS.md).
//
// Hinweis: Diese Sätze liegen (wie die benannten Layouts) im localStorage und
// sind damit GERÄTE-/Browser-lokal, nicht im Projekt-Modell. Sollen sie mit dem
// Projekt mitwandern (Export/Teilen), gehören sie später ins `Project` (siehe
// Report).
/** Eine gespeicherte Ebenen-Kombination: Kategorie-Code → sichtbar?. */
export interface LayerCombo {
codes: Record<string, boolean>;
}
/** Eine gespeicherte Zeichnungs-Kombination: DrawingLevel-Id → sichtbar?. */
export interface DrawingCombo {
ids: Record<string, boolean>;
}
/** Präfix der localStorage-Schlüssel für Ebenen-Kombinationen. */
const LAYER_PREFIX = "cad.layercombos.";
/** Präfix der localStorage-Schlüssel für Zeichnungs-Kombinationen. */
const DRAWING_PREFIX = "cad.drawingcombos.";
// ── localStorage-Sicherung (wie panels/layout.ts) ───────────────────────────
/** Liefert das localStorage-Objekt oder `null`, wenn nicht verfügbar. */
function storage(): Storage | null {
try {
if (typeof localStorage === "undefined") return null;
return localStorage;
} catch {
// Zugriff kann werfen (z. B. blockierte Cookies/Privatmodus).
return null;
}
}
/** Liest und parst einen JSON-Wert; `null` bei Fehlen/ungültig. */
function readJson<T>(key: string): T | null {
const ls = storage();
if (!ls) return null;
try {
const raw = ls.getItem(key);
if (raw === null) return null;
return JSON.parse(raw) as T;
} catch {
return null;
}
}
/** Schreibt einen JSON-Wert; liefert `false`, wenn das Schreiben scheitert. */
function writeJson(key: string, value: unknown): boolean {
const ls = storage();
if (!ls) return false;
try {
ls.setItem(key, JSON.stringify(value));
return true;
} catch {
// Kontingent überschritten oder Zugriff verweigert — still ignorieren.
return false;
}
}
/** Löscht einen Schlüssel; liefert `true`, wenn der Zugriff gelang. */
function removeKey(key: string): boolean {
const ls = storage();
if (!ls) return false;
try {
ls.removeItem(key);
return true;
} catch {
return false;
}
}
/** Listet alle Namen unter einem Präfix (alphabetisch sortiert). */
function listNames(prefix: string): string[] {
const ls = storage();
if (!ls) return [];
const names: string[] = [];
try {
for (let i = 0; i < ls.length; i++) {
const key = ls.key(i);
if (key && key.startsWith(prefix)) {
names.push(key.slice(prefix.length));
}
}
} catch {
return [];
}
return names.sort();
}
/**
* Typwächter für eine `Record<string, boolean>`-Map (toleriert Fremddaten aus
* localStorage). Liefert eine bereinigte Map (nur boolesche Werte), sonst `null`.
*/
function asBoolMap(v: unknown): Record<string, boolean> | null {
if (typeof v !== "object" || v === null) return null;
const out: Record<string, boolean> = {};
for (const [k, val] of Object.entries(v as Record<string, unknown>)) {
if (typeof val === "boolean") out[k] = val;
}
return out;
}
// ── Ebenen-Kombinationen ─────────────────────────────────────────────────────
/** Speichert eine Ebenen-Kombination unter einem Namen. `true` bei Erfolg. */
export function saveLayerCombo(
name: string,
codes: Record<string, boolean>,
): boolean {
if (!name) return false;
const value: LayerCombo = { codes };
return writeJson(LAYER_PREFIX + name, value);
}
/**
* Lädt eine Ebenen-Kombination (nur die `codes`-Map). Fehlt sie oder ist sie
* beschädigt, kommt `null` zurück (so erkennt der Aufrufer „nicht gefunden").
*/
export function loadLayerCombo(name: string): Record<string, boolean> | null {
const raw = readJson<unknown>(LAYER_PREFIX + name);
if (raw === null || typeof raw !== "object") return null;
return asBoolMap((raw as { codes?: unknown }).codes);
}
/** Listet die Namen aller gespeicherten Ebenen-Kombinationen (alphabetisch). */
export function listLayerCombos(): string[] {
return listNames(LAYER_PREFIX);
}
/** Löscht eine Ebenen-Kombination. `true`, wenn der Zugriff gelang. */
export function deleteLayerCombo(name: string): boolean {
return removeKey(LAYER_PREFIX + name);
}
// ── Zeichnungs-Kombinationen ─────────────────────────────────────────────────
/** Speichert eine Zeichnungs-Kombination unter einem Namen. `true` bei Erfolg. */
export function saveDrawingCombo(
name: string,
ids: Record<string, boolean>,
): boolean {
if (!name) return false;
const value: DrawingCombo = { ids };
return writeJson(DRAWING_PREFIX + name, value);
}
/**
* Lädt eine Zeichnungs-Kombination (nur die `ids`-Map). Fehlt sie oder ist sie
* beschädigt, kommt `null` zurück.
*/
export function loadDrawingCombo(name: string): Record<string, boolean> | null {
const raw = readJson<unknown>(DRAWING_PREFIX + name);
if (raw === null || typeof raw !== "object") return null;
return asBoolMap((raw as { ids?: unknown }).ids);
}
/** Listet die Namen aller gespeicherten Zeichnungs-Kombinationen (alphabetisch). */
export function listDrawingCombos(): string[] {
return listNames(DRAWING_PREFIX);
}
/** Löscht eine Zeichnungs-Kombination. `true`, wenn der Zugriff gelang. */
export function deleteDrawingCombo(name: string): boolean {
return removeKey(DRAWING_PREFIX + name);
}