Files
DOSSIER-STANDALONE/docs/design/topbar-dossier.md
T
karim 3d2d4d6321 2D-Plan-Renderer auf WebGL2 (GPU) + akkumulierter Funktionsstand
Neuer GPU-Renderer fuer den Grundriss (src/plan/glPlan/): Earcut-Tessellierung
(konkav-faehig), gehrte Linienzuege (Miter), echte Papier-mm-Strichbreiten im
Massstab (repliziert den SVG-printStrokeVb-Pfad), Hybrid mit scharfem SVG-Text-
Overlay. GPU ist der Standardpfad; der SVG-Renderer bleibt automatischer Fallback,
falls WebGL2/Shader nicht verfuegbar sind. Imperativer Pan (rAF + CSS-transform)
fuer fluessige Interaktion ohne React-Re-Render je Frame.

Enthaelt zudem den bisher nicht committeten Arbeitsstand des Browser-BIM
(Oeffnungen, Treppen, Raeume, Decken, DXF-Export, Materialbibliothek, Kontext-
Import, Tauri-Compute-Boundary-PoC).
2026-07-02 00:12:39 +02:00

7.9 KiB
Raw Blame History

Oberleiste Angleichung an DOSSIER (Umsetzungs-Spezifikation)

Verbindliche Vorlage für den Umbau der Top-Bar (src/ui/TopBar.tsx, src/styles.css, Verdrahtung in src/App.tsx). Quelle: das DOSSIER-Rhino- Plugin (ToolbarApp.jsx, components/BarControls.jsx, TextEditorApp.jsx). Bezeichner englisch, UI-Text/Kommentare deutsch (CONVENTIONS.md).

0. Grundprimitive (neu, DOSSIER-konform)

Alle Leisten-Controls teilen dieselbe Höhe und Pillenform.

  • BAR_H = 22px Basis-Höhe. Segmentierte Pillen BAR_H + 2 = 24px (box-sizing:border-box, 1px Rand inbegriffen).
  • Pille: border:1px solid var(--border), border-radius:999px, background:var(--input). Hover (interaktiv): border-color:var(--accent-border), background:var(--accent-dim).
  • Aktiver Zustand (Toggle AN / aktive Segmentzelle): background:var(--accent), color:#fff.

BarCombo (Pillen-Dropdown)

