Files
DOSSIER-STANDALONE/ARCHITECTURE.md
T
karim ca859c4aa4 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.
2026-06-30 20:52:27 +02:00

24 KiB
Raw Blame History

Architektur — Standalone Browser-BIM (cad)

Stand: 2026-06-29 Vision, Phasen und Backlog: ROADMAP.md. Konventionen: CONVENTIONS.md. Detail-Designs: docs/design/elements.md · docs/design/plans-output.md · docs/design/resources-graphics.md.

Dieses Dokument beschreibt, wie die Standalone-Browser-App gebaut wird und wie sie die Konzepte des DOSSIER-Rhino-Plugins in Browser-Äquivalente übersetzt. DOSSIER ist ein Rhino-8-Plugin (Python + React-WebView); cad ist die eigen­ ständige Browser-Variante: kein Rhino-Document, kein doc.Strings, kein IronPython — stattdessen ein eigenes typisiertes Datenmodell in TypeScript, gerendert über Three.js (3D) und SVG (Plan), gespeichert als JSON-Datei.

Alle Bezeichner im Code sind englisch (Vectorworks-Terminologie); Prosa und UI-Texte sind deutsch. Einheiten intern in Metern.


0. Leitprinzip — ein Modell, viele Darstellungen

Das semantische Gebäudemodell ist die einzige Wahrheit. Jede Sicht (3D, Grundriss, Schnitt, Ansicht) ist eine reine Ableitung (derive) daraus. Darstellung (Detailgrad, Stile, Schraffuren, Overrides) wird beim Rendern angewandt, nie in die Geometrie eingebacken.

                 ┌──────────────────────────────────────────┐
                 │   Project  (semantisches Modell, JSON)    │   ← einzige Wahrheit
                 │   resources · types · designLevels ·      │
                 │   layers · elements                       │
                 └──────────────┬───────────────────────────┘
                                │  pure derive()
        ┌───────────────────────┼────────────────────────────┐
        ▼                       ▼                             ▼
   Scene3D (Three.js)     PlanModel → SVG            SectionModel → SVG
   buildScene()           generatePlan()             generateSection() (HLR)
        │                       │                             │
        └────────── beide lesen dieselben joins/components/styles ──────────┘

Das steht im Spike bereits: generatePlan.ts und Viewport3D.tsx extrudieren dasselbe gehrte Band-Polygon (clippedBand). Diese Symmetrie ist der Kern und wird beim Ausbau strikt gehalten.


1. Repo-Struktur (Ziel)