Wir haben bereits src/ui/Dropdown.tsx. Der Dropdown-Trigger MUSS optisch der Pille entsprechen (Höhe 24, radius 999, obige Farben). Ein optionales Icon sitzt LINKS ausserhalb der Pille (18px breit, var(--muted)), ein optionaler Zahnrad-Knopf („settings") sitzt rechts innerhalb der Pille. Prüfen, ob Dropdown bereits so aussieht; falls nicht → Trigger-CSS angleichen (Klasse tb-dd-trigger). KEINE zweite Dropdown-Implementierung bauen.

Segmentpille (3er/4er)

Aussencontainer display:inline-flex; height:24px; border:1px solid var(--border); border-radius:999px; overflow:hidden. Zellen ohne eigenen Radius; interne Trenner über border-left:1px solid var(--border) (erste Zelle ohne). Aktive Zelle var(--accent)/#fff, inaktiv var(--input)/var(--ink), Hover var(--accent-dim)/var(--accent). Genutzt für: Ansichts-Icons, Zoom (%/fit/center), B/I/U, L/C/R.

BarButton (quadratischer Icon-Knopf)

22×22, border-radius:999px, sonst wie Pille. Aktiv = Akzentfüllung, Icon #fff.

  1. Marke (bestehend, unverändert).
  2. Ansichts-Gruppe (bestehend view-grid; Zellen auf Segmentpillen-Look bringen).
  3. Sichtbarkeits-Kombinationen (bestehend, BarCombo-Look).
  4. Detailgrad + Massstab (gestapelt, BarCombo-Look).
  5. Massstab/Zoom-Cluster NEU (siehe §2) — ersetzt die heutige Gruppe mit der DOPPELTEN Zoom-Anzeige.
  6. Darstellungsart (bestehend, BarCombo).
  7. Text-Gruppe NEU (siehe §3) — die zentrale neue Leiste.
  8. Referenzlinien / Linien-Modus (bestehend).
  9. Rechts: Layout · Ressourcen · Projektname.

2. Massstab/Zoom-Cluster (ersetzt Doppel-Zoom-Bug)

HEUTE FALSCH: In TopBar.tsx wird tb-zoom (Zoom %) ZWEIMAL gerendert (einmal im tb-zoomstack, einmal darunter als eigener <span>). Die zweite, lose <span className="tb-zoom">…%</span> ersatzlos ENTFERNEN.

NEUES Layout — 2×2-Raster (display:grid; grid-template-columns:auto auto; gap:4px 6px; align-items:center):

  • Spalte 1, beide Zeilen (grid-row:1 / span 2): EINE kombinierte Stat-Pille, width:70px, Höhe BAR_H*2+6 = 50px, border-radius:14px (NICHT 999), border:1px solid var(--border), background:var(--input), Innen zwei Zeilen mittig, getrennt durch 1px-Linie (var(--border)):
    • oben: Live-Massstab 1:N (Akzentfarbe, var(--font-mono), 11px, 700)
    • unten: Zoom NN% (var(--ink-2), mono, 11px)
    • „am Massstab" (Zoom==gewählter Massstab): Pille background:var(--accent-dim), border-color:var(--accent), Text var(--accent).
    • Nicht-Plan-Ansicht: beide Werte „—".
  • Spalte 2, Zeile 1: Massstab-Dropdown (BarCombo, ~140px, mono) + Print/PDF bleibt separat. (Massstab-Dropdown ist der bestehende scaleOptions-Dropdown.)
  • Spalte 2, Zeile 2: Zoom-Segmentpille mit 3 Zellen — % (=onZoom100, Label „1:1"/100 %), fit_screen (=onFit), center_focus_strong (=onFitSelection). Material-Icons. Daneben ggf. Referenzlinien-BarButton.

Export-Knöpfe (PDF/DXF) wandern in eine eigene kleine BarButton-Reihe rechts vom Cluster (Icons picture_as_pdf / download) ODER bleiben Pillen — Hauptsache NICHT mehr Teil des Zoom-Blocks, damit der Cluster ruhig bleibt.

3. Text-Gruppe in der Oberleiste (NEU Kern dieser Aufgabe)

Immer sichtbar. 3×2-Raster (grid-template-columns:110px 130px 80px; gap:4px 6px). Setzt Defaults für neuen Text UND formatiert die aktuelle Auswahl live.

Zeile 1:

  • Stil-Preset BarCombo (110px): Optionen aus DEFAULT_PRESETS (Titel/Untertitel/Label/Notiz) + „— Stil —".
  • Font BarCombo (130px): Systemfont-Liste (mind. Helvetica, Arial, Inter, Times New Roman, Georgia, Courier New). applyMark(doc,range,'font',v).
  • Grösse BarCombo (80px): Presets in pt [8,9,10,11,12,14,18,24,36,48]
    • „Eigene…" → Zahl-Input-Pille. applyMark(...,'sizePt',n).

Zeile 2:

  • B/I/U Segmentpille (110px, Icons format_bold/format_italic/ format_underlined): toggleMark(doc,range,'bold'|'italic'|'underline'). Aktiv-Zustand aus isMarkActive(doc,range,mark).
  • L/C/R Segmentpille (130px, Icons format_align_left/_center/_right): setzt paragraph.align im Bereich.
  • „+"-Text-Button (80px, BarButton/Pille, Icon add, Label „Text"): startet das Text-Werkzeug (neues Textobjekt platzieren). Falls das Text-Annotation- Werkzeug noch nicht existiert, Button vorerst disabled mit Tooltip (kein stiller No-Op) — aber Verdrahtung vorbereiten.

Auswahl-Bewusstsein (WICHTIG): Prop textTarget (oder aus App-State): entweder null (nichts Text-artiges selektiert → Controls setzen nur Defaults, Ränder normal) ODER { doc: RichTextDoc, range: TextRange|null, apply: (doc)=>void } für den aktuell selektierten Raumstempel/Text. Ist textTarget != null, tragen die Zeile-2-Pillen border-color:var(--accent) (Akzent-Glow), und alle Aktionen wirken auf textTarget.doc via textTarget.apply(newDoc). Ohne aktive Range (nur Objekt selektiert, kein Editor offen) wirkt Formatierung auf das GANZE Doc.

Quelle der textTarget-Daten: der Raum-Agent exponiert Stempel-Doc + Setter (setRoomStampDoc(roomId, doc)) im App-State (siehe Peer-Absprache). App leitet für den selektierten Raum {doc: room.stampDoc, range: activeStampRange, apply: d => setRoomStampDoc(room.id, d)} an die Text-Gruppe.

Doppelklick auf einen Raumstempel/ein Textobjekt öffnet ein schwebendes Dialog-Fenster (nicht den Footer, kein Panel-Aufklappen). Umsetzung: neue Komponente src/ui/TextEditorDialog.tsx — ein zentriertes/абgesetztes Fenster (~560×420, --shadow-3, border-radius:8px, Titel „Text bearbeiten", Kopf mit Schliessen-✕), Body = der bestehende src/text/RichTextEditor.tsx (er bringt seine eigene Mini-Toolbar mit — das ist hier ok, weil es ein eigenes Fenster ist), Fuss = „Abbrechen" / „Übernehmen". „Übernehmen" ruft setRoomStampDoc(roomId, editedDoc).

Der Doppelklick-Handler lebt in App (Plan-View/Viewport → onDoubleClick auf Stempel-Hit → openTextEditor(roomId)), NICHT im Footer. Falls der Raum-Agent den Footer benutzt hat: diesen Pfad entfernen und durch den Dialog ersetzen.

5. Farb-/Stil-Tokens

Bestehende CSS-Variablen weiterverwenden (--panel,--input,--border, --accent,--accent-dim,--accent-border,--ink,--ink-2,--muted, --font-mono,--shadow-1..3). KEINE neuen Farbwerte hart kodieren. Falls ein Token fehlt (z. B. --accent-border), prüfen und ggf. aus bestehenden ableiten.

6. i18n

Neue Keys in de.ts UND en.ts: text.style, text.font, text.size, text.size.custom, text.bold/italic/underline, text.align.left/center/right, text.add, text.add.hint, text.editTitle, text.apply, text.cancel, text.selectedHint. Presets-Namen über bestehende rt.*/Preset-Keys, sofern da.

7. Gate (Pflicht)

rm -f tsconfig.tsbuildinfo && npx tsc -b grün, npm run build grün, keine AI-Spuren (grep auf Claude/Anthropic/AI/Generated/Co-Authored), Boot-Probe (node scripts/probe.mjs) ohne Konsolenfehler, Screenshot der Oberleiste. KEIN Commit.