Wächst aus dem heutigen src/ (model/plan/viewport/ui). Module sind klein und fachlich geschnitten — wir vermeiden bewusst den elemente.py-Monolithen (7244 LOC) aus DOSSIER (dort als Schwachstelle #4.1 dokumentiert).

src/
  model/
    types.ts            // Project, DesignLevel, LayerCategory, Element-Union  (existiert)
    geometry.ts         // 2D/3D-Mathe ohne Kernel                            (existiert)
    joins.ts            // Wand-Verschneidung (Gehrung; später Prio-T/X)      (existiert)
    sampleProject.ts    // Demo-Haus                                          (existiert)
    project.ts          // Factory, Defaults, Migrationen
    selectors.ts        // abgeleitete Reads (visibleCodes, elementsOnLevel…)
    ids.ts              // newId(prefix) — UUID-Erzeugung
    elements/           // pro Bauteil ein Modul (Daten + Generierung)
      wall.ts  opening.ts  slab.ts  stair.ts  roof.ts  structure.ts  space.ts
  resources/
    componentManager.ts hatchManager.ts lineManager.ts symbolLibrary.ts
    overrides.ts        // regelbasierte Engine (Condition → Action)
  store/
    store.ts            // Zustand-Store: { project, ui }, Aktionen, Undo/Redo
    persistence.ts      // Datei save/load (JSON), Autosave (IndexedDB)
    history.ts          // Undo/Redo-Ring
  plan/
    generatePlan.ts     // Grundriss aus Footprint + Symbolik (existiert)
    generateSection.ts  // Schnitt/Ansicht aus 3D-Projektion (HLR-Worker)
    PlanView.tsx        // SVG-Renderer + Pan/Zoom/Grips (existiert)
    primitives.ts       // Primitive-Union, SVG-Serializer, DXF/PDF-Export
  viewport/
    Viewport3D.tsx      // Three.js-Szene aus dem Modell (existiert)
    scene.ts            // buildScene(project) → THREE.Group (Layer-Spiegel)
    clip.ts             // Plan-/Schnitt-Clipping über THREE.Plane
    camera.ts           // Kamera-Presets, Norden-Rotation
  sheets/
    layout.ts           // Sheet/Viewport-Datenmodell
    SheetEditor.tsx     // Plansatz-Editor
    exportPdf.ts        // Vektor-PDF (svg → pdf-lib / jsPDF)
  workers/
    geometry.worker.ts  // Booleans (Öffnungen) + HLR via Comlink
  ui/
    App.tsx Navigator panels…    // React-Oberfläche (heute in App.tsx, wird gesplittet)

2. Datenmodell

2.1 Zwei unabhängige Achsen (DOSSIER-Modell, im Spike umgesetzt)

DOSSIER trennt zwei orthogonale Konzepte, persistiert als zwei getrennte JSON-Bäume (dossier_zeichnungsebenen, dossier_ebenen). Wir übernehmen das 1:1 in types.ts (existiert bereits als drawingLevels + layers):

  1. Zeichnungsebenen (DesignLevel / DrawingLevel) — die obersten Dokument-Abschnitte. Zwei produktive Arten:
    • Geschoss (kind:"floor"): floorHeight, cutHeight, baseElevation (akkumuliert via recomputeFloorElevations), visible/locked.
    • Schnitt/Ansicht (kind:"section"|"elevation"): linePoints, directionSign, Höhenbereich, Tiefe.
    • Zeichnung (kind:"drawing"): freie 2D-Ebene ohne Geschossbezug.
  2. Ebenen (LayerCategory) — das Grafik-Kategorie-Schema, das in jedem Geschoss gilt. Baum mit {code, name, color, lw, visible, locked, hatch?, children}. Codes 1:1 wie DOSSIER (DEFAULT_LAYER_SCHEMA aus launcher/src/App.jsx): 00 Raster · 01 Vermessung · 20 Wände (└21 Türen/Fenster, 22 Möbel, 25 Stützen) · 30 Decken · 31 Dächer · 35 Träger · 50 Text · 60 Plangrafik …

Ein Element kennt sein Geschoss (floorId) und seine Ebene (categoryCode). Sichtbarkeit ergibt sich aus dem Schnitt (Geschoss geschoss × code Matrix), genau wie DOSSIERs apply_visibility(z_mode, e_mode) in layer_builder.py.

Begriffsnotiz: Heute heißt der Typ im Code DrawingLevel. ROADMAP §5 nennt die Modell-Eingabe (Geschosse) Vectorworks-konform Design Layer und die abgeleitete Ausgabe Drawing Layer / Sheet. Wir behalten DrawingLevel als Union (kind=floor ≙ Design Layer, kind=section/elevation/drawing ≙ Drawing Layer) und führen Sheet erst separat ein (§2.5, plans-output.md). Kein Rename ohne expliziten Auftrag.

2.2 Ressourcen-Bibliotheken (Vectorworks-Stil)

Neuer Block Project.resources (heute provisorisch als flache materials[]). Alles verweist per id — zentral änderbar. Details: resources-graphics.md.

interface Resources {
  lineStyles: LineStyle[];   // Line Manager:  { id, name, weight(mm), color, dash:number[] }
  hatches:    Hatch[];       // Hatch Manager: { id, name, pattern, scale, angle, lineStyleId }
  components: Component[];    // Component Manager (= DOSSIER-Material erweitert):
                             //   { id, name, hatchId, color3d, texture3d?, joinPriority }
}

joinPriority ist DOSSIERs _MATERIAL_PRIO (Beton 800 … Putz 100) als Daten statt Hardcode — steuert die Prioritäts-T-Verschneidung (Risiko #1).

2.3 Aufbauten (mehrschichtige Typen)

interface WallType { id; name; layers: { componentId; thickness }[]; }
interface SlabType { id; name; layers: { componentId; thickness }[]; }

Schichtdicke liegt am Layer, Priorität am Component (so muss man Prio nur einmal pflegen). 3D und Plan lesen dieselben layers[].

2.4 Elemente

Diskriminierte Union Element (heute Wall | Door), erweitert um Window | Slab | Stair | Roof | Column | Beam | Space | Draw2d. Jedes Element: { id, type, floorId, categoryCode, ... }. Gehostete Elemente (Tür/Fenster) tragen hostWallId statt floorId (Geschoss ergibt sich aus der Wand). Volle Felder: elements.md.

2.5 Pläne / Output (eigene Achse)

interface Sheet {              // Plansatz-Blatt (≙ DOSSIER Layout/PageView)
  id; name; paper: "A0".."A4"|"Letter"; landscape: boolean;
  viewports: SheetViewport[];  // platzierte Ableitungen + Titelblock
  folder?: string;
}
interface SheetViewport {      // ≙ DOSSIER Detail + gebundener Ausschnitt
  id; rect: Rect; sourceViewId: string;  // referenziert DrawingLevel oder ViewSnapshot
  scale: number;               // 1:N
}
interface ViewSnapshot {       // ≙ DOSSIER Ausschnitt (View-Snapshot)
  id; name; folder?;
  camera: CameraState; scale: number; detailLevel: DetailLevel;
  visibility: VisibilityState;          // pro Geschoss/Ebene
  overrides?: { presetId?; enabled };
}

3. State, Persistenz, Undo/Redo

3.1 Store — Zustand

Heute: useState<Project> in App.tsx, immutabel via setProject. Das skaliert nicht für Werkzeuge + Undo. Migration auf Zustand (ROADMAP §4):

interface AppState {
  project: Project;          // das Dokument
  ui: {                      // flüchtig, NICHT persistiert/undobar
    activeLevelId; activeViewType; selection: string[];
    activeTool: ToolId; hover; cameraByView; planTransform;
  };
  // Aktionen mutieren via Immer-Producer; jede Modell-Mutation pusht History.
  apply(mutator: (p: Project) => void): void;
  undo(): void; redo(): void;
}

Trennung Modell ↔ UI ist hart: nur project ist undobar und wird gespeichert. ui (Auswahl, Kamera, aktives Werkzeug) lebt separat. Das ersetzt DOSSIERs sc.sticky-Bus — wo DOSSIER cross-modul über Sticky-Keys kommuniziert, lesen bei uns einfach alle Komponenten denselben Store (reaktiv). Kein Sticky, kein Bridge-Polling, keine is not None-Lücken (DOSSIER-Schwachstelle #4.4).

3.2 Persistenz — JSON-Datei statt doc.Strings

DOSSIER speichert alles in doc.Strings (Key-Value am Rhino-Document) + globale Presets als JSON-Dateien im User-Home. Browser-Äquivalent:

DOSSIER Browser (cad)
doc.Strings.SetString(key, json) Feld im Project-Objekt (ein JSON-Baum)
.3dm-Datei mit eingebetteten Strings .cad.json-Datei via File System Access API
Globale Presets (~/Library/.../*.json) LocalStorage (cad.presets.*) + Export/Import
Launcher schreibt Settings-Datei IndexedDB Autosave (Phase 5)
// persistence.ts
const FORMAT_VERSION = 1;
function serialize(p: Project): string            // JSON.stringify({ version, project })
function deserialize(text: string): Project        // + migrate(version) bis aktuell
async function saveToFile(p: Project): Promise<void>   // showSaveFilePicker → .cad.json
async function loadFromFile(): Promise<Project>        // showOpenFilePicker; Fallback <input type=file>
async function autosave(p: Project)                    // IndexedDB, debounced 2 s
  • Save/Load: File System Access API (showSaveFilePicker/showOpenFilePicker); Fallback Download-Blob + <input type=file> für Firefox/Safari.
  • Autosave/Recovery: IndexedDB (via idb), entkoppelt vom expliziten Speichern.
  • Cross-Projekt-Presets: LocalStorage mit Namespacing (cad.presets.overrides, cad.presets.wallTypes, …) + JSON-Export/Import — ersetzt DOSSIERs ~/Library/.../override_presets.json (overrides.py).
  • Migrationen: migrate(data, fromVersion)-Kette, additiv. DOSSIER macht das per Sticky-Prefix (traite_pause_dossier_); wir machen es versioniert im Datei-Schema.

3.3 Undo/Redo

DOSSIER überlässt Undo Rhino (und hat dadurch Cache-Stale-Bugs, Schwachstelle #4.3). Wir besitzen das Dokument selbst → eigener History-Ring:

// history.ts — Snapshots des project-Teilbaums (strukturelles Sharing via Immer-Patches)
interface History { undo: Patch[][]; redo: Patch[][]; }

Jede apply()-Mutation erzeugt Immer-Patches → Push auf undo. Da abgeleitete Sichten pure sind, ist nach undo() kein Cache zu invalidieren — der ganze Cache-Stale-Komplex aus DOSSIER (_JOINTS_CACHE_KEY, Material-Cache) entfällt strukturell.


4. Rendering

4.1 Three.js-Szene spiegelt den Ebenen-Baum

DOSSIER baut in Rhino eine Layer-Hierarchie Zeichnungsebene → CODE_Name-Sublayer (layer_builder.build_layers) und hängt jedes Objekt an den passenden Sublayer. Browser-Spiegel: ein THREE.Group-Baum mit identischer Struktur, gebaut aus dem Modell (nicht persistiert — reine Ableitung):

scene
└─ levelGroup[floorId]                 (y-Offset = baseElevation)   ← Geschoss
   └─ categoryGroup[categoryCode]      (userData.code)              ← Ebene
      └─ element meshes                (userData.elementId)
// scene.ts
function buildScene(project: Project, vis: VisibilityState): THREE.Group
function syncScene(root: THREE.Group, project, vis)   // diff statt full rebuild (Perf)
  • Sichtbarkeit: categoryGroup.visible = vis.codes.has(code) und levelGroup.visible = vis.floors.has(floorId) — entspricht DOSSIERs apply_visibility, aber als simples .visible-Toggle statt Layer-Table-Modify.
  • Farbe/Material: Component → MeshStandardMaterial (PBR später), per componentId gecacht (wie heute layerMaterial-Cache, aber projektweit).
  • „Grau/gesperrt"-Modi (DOSSIER grey/grey_locked): graues Override-Material auf der Gruppe statt Layer-Color-Tausch.

4.2 Grundriss = Clipping-Ebene + symbolische Generierung

DOSSIER zeigt den Grundriss eines Geschosses, indem es eine Clipping-Plane auf okff + schnitthöhe legt, Normale Z (layer_builder.update_clipping_plane). Zwei Browser-Pfade — beide nötig (ROADMAP §3):

  1. 3D-Viewport im Plan-Modus: THREE.Plane(normal=(0,0,-1), constant=cutZ) über renderer.clippingPlanes + material.clippingPlanes. clip.ts setzt die Ebene pro aktivem Geschoss (cutZ = baseElevation + cutHeight), Kamera orthografisch von oben. So sieht man den echten geschnittenen Volumenkörper.
  2. Symbolischer SVG-Grundriss (der Hauptweg, schnell + sauber): generatePlan.ts erzeugt Vektor-Primitive direkt aus Parametern — Wand-Schnittbänder, Öffnungs-Aussparungen, Tür-Schwenkbögen — ohne Mesh zu schneiden. Steht im Spike. Ausbau: Schraffuren, Lauflinien, Bemaßung, LoD (plans-output.md).

4.3 Schnitt/Ansicht = Kamera + Schnittebenen + HLR

DOSSIER setzt 12 Clipping-Planes (Cut + Back), Parallel-Projektion senkrecht zur Linie, zoomt auf die BBox (schnitte.activate_schnitt). Browser:

  • 3D-Vorschau: zwei THREE.Plane (Cut auf der Linie, Back in directionSign- Richtung um depthBack versetzt) + orthografische Kamera senkrecht zur Linie.
  • Vektor-Ergebnis (Risiko #4): Hidden-Line-Removal durch das zusammengebaute Gebäude → saubere Linien als SVG. Via OpenCascade.js (HLRBRep) im Web Worker (Comlink), Ergebnis gecacht pro (Schnitt, sichtbare Ebenen, Modell-Hash). Geschnittene Bauteile bekommen Component-Schraffur (Section-Style, resources-graphics.md). Details: plans-output.md.

4.4 SVG-Renderer + Geometrie-Booleans

  • Plan-Output: SVG (PlanView.tsx). Primitive-Union (polygon/line/arc/text/ hatch) → SVG-Elemente; derselbe Serializer speist DXF- und PDF-Export.
  • Öffnungs-Booleans (Risiko #2): Tür/Fenster schneidet Loch in Wand. Für 3D reicht heute das Aussparen per Segment-Extrusion (im Spike). Für exakte B-Rep- Verschneidung (und IFC) OpenCascade.js oder Manifold im Worker — Entscheidung in Phase 0 (ROADMAP §8): OCC = exakt/B-Rep, Manifold = schnell/Mesh.

5. React-Panel-Struktur

DOSSIER ist eine Sammlung getrennter WebView-Panels (EBENEN, ELEMENTE, GESTALTUNG, OBERLEISTE, MASSSTAB, AUSSCHNITTE, DIMENSIONEN, LAYOUTS, OVERRIDES), die über die Python-Bridge + sc.sticky kommunizieren. Im Browser ist alles eine SPA mit einem geteilten Store — die Panels werden zu Docking-Bereichen.

┌─────────────────────────────────────────────────────────────────────┐
│ TopBar      Werkzeuge · Ansichtstyp · Massstab · Snaps · Save/Load   │  ≙ OBERLEISTE+MASSSTAB
├──────────────┬──────────────────────────────────────┬───────────────┤
│ Navigator    │  Viewport (Three.js  oder  SVG-Plan)  │ Inspector     │
│ ┌──────────┐ │  ┌────────────────────────────────┐  │ ┌───────────┐ │
│ │Zeichnungs│ │  │  3D / Grundriss / Schnitt /    │  │ │ aktives   │ │  ≙ ELEMENTE/
│ │-ebenen   │ │  │  Ansicht — abgeleitet aus dem  │  │ │ Element + │ │    GESTALTUNG-
│ ├──────────┤ │  │  Modell                        │  │ │ Stil      │ │    Properties
│ │ Ebenen   │ │  └────────────────────────────────┘  │ └───────────┘ │
│ │ (Baum)   │ │                                       │ Manager-Tabs: │
│ │          │ │                                       │ Components ·  │  ≙ Resource-
│ └──────────┘ │                                       │ Hatch · Line  │    Manager
├──────────────┴──────────────────────────────────────┴───────────────┤
│ BottomBar    Koordinaten · Snaps · Statusmeldungen                   │
└─────────────────────────────────────────────────────────────────────┘
   Modal/Drawer:  Ausschnitte · Sheets/Layouts · Overrides · Kamera-Presets · SIA-Bilanz

Komponenten-Map (heute alles in App.tsx; wird gesplittet):

Bereich DOSSIER-Panel cad-Komponente
Zeichnungsebenen-Liste EBENEN (oben) Navigator/DrawingLevels.tsx
Ebenen-Baum EBENEN (Baum) Navigator/LayerTree.tsx (Spike: CategoryRow)
Top-Bar (View/Snaps/Massstab) OBERLEISTE, MASSSTAB TopBar.tsx
Element-Eigenschaften ELEMENTE, ELEMENT-PROPERTIES Inspector/ElementProps.tsx
Stil/Attribute der Auswahl GESTALTUNG Inspector/StylePanel.tsx
Component/Hatch/Line (Project-Settings, mass_style) managers/*Manager.tsx
View-Snapshots AUSSCHNITTE panels/Snapshots.tsx
Plansätze LAYOUTS sheets/SheetEditor.tsx
Regelbasierte Overrides OVERRIDES panels/Overrides.tsx
Bemaßung DIMENSIONEN panels/Dimensions.tsx
Element-Übersicht (BIM-Tree) ELEMENTE-ÜBERSICHT panels/ElementTree.tsx
Kamera-Presets KAMERA panels/CameraPresets.tsx

Werkzeug-System (ersetzt DOSSIERs Rhino-Command-Aliases cmd/wand.py etc.): ein Tool-Interface mit onPointerDown/Move/Up, das Vorschau-Primitive zurückgibt und beim Bestätigen store.apply() ruft. Pointer-Events auf Canvas/SVG mit Snap-Engine (Endpunkt/Mitte/Schnitt/Ortho/Raster). Tabelle der Tools: elements.md §8.


6. Rhino → Browser — Mapping-Tabelle

DOSSIER (Rhino-Plugin) cad (Standalone-Browser) Anmerkung
Rhino RhinoDoc Project (TS-Objekt im Store) einzige Wahrheit
doc.Strings[key]=json Feld im Project-JSON persistiert in .cad.json
.3dm-Datei .cad.json via File System Access API + IndexedDB-Autosave
Globale Presets (~/Library/*.json) LocalStorage + Export/Import cross-Projekt
sc.sticky (cross-modul Bus) Zustand-Store (reaktiv) kein Polling, kein None-Bug
panel_base.BaseBridge / WebView-IPC direkte React-Props/Store keine document.title-Hacks
document.title="RHINOMSG::" Polling entfällt SPA, kein WebView
Eto.Forms Satelliten-Fenster React-Modal/Drawer z.B. Ausschnitt-Settings
Rhino Layer-Tabelle (Sublayer-Baum) LayerCategory[]-Baum + THREE.Group-Spiegel scene.ts
layer_builder.build_layers buildScene (Ableitung) Gruppen statt Layer
layer.PlotWeight LineStyle.weight (mm) → SVG stroke-width massstabsabh. (§plans)
SectionStyle (Layer) Component-Schraffur beim Schnitt-Rendern resources-graphics.md
Clipping-Plane (AddClippingPlane) THREE.Plane + renderer.clippingPlanes clip.ts
vp.ChangeToParallelProjection THREE.OrthographicCamera camera.ts
vp.GetFrustum() → Massstab Frustum-Breite (ortho) → 1:N gleiche Mathe (§plans)
CoreGraphics DPI-Auto-Detect window.devicePixelRatio + CSS-px Browser kennt DPI nativ
Rhino.Geometry.Brep-Booleans OpenCascade.js / Manifold (Worker) Öffnungen, HLR
HLRBRep (über Rhino-Display) OpenCascade.js HLRBRep (Worker) Schnitt/Ansicht-Linien
Rhino-Grips + DisplayConduit Pointer-Events auf SVG/Canvas + Overlay grip-editing (elements.md)
Rhino.Input.Custom.GetPoint Tool-Pointer-Handler + Snap Werkzeug-System
MouseCallback (Doppelklick Schnitt) onDoubleClick auf SVG-Symbol plans-output.md
Rhino Undo/Redo eigener History-Ring (Immer-Patches) kein Cache-Stale
HatchPattern-Tabelle Hatch-Ressourcen + SVG <pattern> resources-graphics.md
Linetype-Tabelle LineStyle.dash[] → SVG stroke-dasharray resources-graphics.md
TextEntity / Rich-Text SVG <text> / HTML-Annotation plans-output.md
RhinoPageView (Layout) Sheet + SheetEditor plans-output.md
FilePdf Multi-Page-Export svg → pdf-lib/jsPDF (Vektor) plans-output.md
Swisstopo/OSM via .NET HttpClient fetch() (CORS-fähige STAC/Overpass-APIs) Phase 4
LV95↔WGS84 (Python-Formeln) dieselben Formeln in TS portiert Phase 4
IronPython 2.7/3 Runtime-Risiken entfällt (TS/Browser)

7. Was wir von DOSSIER übernehmen — und was nicht

Übernehmen (bewährte Konzepte):

  • Zwei-Achsen-Dokumentmodell (Zeichnungsebenen × Ebenen).
  • Layer-Codes/Farben/lw 1:1 (DEFAULT_LAYER_SCHEMA).
  • Component/Hatch/Line-Manager mit id-Verweisen + joinPriority.
  • Ansichtstypen = Kamera + optionaler Schnitt (vereinheitlicht).
  • LoD (darstellung: einfach/standard/detail) mit Dokument-Override.
  • Regelbasierte Overrides (Condition → Action, additive Priorität, reversibel).
  • Ausschnitte (View-Snapshots), Layer-Kombinationen, Massstab-pro-Viewport.
  • SIA-416-Räume, Norden-Rotation, Stil-Kataloge.

Bewusst anders (Browser-nativ, Schwachstellen vermeiden):

  • Kein Sticky-Bus → ein reaktiver Store (vermeidet DOSSIER #4.4/#4.5/#4.6).
  • Kein Monolith → Bauteil-Module statt 7244-LOC-elemente.py (#4.1).
  • Pure Ableitungen statt mutierter Doc-Objekte → kein Cache-Stale (#4.3), kein Undo/Redo-Loch.
  • Versioniertes Datei-Schema statt UserString-Sticky-Migration.
  • Persistenz im Project-JSON statt verteilt über doc.Strings.

Anti-Over-Engineering (aus DOSSIER-CONVENTIONS.md übernommen): keine Abstraktion ohne konkretes Problem; kein try/catch: {} als Bug-Versteck; erst grep/lesen, dann editieren; Wand-Geometrie nie „nebenbei" refactoren.


8. Reihenfolge bei Code-Arbeit

  1. Dieses Dokument + das relevante Detail-Design (elements/plans-output/ resources-graphics) lesen.
  2. Dann das betroffene Modul lesen (nicht raten).
  3. Erst danach editieren; Project immer immutabel via store.apply() ändern.
  4. Verifizieren: npx tsc -b, npm run build, Screenshot via node scripts/probe.mjs — Geometrie visuell prüfen.
  5. Dieses Dokument aktuell halten, wenn sich Patterns/Mapping ändern.