Commit Graph

73 Commits

Author SHA1 Message Date
karim a308ba62d2 Projekt-Settings-Dialog + Library Phase A + Material-Merger
- Project-Settings-Dialog (Voreinstellungen Geschoss/Schnitt + Material-Editor)
  ueber Zahnrad-Icon in Oberleiste; Defaults werden in schnitte.pick_schnitt
  + GeschossManager als Vorgabe genommen, pro-Element-Werte unangetastet
- Dossier-Library Phase A (lokal, read-only): rhino/library.py + LibraryBrowser
  Satellite; Seed-Manifest unter ~/Library/Application Support/Dossier/library/
- Material-Merger: _get_all_materials(doc) merged builtin _MATERIAL_LIBRARY
  mit Projekt-Settings-Materialien (inkl. Library-Imports); Wand-Erstellung,
  Sub-Layer-Anlage + Elemente-Material-Dropdown ziehen jetzt aus dem Merge

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 02:19:09 +02:00
karim ee01c7ebdc Snap-Bar + Drag-Reorder + Schnittperspektive + Top-View Z-Guard + Multi-Geschoss-Clipping
Snap-Bar (Oberleiste):
- 4x2 Icon-Grid mit architektonischen Osnap-Modi (End/Mid/Int/Perp/Cen/Near)
- Master-O Toggle + Grid-Sichtbarkeit
- Symbol-Wahl angelehnt an Rhinos eigene Snap-Marker
- Ortho + Grid-Snap raus (in Rhino-Footer)
- Backend: _osnap_flag_map + _get/_set_osnap_modes + _set_grid_visible

Drag-to-Reorder (GeschossManager):
- HTML5-Drag auf jeder Zeile
- Drop-Indikator (accent-Border oben/unten je nach Cursor-Position)
- Gedraggte Row faded auf opacity 0.4
- Array-Reorder + onChange triggert recalcOkff -> OKFFs konsistent

Schnittperspektive:
- projection: 'parallel' | 'perspective' im Schnitt-Settings-Dialog
- Augenhoehe (cameraHeight) nur bei Perspektive sichtbar
- activate_schnitt mit ChangeToPerspectiveProjection(50 FOV)
- skip_view=True bei Grip-Drag-Re-Activate damit View nicht ploetzlich
  in Section springt

Top-View Z-Guard:
- _is_active_view_top_like + _suppress_z_drift_if_top_view in
  _on_object_replaced — bei Plan-View wird Z-Drift einer source-curve
  automatisch zurueckgerollt (gegen ungewolltes Snappen auf z!=0 oder
  Gumball-Z)

Multi-Geschoss-Clipping-UX:
- Klick auf cut-Icon einer nicht-aktiven Geschoss-Zeile aktiviert das
  Geschoss mit + toggelt Clipping → Plane erscheint sofort

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 23:44:12 +02:00
karim 736325fba1 Wand-Grips + Schnitt-Grips + Referenz-Sublayer pro Bauteil + Print-Auto-Hide
Custom-Grip-Overlays via DisplayConduit + MouseCallback:
- wand_grips.py: dicke klickbare Marker an wand_axis-Endpunkten, auch
  wenn die Referenz-Layer ausgeblendet ist. GetPoint mit fixem Anker.
- schnitt_grips.py: 3 Marker pro Schnitt (P1, P2, Mid). Mid translatiert
  ganze Linie, P1/P2 verschieben Endpunkt. Hide Clipping-Planes waehrend
  GetPoint damit kein Verbots-Cursor durch Locked-Edges erscheint.
  skip_view=True bei Re-Activate damit Drag nicht in Section springt.

Referenz-Architektur umgebaut:
- wand_axis + oeffnung_point liegen jetzt unter <Geschoss>::20_Waende::
  20r_Referenz statt eigener top-level 19_Referenzlinien-Ebene.
- Migration v4 zieht existierende Sources auf den neuen Pfad.
- Toggle in Oberleiste keyword-driven: findet alle 'Referenz'-Sub-Ebenen
  rekursiv, toggelt alle Praefixe gemeinsam. Bauteil-uebergreifend.

Oberleiste-Layout:
- Druck-Ansicht-Button hoch neben Massstab-Dropdown (Reihe 1).
- Referenzlinien-Toggle in Reihe 2 neben Zoom-Pill, symmetrisch zum
  Druck-Button. Zoom-Pill auf 3 Buttons reduziert.
- Print-View AN → Referenz-Layer automatisch ausblenden, Snapshot
  restored beim Ausschalten.

Fix: clear_schnitt_clipping respektiert Mode=Locked nicht — vor Delete
auf Normal-Mode wechseln + Modify damit's persistiert. Schnitt-Loeschen
raeumt Clipping-Planes jetzt sauber auf.

Fix: Schnitt-Doppelklick-Handler aktiviert nur bei expliziter Schnitt-
Auswahl, ignoriert andere Selektionen.

Fix: _send_state Selection-Detection mit Source-ODER-Volume-Fallback —
Fenster-Properties erscheinen jetzt auch wenn oeffnung_point auf hidden
Referenz-Layer liegt.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 20:58:06 +02:00
karim 059cbf8d4d Schnitt/Ansicht-Feature + Terrain-Volumen + Geschoss-Add-Dialog
Schnitt-Feature V1+V2:
- Neues rhino/schnitte.py mit Pick-Workflow, Activation (Clipping-Planes +
  Parallel-View), 2D-Plan-Symbol auf 18_Schnittlinien-Sublayer
- Doppelklick auf Symbol aktiviert den Schnitt
- Schnitt-Settings (cutAtLine/Tiefe/Höhen/Blickrichtung) im GeschossSettingsDialog
- View-Snapshot + Restore beim Wechsel Schnitt → Geschoss
- Symbol-Cleanup bei Delete via normalem Ebenen-Menü

Terrain als Volumen:
- swisstopo.volumize_terrain_object: Skirt + Bottom-Cap auf Mesh/Brep
  damit Clipping-Planes gefuellte Querschnitte erzeugen
- UI im SwisstopoApp mit Nachbearbeitung-Section + Tiefen-Eingabe

Geschoss-Add mit Dialog:
- + im GeschossManager oeffnet 3-Optionen-Picker (Geschoss/Schnitt/Zeichnung)
- Geschoss-Dialog mit Anker-Dropdown, Position über/unter, Auto-Name,
  Höhen-Prefill aus Anker

Fix: _send_state fallback — Element gilt als selektiert wenn Source ODER
Volume in der Selection ist (robust gegen Layer-Visibility wenn Referenz-
linien-Layer im aktuellen Mode versteckt ist)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 18:28:59 +02:00
karim 3277f61ced Oeffnungen-Sublayer + Sturzlinien + Referenz-Layer + Pill-Inputs + Anordnen-Pill
- Oeffnungen-Subtree (Rahmen/Glas/Tuerblatt/Sims/Pane/Schwung/Sturz) als
  nested Children unter WAENDE im dossier_ebenen-Tree registriert + per-Kind
  Material (Glas mit Transparenz)
- Sturzlinien bei 1:100 Tueren mit Innen/Aussen/Beide/Keine-Dropdown
- Referenzlinien-Layer (19) als eigene Ebene fuer wand_axis + oeffnung_point
- Swisstopo Patch-Terrain (Brep.CreatePatch) ersetzt das falsche Loft
- Pill-Style fuer alle Inputs zentral via index.css
- 2x2 Anordnen-Pill in der Oberleiste (BringToFront/Forward/Backward/SendToBack
  via Rhinos DisplayOrder, kein Z-Offset)
- Chevron-Verschiebung in Ebenen-Panel ohne dass Siblings shiften
- Fix: _update_ebene_field walked nur Top-Level, nested Sublayer-Style-
  Changes wurden nicht persistiert
- Fix: Sturz-Linetype wurde bei jedem Wand-Regen zurueckgesetzt

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 16:07:44 +02:00
karim 655d368c92 Tueren-Schwung: Live-Update + Hinge-Position auf Wand-Innenkante
- Schwung-Curves-Block VOR dem Volume-Replace-Pfad — sonst greifen
  openAngle/hingeSide/swingInvert-Aenderungen nicht (Volume-Anzahl
  bleibt gleich → continue → Swing-Skip).
- Hinge-Punkt sitzt nicht mehr auf der Wand-Achse sondern auf der
  Wand-Innenkante (half_d * inside-Richtung). Tuerblatt + Bogen
  beginnen damit an der Wandflucht statt in der Wandmitte.
- Rotation-Vorzeichen korrigiert (rad = open_angle * sweep_sign *
  aus_sign) damit Tuere wirklich ins Innere schwingt.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 13:23:44 +02:00
karim 0c5f8055a5 Fenster/Tueren LoD + Stile + Phase-3-Ausschnitt-Darstellung + UI-Konsistenz
Fenster/Tueren:
- 3-stufige SIA-400-Darstellung pro Element: einfach (1:100, flache
  Scheibe ohne Tiefe in Wand-Mittelebene), standard (1:50, Rahmen +
  Glas + Sims), detail (1:20, Doppelverglasung).
- Aussenseite-Flag mit Auto-Detection aus der Click-Richtung beim
  Setzen — Sim sitzt automatisch aussen. Im Panel als Umkehren-Toggle.
- Tueren-Rahmen-Typ Zarge|Block — Blockrahmen ragt seitlich raus.
- Rahmen-Offset (m von Wand-Innenseite) ersetzt das 3-Preset Lage-
  Feld. Wirkt auch in der einfachen Darstellung (Pane sitzt auf der
  Rahmen-Mittelebene, nicht in Wand-Mitte).
- Sims nur AUSSEN. Innen entfaellt — der Sim ist gleichzeitig der
  visuelle Indikator fuer die Aussenseite.
- Oeffnungs-Stile: list/save/delete-API mit 6 Default-Presets
  (Fenster Standard/Gross/Bandlage, Tuer Innen/Eingang/Verglast).
  Style-ID per UserString am Objekt persistiert. Im Panel BarCombo
  mit "Aktuelle als Stil speichern…". Beim Rhino-Command "Stil"-
  Option zum Picken vor dem Klick.

Ausschnitt-Darstellung (Phase 3):
- Doc-Level Override dossier_aktive_darstellung gewinnt vor per-
  Object-Setting. Wechsel triggert Regen aller Oeffnungen via neuer
  regenerate_all_oeffnungen-API.
- Ausschnitt-Capture speichert die Darstellung mit, Restore wendet
  sie an und regeneriert.
- Oberleiste-Quick-Switch BarCombo mit 4 Optionen.
- AusschnittSettings-Dialog: Darstellungs-Dropdown.

Gestaltung (SectionStyle Phase 2):
- _set_section_style schreibt per-Object SectionHatchIndex/Scale/
  Rotation/Color mit Multi-Fallback (Property-Namen varieren je
  Rhino-Build). _selection_summary liest die selben zurueck.
- HatchEditor als shared Component fuer Fill + Section.
- geometryKind ignoriert DOSSIER-Source-Curves damit Wand-Selektion
  (Axis + Volume) als 3D klassifiziert wird.

UI-Konsistenz Panels:
- Ebenenkombi zurueck als eigene Section oben im Ebenen-Panel,
  Modelldarstellung-Dropdown an die freigewordene Position in der
  Oberleiste (Row 1 Col 2 im 2x2-Preset-Block).
- BarCombo erweitert: stretch-Prop (Pill waechst auf Container-
  Breite), onSecond/secondIcon/secondTitle fuer 2. Trailing-Button,
  gearIcon-Prop. Plus-Slot immer ganz aussen rechts, Settings-Slot
  direkt nach dem Caret.
- Ebenen + Zeichnungsebenen visuell kohaerent: identisches Padding
  (1px 12px 1px 0), Chevron/Spacer-Slot 12px, Master-Row mit Eye
  16x16 + Lock 14x14, gleiche Border + Borderfarbe. Eye-Icons in
  beiden Panels untereinander ausgerichtet.
- Properties-Container ohne Border (war zuvor accent-gruen, dann
  border — User wollte gar nichts mehr).
- ElementList raus aus dem Elemente-Panel (Uebersicht via Tree-
  Window erreichbar). NeuesElement bleibt voll sichtbar bei
  Selektion (kein Collapse), Properties oben.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 12:34:15 +02:00
karim 9ae8574ab0 Panel-Konsistenz: Gestaltung context-aware + Tabellen-Cleanup
Gestaltung:
- Backend detektiert geometryKind (curve / curveOpen / 3d / mixed),
  ignoriert DOSSIER-Source-Curves (wand_axis etc.) damit die
  Klassifikation einer Wand-Selektion (Axis + Volume) als reines 3D
  durchgeht.
- UI zeigt sektion-spezifische Section-Heads: 2D-Curve → Fill+Pen,
  3D-Solid → Section Style+Boundary. Section Style + Fill nutzen
  jetzt einen gemeinsamen HatchEditor (gleiche Controls).
- _set_section_style schreibt per-Object SectionHatchIndex/Scale/
  Rotation/Color via _try_set_attr-Multifallback.
- Selection-Summary liest dieselben Properties zurueck.
- ColorBar als Pill (borderRadius 999) statt eckig.
- "Attribute"-Header oben raus (war alt + redundant).

Ebenen / Zeichnungsebenen:
- Aktive Zeile wieder als Pill (borderRadius 999) — aber ohne
  Margin/Shift, Inhalt springt nicht. Kein bold-text-change mehr.
- Linker borderLeft komplett raus — vorher leere graue Spalte am
  Panel-Rand sichtbar.
- Inhalt mehr nach links (Padding 8/6 statt 12).
- Zeilen-minHeight 24, kompaktere Icons.
- EbenenSettings: Ebene-Picker auf BarCombo.
- GeschossManager: Gebaeudehoehe-Zeile raus, Stift→Settings-Icon.

ElementeApp:
- Alle btn-contained/btn-outlined → BarToggle (Wand-Aufbau, Raum-
  Align, Treppe-Lage/Modus, Oeffnung-Ref/Rahmen/Fluegel/Glas,
  AutoOverride).
- ReferenzSelector → BarToggle.
- "Neues Element"-Container ohne Box-Border, Header-Slot
  beherbergt Projektuebersicht-Pill statt label.
- Property-Labels (UK/OK/Dicke/...) von text-muted auf
  text-secondary (war zu blass).
- Gruener Accent-Border um Properties-Containern → normaler border.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 01:17:58 +02:00
karim 15fb0a6037 Elemente: BIM Project Browser + Properties-Satellite-Window
Zwei neue Satellite-Windows (analog Kamera/Text-Editor):

1) Projekt-Übersicht (elemente_uebersicht.py + ElementeUebersichtApp.jsx)
   - Tree Geschoss → Kind → Element-Instanzen
   - Suche + Kind-Filter-Chips
   - Klick = selektieren in Rhino, Shift+Klick = zoomen
   - Erreichbar via account_tree-Button im Elemente-Panel-Header

2) Properties-Satellite (elemente_properties.py + ElementePropertiesApp.jsx)
   - Eigenes Fenster mit der PropertiesView (gemeinsame Komponente)
   - Live-Updates: elemente._send_state forwarded zu satellite-bridge via sticky
   - Erreichbar via open_in_new-Icon oben rechts in der Properties-Karte
   - Inline-Properties im Panel bleiben — Satellite ist für mehr Platz

Plus ElementeApp-Cleanup:
- ElementList (alle Elemente-Liste) raus — wird jetzt von Projekt-
  Übersicht abgedeckt.
- Properties springen bei Selektion nach oben, NeuesElement bleibt
  voll sichtbar darunter (kein Scrollen mehr).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 01:17:31 +02:00
karim d5bcee2157 UI-Konsistenz: shared BarControls + Tabellen-Look fuer Panels
Pill-basierte Toolbar-Primitiven aus OberleisteApp extrahiert nach
src/components/BarControls.jsx — BarCombo (Dropdown), BarButton
(Icon-Button), BarToggle (Label/Icon mit Active-State), BAR_H=22.

OberleisteApp nutzt jetzt die geteilten Komponenten (Verhalten
unveraendert).

EbenenManager + GeschossManager:
- Sichtbarkeits-Toolbar: native <select> + btn-icon-sm → BarCombo
  (mit visibility-Icon links) + BarButton add/settings.
- GeschossManager Stift-Icon (edit) → Settings-Icon.
- Zeilen-Layout: eckig statt Pill (margin 0, borderRadius 0,
  3px Accent-Strip links fuer aktive Zeile), minHeight 24, gap 4,
  kompaktere Padding/Icon-Sizes — Vectorworks-naeher.

DimensionenApp:
- Welt/CPlane: 2x BarToggle statt btn-contained/outlined
- Z-Selektor: 3x BarToggle (icon-only)
- Drehen-Apply + 90°-CCW/CW: BarButton mit rotate_*-Icons (4
  Preset-Buttons -90/-45/45/90 ersetzt durch 2 schnelle 90°-Buttons
  — passt besser in die schmale Sidebar)

README aktualisiert: Runtime jetzt CPython 3.9, TextEntity-RTF-Limit
dokumentiert, BarControls + text_editor/text_create erwaehnt.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 23:56:33 +02:00
karim 26c7d9e67d Text-Editor: Rhino RTF-Dialekt + Round-Trip + Oberleiste-Sync
Lange Iteration mit dem Rhino TextEntity-RTF-Parser (siehe MEMORY:
rhino_textentity_rtf_limits.md). Finale Form:

- RTF-Body: per-Segment {\fN\cfN\b\i\ulnone seg}-Groups, \par
  zwischen Groups als Linebreak, { }-Space-Group fuer Leerzeilen
  (Rhino collapsed sonst aufeinanderfolgende \par). \fs (Font-Size)
  ist NICHT unterstuetzt → eine Size pro TextEntity (global).
- htmlToRuns: emittiert \n VOR Block-Elementen wenn schon Content
  davor — fixt nested <div>A<div>B</div></div> die sonst A+B ohne
  Trenner als ein Run liefern.
- Round-Trip-Erhaltung: editor.innerHTML 1:1 als UserString
  "dossier_text_html" persistiert, beim Reopen direkt gesetzt
  (kein runsToHtml-Konvertieren das Zeilen verlieren kann).
- Oberleiste-Editing: in-place modify von obj.Geometry + Commit-
  Changes statt Duplicate+Replace (Mac Rhino gibt False zurueck
  bei RichText-Klonen). Plus _patch_rtf_b_i_ul: regex-flippt
  \b/\b0, \i/\i0, \ul/\ulnone global in der RTF damit Bold/Italic/
  Underline OFF in der Oberleiste auch wirklich auf DOSSIER-Texte
  greift (per-Segment-Codes wuerden te.Font-Aenderung sonst
  uebersteuern).
- Stil-ID am Text persistiert + von read_selection_settings
  zurueckgelesen → Stil-Dropdown spiegelt Selektion.
- Editor neu: V-Align (Top/Middle/Bottom), Mask-Type (None/Viewport/
  Solid) mit Farb-Picker, Case-Transform (upper/lower/capitalize/
  invert), Masstaeblich-Toggle (AnnotationScalingEnabled),
  Symbol-Popover, Frame-Optionen.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 23:56:16 +02:00
karim ae80185064 Text-Editor: Toggle "Masstäblich" (AnnotationScaling)
Neuer Pill-Button in Row 3 neben "Zur Kamera": setzt
TextEntity.AnnotationScalingEnabled. Property-Name wird in mehreren
Varianten probiert (Rhino 8 API hat das je nach Build leicht anders
benannt). Zustand wird zusaetzlich als UserString
"dossier_text_scaled" persistiert, sodass open_for_edit den Toggle
auch dann korrekt restored wenn die API-Property nicht gelesen
werden kann.

Default = an (entspricht aktuellem Verhalten: Text skaliert mit der
Annotation-Scale des Layouts).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 13:23:30 +02:00
karim 6b3421e7af Text-Editor: V-Align + Mask-Type/Color + Case-Transform
Erweitert den Editor um die fehlenden Rhino-Text-Optionen:

- Vertical Alignment (Top/Middle/Bottom) als 3 Pill-Buttons in Row 2.
  Backend: text_create._apply_valign mit TextVerticalAlignment-Enum.
- Mask-Type Dropdown (None/Viewport/Solid). Bei Solid erscheint ein
  Mask-Color-Picker. MaskMargin disabled wenn Type=none.
  Backend: schaltet te.MaskEnabled + te.MaskUsesViewportColor +
  te.MaskColor entsprechend.
- Case-Dropdown in Row 2: upper/lower/capitalize/invert. Wirkt nur
  auf die Selektion via execCommand insertText.

open_for_edit liest valign + maskType/Color/Margin aus bestehendem
TextEntity zurueck, damit beim Re-Open der Zustand erhalten bleibt.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 13:08:51 +02:00
karim 211078d229 Text-Editor: Style-Picker focus-restore + Runs-Round-Trip beim Reopen
User-Bugs:
1. Stil-Picker setzt Toolbar-State aber Editor zeigt alle Texte gleich
2. Beim Reopen (Doppelklick auf DOSSIER-Text) ist Editor leer/unformatiert

Fix 1 — applyStyle focus-restore:
Style-Picker stiehlt Focus → savedRange ist da, aber execCommand
ohne Focus auf editable schreibt nichts. Fix: editorRef.focus() +
restoreSelection() VOR execCommand. Plus Size jetzt via inline-span
(execCommand fontSize geht nur 1-7 Scale, kein Pixel).

Fix 2 — Runs persistieren als UserString fuer Round-Trip:
- _commit: runs als JSON in attrs.SetUserString("dossier_text_runs")
  abgelegt zusammen mit dem dossier_text-Tag
- open_for_edit: liest UserString, parsed JSON → initial_runs
- Bridge INIT: sendet initialRuns mit
- Frontend onMessage INIT: wenn initialRuns vorhanden → runsToHtml()
  baut HTML mit spans (font-family, font-size, color) + Tags (b/i/u/
  sup/sub) — Editor zeigt jetzt beim Reopen das tatsaechliche reiche
  Format statt PlainText im Default-Helvetica

runsToHtml: pro run text/style-Splittung per \n → <br>. Escaping fuer
&<> damit kein XSS / Parse-Fehler.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 02:13:42 +02:00
karim 1596bbd941 Text-Editor: B/I/U sync via queryCommandState + RTF-Reihenfolge fixen
User: B/I/U Toggle-Buttons hinken hinterher oder zeigen invertiert.
Im WYSIWYG sieht alles richtig aus aber Rhino zeigt nur die LETZTE
gesetzte Schriftart fuer alles.

Fix 1 — B/I/U sync:
- selectionchange-Listener pollt jetzt queryCommandState fuer
  bold/italic/underline und syncen das an Toolbar-State
- toggleBold/Italic/Underline machen nur noch exec() — kein manuelles
  setBold(b => !b) mehr (war out-of-sync wenn execCommand wegen
  fehlender Selection nicht griff)
- B/I/U-Button-Highlight reflektiert jetzt die echte Cursor-Position

Fix 2 — RTF nimmt nur letzte Schrift:
Reihenfolge im _commit war falsch. Vorher: RichText → TextHeight →
_apply_font → _apply_align. _apply_font setzt te.Font (Default-Font)
NACH dem RichText → schiesst die per-Run-Fonts in der RTF tot.

Neue Reihenfolge:
1. PlainText (Initial-Content)
2. TextHeight, Font, Align (Defaults fuer ALLES)
3. RichText (ueberschreibt Defaults pro Run)

Plus Font-Tabelle in _runs_to_rtf jetzt mit \fnil\fcharset0 Marker
(Rhinos RTF-Parser kann sonst font-Eintraege ignorieren und Default
verwenden).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 02:00:03 +02:00
karim 51987dcc38 Text-Editor: Selection-Preservation + per-Span Font/Size
User-Bug: Stile aendern nichts im Editor oder springen alle in eine
Zeile. Mit "Herumfummeln" partiell ge-fixed. Root-Causes:

1. Toolbar-Buttons stehlen Focus aus Editor → Selection futsch →
   execCommand wirkt auf nichts. Fix: onMouseDown + preventDefault
   auf B/I/U/Sup/Sub/Align (Pill akzeptiert jetzt onMouseDown prop).

2. Editor-div hat fontFamily/fontSize aus React-State → ueberschreibt
   per-Span-Styles → alles sieht gleich aus. Fix: editor-div hat
   statische Defaults (Helvetica 20px), per-Selection Styles wirken
   ueber span-Wrapping (applyInlineStyleToSelection).

3. Newlines kollabieren (text springt auf eine Zeile). Fix:
   white-space: pre-wrap auf editor-div.

4. Font/Size dropdowns: alter execCommand fontName war buggy. Neu:
   applyInlineStyleToSelection('font-family', font) bzw. 'font-size'
   wickelt die Selektion in ein <span style="..."> ein, neue Selection
   liegt auf dem Span (Folge-Operationen wirken sauber).

5. Selection-change Event-Listener speichert die letzte Editor-Selection
   in savedRangeRef. restoreSelection() vor jeder Operation stellt sie
   wieder her — robust auch wenn der Focus zwischendurch weg war.

Backend (_runs_to_rtf): BASE_PX = base_size_m * 100 statt hardcoded 14.
Frontend rendert 1m = 100px, also entspricht base_size_m*100px dem
\\fs20 in RTF (= 1.0× TextEntity.TextHeight). _commit passes settings.
size mit, damit das Mapping stimmt.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 01:54:25 +02:00
karim f4404db64a Doppelklick-Hook auf DOSSIER-Texte + Size-Mapping in RTF
User: Doppelklick auf DOSSIER-Text oeffnet weiterhin Rhinos Editor.
Verschiedene Groessen im Editor erscheinen nicht in Rhino.

Doppelklick-Hook (rhino/text_editor.py):
- _DossierTextDoubleClickHook subklassiert Rhino.UI.MouseCallback.
  OnMouseDoubleClick prueft selektierte TextEntities auf UserString
  "dossier_text"="1" und cancelled das Event (= blockt Rhinos
  Standard-TextEdit-Dialog), setzt sticky["dossier_pending_text_edit"]
  mit der Obj-ID
- _on_idle_check_pending_edit (RhinoApp.Idle event): nimmt sticky-ID
  auf naechstem Idle-Tick und ruft open_for_edit(obj) — defer noetig
  weil Eto-Form aus MouseCallback heraus oeffnen Re-Entrancy macht
- _ensure_double_click_hook() installiert Hook + Idle-Handler einmalig
  pro Rhino-Session (idempotent)
- startup.py ruft das jetzt direkt nach Modul-Load auf

Edit-Mode (open_for_edit):
- Liest aus bestehendem TextEntity die Settings (Font, Size, Bold,
  Italic, Underline, Align) + PlainText
- Frame fuer Dialog-Positionierung aus BBox abgeleitet
- TextEditorBridge mit edit_obj_id + initial_text gestartet
- INIT-Payload um initialText + editMode erweitert
- COMMIT: bei edit_obj_id gesetzt → doc.Objects.Replace statt AddText.
  Plane wird vom Original uebernommen wenn keine explizite Rotation,
  damit der Text an seinem Platz bleibt

Frontend (TextEditorApp.jsx):
- Bei INIT mit initialText: editor.innerText wird damit befuellt
- htmlToRuns extrahiert font-size in Pixel pro Run (inline style oder
  computed style != base)
- baseCtx _basePx aus computed style des Editor-divs

Size-Mapping (rhino/text_editor.py _runs_to_rtf):
- fontSizePx in Runs triggert non-trivial (RTF wird generiert)
- Pro Run: \fs in Halb-Punkten = 20 * (run_px / 14_base_px) round
- 14px = \fs20 (1.0× TextEntity.TextHeight)
- 21px = \fs30 (1.5×)
- 28px = \fs40 (2.0×)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 01:44:45 +02:00
karim e7a1753519 Text-Editor: Default-Stile + Stil-Picker im Dialog
User-Wunsch: vorgespeicherte Stile (Heading, Paragraph Helvetica/Georgia)
direkt im Editor anwendbar.

Backend (text_create.py):
- _DEFAULT_STYLES: 7 sinnvolle Architektur-Defaults — Titel (0.40m bold),
  Heading 1 (0.30m bold), Heading 2 (0.20m bold), Paragraph Helvetica
  (0.15m), Paragraph Georgia (0.15m Georgia), Notiz (0.10m italic),
  Bildlegende (0.08m italic)
- list_styles: seedet die Defaults beim ersten Zugriff falls noch keine
  Styles im Doc existieren (analog mass_style)
- Bestehende save_style/delete_style/apply_style funktionieren weiter

Backend (text_editor.py):
- INIT-Payload erweitert um styles[] (Liste aller verfuegbaren Stile
  mit id/name/font/size/bold/italic/underline/align)

Frontend (TextEditorApp.jsx):
- Neuer Stil-Picker als erstes Dropdown in Toolbar-Row 1 (150px)
- Optionen: "— Stil wählen —" + alle verfuegbaren Stile
- onChange: applyStyle(style) — setzt Toolbar-State + appliziert via
  execCommand auf die aktuelle Selektion im WYSIWYG-Editor (oder als
  Default fuer kommendes Tippen wenn keine Selektion)
- queryCommandState-Check fuer Bold/Italic/Underline damit nur toggled
  wird wenn nicht schon im gewuenschten State

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 01:38:23 +02:00
karim 54aa1c9e84 Text-Editor: Symbol-Popover, Frame/Mask/Rot/Kamera, Phase 2 RTF-Mapping
User: Sonderzeichen in einen Button packen mit Popover-Box, neue
Optionen (Frame around text, Horizontal to view, Rotation, Mask
margins), und Phase 2 = mixed-fonts via Rich-Text.

Frontend (TextEditorApp.jsx):
- Sonderzeichen-Reihe weg, ersetzt durch [Symbole] Pill-Button mit
  Popover-Box. Symbols gruppiert (Mathematik, Pfeile, Auszeichnung)
- Toolbar Reihe 3 neu: [Rahmen ▼] [Mask ____ m] [Rot ____ °]
  [Zur Kamera Toggle] [Symbole Popover]
- htmlToRuns(rootEl): walks contentEditable DOM, extrahiert Format-
  Runs (text, font, color, bold, italic, underline, sup, sub) basierend
  auf Tag-Hierarchie (b/i/u/sup/sub/font/span style)
- onCommit sendet jetzt zusaetzlich runs[] + frame/horizontalToView/
  rotation/maskMargin in settings

Backend (text_editor.py):
- _commit: setzt Plane mit Rotation um Z (math.radians + Plane.Rotate)
- MaskFrame: NoFrame/RectFrame/CapsuleFrame ueber TextMaskFrame-Enum
- MaskEnabled+MaskOffset+MaskUsesViewportColor wenn maskMargin>0
- te.DrawForward = horizontalToView (Text steht zur Kamera)
- _runs_to_rtf(runs, default_font): Phase 2 — generiert Rhinos RTF aus
  den Runs. Triviale Runs (alle plain, ein Font) → None (PlainText
  fallback). Sonst: \rtf1 + fonttbl + colortbl + Format-Codes pro Run
  (\f \cf \b \i \ul \super \sub \nosupersub). _rtf_escape handelt \,
  {, }, \n und non-ASCII (\u-Notation). te.RichText = rtf, Fallback
  auf PlainText wenn das fehlschlaegt.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 01:35:28 +02:00
karim ab0ecfbf14 React WYSIWYG Text-Editor (Topmost Satellite-WebView) — Phase 1
User-Wunsch: eigener WYSIWYG-Editor im React/Topbar-GUI-Stil. Topmost.
Verschiedene Schriftarten/-Dicken sichtbar im Editor selbst.

Neues Backend (rhino/text_editor.py):
- TextEditorBridge mit Frame-Daten im Konstruktor, INIT-Push mit
  Settings + Font-Liste, COMMIT erstellt TextEntity, CANCEL schliesst
- open_with_frame(p1, p2, origin, width, height): oeffnet Satellite-
  Window mit mode='text_editor' + topmost=True
- panel_base.open_satellite_window: neuer Parameter topmost (default
  False) der form.Topmost setzt

text_create.create_text: ruft jetzt text_editor.open_with_frame nach
dem Frame-Pick. Eto-basierter _dossier_text_editor bleibt im Modul als
Fallback aber wird nicht mehr verwendet.

Neues Frontend (src/TextEditorApp.jsx, mode='text_editor'):
- Layout im DOSSIER-Topbar-Stil (dunkle Pills, accent on hover)
- Pill-Helper-Komponente fuer alle Toggle/Action-Buttons
- Dropdown-Helper fuer Font + Size
- Toolbar Row 1: Font-Dropdown | Size-Dropdown | Color-Picker | Layer-Reset
- Toolbar Row 2: B/I/U mit Material-Icons | L/C/R Align | x²/x₂ Sup/Sub
- Sonderzeichen-Palette: 41 Unicode-Symbole (Architektur/Math/Pfeile/
  Auszeichnungen), Klick inserted am Cursor
- WYSIWYG-Editor: contentEditable div mit fontFamily=ausgewaehlt,
  textAlign=ausgewaehlt — Format-Toolbar wirkt via document.execCommand
  (bold/italic/underline/justifyLeft/Center/Right/superscript/subscript/
  fontName/foreColor)
- "Einfuegen" sendet COMMIT mit text (innerText) + settings
- "Abbrechen" CANCEL → Bridge schliesst Form

Phase 1 Limitation: rendert in Rhino als PlainText mit den globalen
Settings (font/size/bold/italic/align/color) — verschiedene Schriftarten
INNERHALB des Texts sind im Editor sichtbar aber nicht im finalen
Rhino-TextEntity. Phase 2: HTML → Rhino RichText/RTF Mapping.

rhinoBridge.js: send() jetzt exportiert (war intern) damit
TextEditorApp generisch COMMIT/CANCEL senden kann.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 01:28:26 +02:00
karim 38041ab6a0 Text-Editor: Stile, Size-Dropdown, U+Align, bessere Icons, Fonts-Fallback
User-Wunsch: Text-Editor war unfertig — keine Fonts sichtbar, Bold liess
sich nicht entfernen, Size soll Dropdown mit Eigene, Text-Stile noetig,
Unterstrichen + Links/Mitte/Rechts fehlten, schoenere Icons.

Backend (text_create.py):
- DEFAULTS erweitert um underline + align (left/center/right)
- _normalize() validiert Settings (align nur left/center/right)
- Text-Style-Preset-System analog mass_style:
  - list_styles / save_style / delete_style / apply_style
  - get_active_style_id / set_active_style_id
  - doc.Strings["dossier_text_styles"] (JSON list mit id/name + settings)
  - doc.Strings["dossier_text_style_active"]
- _apply_align(te, "left"|"center"|"right") setzt TextHorizontalAlignment
- apply_settings_to_selection + create_text rufen _apply_align mit auf
- read_selection_settings liest auch align
- available_fonts mit Fallback-Liste (Helvetica, Arial, Times, etc.) wenn
  Rhino.DocObjects.Font.AvailableFontFaceNames leer ist
- underline: in Settings + Styles persistiert, NOCH NICHT visuell
  appliziert (braucht TextEntity-RichText-API)

Backend (oberleiste.py):
- Neue Handler APPLY_TEXT_STYLE / SAVE_TEXT_STYLE / DELETE_TEXT_STYLE
- State liefert textStyles + textStyleActiveId
- textFonts jetzt bei jedem _send_state mitgeschickt (vorher one-shot mit
  _fonts_sent flag — verlor sich nach Panel-Re-Mount und User sah keine
  Fonts mehr)

Frontend (OberleisteApp):
- Text-Block komplett neu gelayoutet (3 Spalten Grid):
  Reihe 1: [Style ▼] [Font ▼] [Size ▼]
  Reihe 2: [B|I|U] [L|C|R] [+]
- Style-Dropdown mit Optionen "+ Speichern…" und "🗑 Aktiven loeschen"
- Size-Dropdown mit Preset-Werten (0.05/0.10/.../1.00 m) + "Eigene…"
  → toggle zu Custom-Number-Input bei "Eigene"-Auswahl
- B/I/U mit Material-Icons format_bold/italic/underlined statt B/I-Text
- L/C/R Alignment-Buttons mit format_align_left/center/right
- ToggleBtn-Helper-Komponente fuer alle 6 Toggles
- "+" Insert-Button bleibt klein (Icon size 14)
- Accent-Border auf allen Pills wenn Text selektiert (visuelles Feedback
  "Aenderungen wirken auf Selektion")
- Bold/Italic/Underline lassen sich jetzt sauber togglen (waren als
  proper Booleans serialisiert — vorher Bug evtl. durch fehlende Font-
  Liste maskiert)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 00:44:17 +02:00
karim 6fce00343c Text-Block: Bug-Fix + Vectorworks-Style + Edit-Selected
User-Probleme:
1. Floating Eto-Dialog erschien nicht nach GetPoint (Mac-Bug)
2. UI war zu klobig, sollte vectorworks-mässig kompakt sein, "+" als
   kleines Icon-Symbol
3. Selektierten Text aendern war nicht moeglich

Fix 1 — Bug: _floating_input geloescht, ersetzt durch
_prompt_for_text() das Rhino.UI.Dialogs.ShowEditBox benutzt. Nativer
cross-platform Dialog ohne Eto-Modal-Bug. Workflow:
  GetPoint → ShowEditBox → AddText. Funktioniert auf Mac.

Fix 2 — UI: Text-Block kompakt umgebaut.
  Reihe 1: [Font ▼] | [Size m]   (130 + 60 = 196px)
  Reihe 2: [B][I][+] segmented pill (gleiche Breite wie Reihe 1)
"+" ist jetzt kleines add-icon (size 13), kein "+ Text" Label mehr.

Fix 3 — Edit-Selection: neue Funktionen in text_create.py:
- _selected_text_objects(doc) → Liste der selektierten TextEntities
- read_selection_settings(doc) → Settings der ersten Selektion
- apply_settings_to_selection(doc, patch) → wendet font/size/bold/italic
  auf alle selektierten TextEntities an
oberleiste.SET_TEXT_SETTINGS handler appliziert die Aenderung jetzt
ZUSAETZLICH auf die Selection (wenn vorhanden) — UND speichert als
Default. State enthaelt textSelectionSettings, UI nutzt diese als
Anzeige-Werte wenn vorhanden (Werte spiegeln Selektion live).
Visual: Border-Color der Size/Segmented-Pill wird accent wenn Selection
aktiv ist (Hinweis dass Aenderungen auf Selektion wirken).

Sig-Update: textSelectionSettings + lastSetView in last_state_sig damit
State neu gepusht wird wenn sich Selection/View aendert.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 00:32:16 +02:00
karim 38314bcc6f Topbar padding-top 3 → 4 (≈ +30%)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 00:26:16 +02:00
karim 79c8392c2c Pill-Hoehen erzwingen: box-sizing border-box + min/max-height auf Buttons
User: View-Bars und Massstab-Buttons-Pill sind hoeher als die Dropdowns
trotz gleicher BAR_H. Browser-Defaults fuer <button> koennen trotz
padding:0 implizite Mindesthoehen einfuehren — besonders auf macOS WebKit.

Fix:
- Alle Outer-Pills (View Row1/Row2, Massstab Buttons-Pill, BarCombo)
  bekommen explizite height = BAR_H+2 + box-sizing: border-box. Damit
  visual genau BAR_H+2 = 24px egal welche default-render-Schicht
  drueberkommt.
- Inner Buttons (cellStyle + SegBtn): minHeight/maxHeight = BAR_H,
  appearance: none, line-height: 1, box-sizing border-box. Lockt die
  Hoehe absolut.
- Massstab SegBtn Icon-Size 13 → 11 (analog View-Icons)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 00:23:04 +02:00
karim c16f5ea740 View-Toggle: Icons 13→11, N/O/S/W Font 11→10, BarSelect Dead-Code raus
User: View-Bars wirken hoeher als andere Bars. Hoehen sind mathematisch
identisch (BAR_H + Border = 24 visual pro Reihe), aber 8 Buttons mit
Border-Trennlinien wirken visuell dichter als ein einzelner Dropdown.

Visual-Fix per User-Vorschlag: Icons + Font in Views minimal verkleinert,
macht die Bars weniger "dominant".

Plus BarSelect (komplett unused Dead-Code seit Migration zu BarCombo)
geloescht — der hatte als einziger noch bg-item statt bg-input und
verwirrte die Background-Konsistenz-Frage.

Hintergrund-Check: Alle Pills nutzen jetzt einheitlich var(--bg-input),
Hover var(--bg-item-hover), Active var(--accent).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 00:17:37 +02:00
karim 872832a3cc View-Toggle: Active aus lastSetView + Stat-Box-Hoehe angeglichen
User-Feedback:
1. View-Bars sind hoeher als andere Elemente auf der Seite
2. Active-Highlight bleibt auf Top haengen — andere Views leuchten nicht
3. Glitch: Klick auf Top → Bar zeigt weiterhin Perspektive aktiv

Fix 1 (Hoehe): Stat-Box Inhalts-Hoehe BAR_H*2+4 → BAR_H*2+6, der innere
Trennstrich-Gap 4 → 6. Damit visual 50 → 52 = identisch mit den 2-row-
Blocks (View, Preset, Massstab).

Fix 2 + 3 (Active-Highlight): Backend trackt `self._last_set_view` ←
gesetzt wenn handler in SET_VIEW erfolgreich war. Frontend matchView
prueft zuerst `state.lastSetView === v` — kein Race-Condition zwischen
ChangeProjection und Viewport-State-Lesen mehr.

Fallback auf Viewport-State-Detection wenn lastSetView noch null
(initial load). N/O/S/W kriegen jetzt auch Active-Highlight (vorher
hartcoded false).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 00:13:08 +02:00
karim 4a31652f78 Stat-Box: gruener Border wenn at-scale (analog Hover-Akzent)
User: wenn die 2-stoeckige Massstab-Pille gruen ist (atScale=true), soll
auch der Border gruen sein — konsistent mit den anderen Hover/Active-
Akzenten in der Topbar.

Border: '1px solid border' → '1px solid (accent wenn atScale, sonst
border)'. Transition fuer smooth Wechsel beim Snap auf 100%.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 00:06:20 +02:00
karim 205c626a5a View-Toggle 2x4: TOP/ISO/PERSP/Cam + N/O/S/W mit Norden-Rotation
User-Vorschlag: Architektur-konformes View-Layout — 3D-Views oben, 4
Gebaeudeansichten unten. Plus Norden-Rotation als Doc-Setting damit bei
rotierten Projekten (swissBUILDINGS, Sonnenberechnungen) die richtigen
Wandansichten gepickt werden.

Backend (rhino/kamera.py):
- get_north_angle/set_north_angle — doc.Strings["dossier_north_angle"]
  (Grad im Uhrzeigersinn von +Y, default 0°)
- _scene_target_and_diag(doc) — gemeinsamer Helper fuer Szenen-Center +
  Diagonal-Distanz
- set_cardinal_view(vp, 'N'|'O'|'S'|'W'): rotiert Kamera-Position via
  Norden-Vektor. Parallel-Projektion, Camera-Z = Target-Z (echte
  Elevation), Up-Vektor +Z.
- set_top_view(vp): Plan-Ansicht mit Norden = Up-Vektor (Plan rotiert
  visuell wenn Norden != +Y)
- _set_iso(vp, octant): Octant-Richtung jetzt aus north+east-Vektoren
  konstruiert → ISO rotiert mit Norden mit
- Bridge-Handler SET_NORTH_ANGLE + state.northAngle, notify Oberleiste

Backend (oberleiste.py):
- SET_VIEW erweitert: Top → kamera.set_top_view, N/O/S/W →
  kamera.set_cardinal_view, Iso → kamera._set_iso. Front/Right/etc bleibt
  als Legacy direkt-Rhino-Call.
- State liefert northAngle

Frontend (OberleisteApp):
- VIEWS_ROW1: TOP/ISO/PERSP + Kamera-Settings-Button (Icons only)
- VIEWS_ROW2: N/O/S/W als DM-Mono-Buchstaben
- 2x4-Grid, VIEW_W=140 (konsistent mit Massstab-Pills), CELL_W=35
- matchView nur fuer Top/Iso/Perspective; Cardinals haben keinen
  Active-State (Viewport-Name ist nicht zuverlaessig erkennbar)

Frontend (KameraApp):
- Plan-Norden Section mit Number-Input (Grad, 0.5°-Step) + Reset-Button
- Hinweis-Text dass Wirkung auf TOP/ISO/N/O/S/W geht

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 00:04:44 +02:00
karim 2252ffd2f9 Text-Erstellung mit Floating-Input-Box (Variante B)
Neuer Workflow: Klick "+ Text" in Topbar → Punkt im Viewport picken →
Floating Eto-Dialog erscheint neben dem Mauszeiger → User tippt → Enter
fuegt TextEntity mit Topbar-Settings ein. Esc bricht ab.

Backend (rhino/text_create.py):
- load_settings/save_settings — persistiert font/size/bold/italic in
  doc.Strings["dossier_text_settings"] (JSON)
- available_fonts() — System-Font-Namen via
  Rhino.DocObjects.Font.AvailableFontFaceNames
- _floating_input() — Eto.Dialog mit TextBox, ShowModal mit Rhino-
  MainWindow als Parent, positioniert bei Mouse.Position
- create_text() — RhinoGet.GetPoint → _floating_input → TextEntity
  mit Font/Size/Bold/Italic erstellen + AddText
- _apply_font() mit 2 Fallback-Pfaden (FontTable.FindOrCreate +
  Font.FromQuartetProperties) fuer RhinoCommon-Kompatibilitaet

Backend (oberleiste.py):
- CREATE_TEXT handler → text_create.create_text()
- SET_TEXT_SETTINGS handler → text_create.save_settings (merge partial)
- State payload: textSettings (immer) + textFonts (einmalig initial,
  via _fonts_sent Flag — Liste aendert sich nicht zur Laufzeit)

Frontend (OberleisteApp + rhinoBridge):
- createText() + setTextSettings() Bridge-Funktionen
- Text-Block 2x2 Grid analog Massstab:
  R1: Font-Dropdown (BarCombo mit text_fields icon) | Size-Input mit "m" suffix
  R2: B/I-Toggles (segmented pill mit accent-Fill bei active) | "+ Text" Button
- Hover-Logik analog View-Toggle (bg → bg-item-hover, color → accent-light)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 23:52:00 +02:00
karim 817455bbba Massstab Stat-Box: Live + Zoom-% als EINE gerundete Pill statt zwei
User: aus den zwei uebereinander stehenden Chips (1:405 / 4.9%) eine
einzelne gerundete Pill machen. Text-Hoehen bleiben wie vorher, dazwischen
ein duenner Trennstrich.

Aufbau:
- Outer: grid-row spans both Reihen, width STAT_W, height BAR_H*2+4
- Innen: top BAR_H mit live-Text, 4px Gap mit zentriertem 1px-Trennstrich
  (left/right 6px Inset), bottom BAR_H mit Ratio-Text
- Rundung: borderRadius 14 (gerundete Ecken)
- atScale: accent-dim bg + accent-light text fuer beide Texte

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 23:46:00 +02:00
karim e2c13e7844 Massstab: Pills gleich breit + Icon weg + weisse Texte + Hover-Akzent
User-Wunsch:
- Aktueller Massstab (live) + Zoom-% weiss, accent bei 100%
- Dropdown immer weiss (kein valueAccent mehr)
- Buttons unten wie View-Toggle (weiss default, gruen on hover)
- Massstabs-Icon weg, Pills (Dropdown + Buttons-Bar) gleich breit + exakt
  uebereinander

Aenderungen:
- BarCombo: Icon-Slot nur wenn `icon` truthy gerendert (kein leerer Slot)
- Massstab-Dropdown ohne icon-Prop → kein 18px Slot links, Pill sitzt
  direkt in der Grid-Zelle
- PILL_W=140 für Dropdown UND Buttons-Pill, jeder Button BTN_W=35
- statChipStyle: color text-primary (weiss) statt text-muted, accent-light
  nur bei atScale=true
- valueAccent prop entfernt aus Massstab-Dropdown
- SegBtn mit Hover-Logik analog View-Toggle: bg → bg-item-hover,
  color → accent-light, active bleibt accent-fill

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 23:40:45 +02:00
karim 3f5f48cb2c Caret differentiated + Accent-Hover wie in Elemente
User:
1. Wireframe-Caret war fast draussen (zu weit rechts), bei anderen
   minimal mehr rechts erwartet
2. Hover-Effekt mit accent-border wie in Elemente fuer alle Pills

Fix 1: backgroundPosition differenziert:
- Ohne Gear: 'right 10px center' (normaler Pill-Rand-Abstand)
- Mit Gear: 'right 1px center' (klebt am Gear)
So sitzt der Caret optisch konsistent — bei Display innen, bei den
anderen direkt vor dem Gear.

Fix 2: Hover-Akzent analog ElementeApp PillButton:
- BarCombo Pill-Container: bg → bg-item-hover, border → accent
- BarButton (Camera, Settings-Gears in Pills, Print-Toggle): selbe Logik,
  active-State bleibt accent-fill
- View-Toggle-Buttons: bg → bg-item-hover, text → accent-light, active
  bleibt accent-fill
Transitions 0.15s fuer smooth feel.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 23:32:24 +02:00
karim b0badbbda6 BarCombo Caret: right 9px → 2px, klebt jetzt am Gear
User: Caret-Abstand zum Gear soll symmetrisch zum Abstand vom Gear zum
rechten Pill-Rand sein. Vorher saß der Caret 9px links vom Gear, der Gear
8px vom rechten Pill-Rand → asymmetrisch (caret-to-gear groesser als
gear-to-edge).

Fix: backgroundPosition 'right 2px center'. Caret-Image rechtes Ende 2px
vom Select-Rand, Caret-Center ~7px links vom Gear-Button-Linker-Rand,
plus 8px Gear-Padding-Left + 6px halbes Gear-Icon = ~13px caret-to-gear-
icon. Gear-to-edge = 8+6 = 14px. Symmetrisch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 23:24:07 +02:00
karim ea8292ed14 Massstab 2x2-Grid: Live | Dropdown / Ratio | Buttons
User-Korrektur: Layout sollte 2x2 sein:
  Reihe 1: [Aktueller Massstab]  [Dropdown gesetzter Massstab]
  Reihe 2: [Zoom-Verhaeltnis %]   [Buttons]

Restrukturiert auf CSS-Grid mit 2 Spalten, 2 Reihen.
- Live + Ratio-Chips links (80px fixe Breite, monospace)
- Dropdown + Buttons rechts
- Stat-Chip-Helper statChipStyle(accentTint): accentTint=true bei 100% match

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 23:22:12 +02:00
karim ea4c891b98 Massstab: Dropdown (gruen) oben, Zoom-Verhaeltnis unten
User-Wunsch: ueber dem aktuellen Live-Zoom soll der gesetzte Massstab in
gruen oben sein, unten das Verhaeltnis des aktuellen Zooms zum gesetzten.

Layout:
- Buttons-Pill links (vertikal zentriert ueber beide Reihen)
- Rechts oben: Massstab-Dropdown — BarCombo mit neuem valueAccent prop,
  faerbt Select-Text in accent-light + bold wenn ein Massstab gesetzt ist
- Rechts unten: Zoom-Ratio-Chip (% des gesetzten Massstabs).
  100% = at-scale → accent-dim Hintergrund + accent-light Text.
  <100% = rausgezoomt, >100% = reingezoomt → bg-input + muted text.

Ratio-Formel: appliedScale / liveScale * 100.
- 1:50 gesetzt, live 1:50 → 100% (exakt, accent)
- 1:50 gesetzt, live 1:100 → 50% (rausgezoomt)
- 1:50 gesetzt, live 1:25 → 200% (reingezoomt)

Per Klick auf das "%" Button (Reihe 1) snappt der Zoom auf 100%.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 23:18:31 +02:00
karim 0182497963 Massstab Row 1: 4 Buttons in segmented Pill statt Einzel-Knoepfen
User: Massstab-Block sah nicht huebsch aus, die 4 runden Buttons saßen
detached nebeneinander. Polish: alle 4 in einer durchgehenden Pill-Gruppe
(analog View-Toggle), bg-input fuer inaktive, accent fuer aktiven Print-
View-Toggle. Sieht jetzt visuell geschlossen aus.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 23:10:55 +02:00
karim 02b5fbfde4 BarCombo Caret: immer rechts vom Select (statt 30px reingerueckt bei Gear)
User-Screenshot: Caret stand bei Combos mit Gear irgendwo in der Mitte
zwischen Text und Gear. Grund: backgroundPosition war auf 'right 30px'
gesetzt damit der Caret nicht ueberlappt, aber er sass relativ zum Select-
Element (nicht zum Pill), also tatsaechlich weit links.

Fix: 'right 9px center' immer — der Select hat 22px right-padding und
endet direkt links vom Gear, der Caret klebt jetzt am rechten Rand des
Select-Bereichs (= so weit rechts wie moeglich ohne Gear zu ueberlappen).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 23:04:16 +02:00
karim b78a95caaa Massstab in 2 Reihen + View-Toggle inaktiv ebenfalls dunkel
User-Wunsch: Massstab-Stack analog Preset-Block. Plus inaktive View-
Toggle-Buttons sollen denselben dunklen Hintergrund haben wie der
Preset-Block.

Massstab:
- Reihe 1 (oben): Buttons (%, fit_screen, center_focus_strong, print/edit)
- Reihe 2 (unten): Live-Zoom-Chip + Massstab-Dropdown (BarCombo statt
  BarSelect — dunklerer bg-input + zusammenhaengender Pill)
- Custom-Input ebenfalls auf bg-input

View-Toggle: inaktive Buttons bg-item → bg-input (konsistent dunkel mit
allen anderen Pills). Aktiv bleibt accent-fill.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 23:03:02 +02:00
karim 1d1cd10a0b Oberleiste 2-Reihen-Block: Display|Kombi oben, Overrides|Masse unten
User-Wunsch: Overrides unter Wireframe, Kombi neben Wireframe, gleiche
Pill-Breiten + gleiche X-Achse, Boxen zusammenhaengend (kein Split
zwischen Select und Gear), dunkler wie Elemente.

Neue Komponente BarCombo:
- Icon roh links (18px fixe Breite → X-Alignment zwischen Reihen)
- iconClickable=true macht Icon zum Toggle-Button (fuer Overrides AN/AUS)
- Combined pill: ein gemeinsamer Container (bg-input — dunkler statt
  bg-item) mit select + optional gear in einem nahtlosen Rahmen
- Gear sitzt im selben Pill, kein border-left, transparent bg
- Caret-Position verschoben (right 30px) wenn gear vorhanden — Caret
  bleibt innerhalb des sichtbaren Select-Bereichs

Layout:
- CSS Grid 2x2 mit fester Pill-Breite (PRESET_W = 150)
- Reihe 1: Display | Kombi
- Reihe 2: Overrides | Masse
- Gleicher Spalten-Track in beiden Reihen → identische X-Positionen

Entfernt: BarSelect/BarButton im Display/Masse/Overrides/Kombi-Pfad,
alte Sektionen am Ende der Toolbar. BarButton bleibt fuer Camera + Zoom-
Buttons + Print/Edit. View-Toggle bleibt segmented-pill am Anfang.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 22:54:43 +02:00
karim 700cc11956 Oberleiste-Mittelweg: BAR_H 22, Font zurueck 11, Logo 17 — Toolbar-Gap bleibt
User: vorher zu klein. Mittelding zwischen 24 (original) und 20 (zu klein):
- BAR_H 20 → 22, PILL_H 18 → 20, Sep 18 → 20
- Select-Font 10 → 11, Padding 22/10 → 24/12, Caret 8 → 9
- BarButton Icon 12 → 13
- View-Toggle Font 9 → 10, Icon 12 → 13, Padding 8 → 10, Gap 3 → 4
- Live-Zoom Font 10 → 11, MinWidth 56 → 60
- Custom-Input Width 90 → 100, Padding 10 → 12, Font 10 → 11
- Logo 15 → 17

Toolbar-Gap 6 + Padding 3/10/6 bleiben (User: "find ich gut").

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 22:39:58 +02:00
karim 66d6e63959 Oberleiste etwas kleiner — BAR_H 24→20, Fonts 11→10, Logo 18→15
User-Wunsch: alles etwas kleiner. Konsistent runter:
- BAR_H 24 → 20 (Selects, Buttons, View-Toggle, Live-Zoom, Massstab-Input)
- PILL_H 20 → 18 (Legacy-Pills)
- Sep 22 → 18 hoch, 4 → 3 Margin
- Toolbar-Gap 8 → 6, Padding 4/12/8 → 3/10/6
- Logo "DOSSIER." 18 → 15
- Select-Font 11 → 10, Padding 26/12 → 22/10, Caret-Pos 10 → 8
- View-Toggle: Font 10 → 9, Icon 13 → 12, Padding 10 → 8
- BarButton Icon 13 → 12
- Live-Zoom Font 11 → 10, MinWidth 64 → 56, Padding 12 → 10
- Custom-Massstab-Input: Width 100 → 90, Font 11 → 10

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 22:36:06 +02:00
karim cf40c03602 Zurueck zum Vectorworks-Look — Custom Pill-Chrome statt raw native HTML
User-Klaerung: erwartet wird der polierte Vectorworks-Style, nicht rohe
macOS-HTML-Form-Elemente. Vectorworks ist custom-gestylt um nativ
auszusehen, aber mit konsistentem Toolbar-Polish.

Zurueckgerollt:
- BarSelect: Pill-Container (bg-item, border, border-radius 999) +
  Custom-SVG-Caret aus --select-arrow. appearance:none. Icon roh links
  (Userpraferenz). joinedRight macht rechte Pill-Kante flach.
- BarButton: Pill-Container mit border + border-radius 999. active=true
  setzt accent-Background + bg-panel-Iconfarbe (klare Toggle-Anzeige).
  joinedLeft macht linke Kante flach fuer Verkettung.
- View-Toggle (Top/Front/Right/Iso/Persp): Segmented-Pill-Gruppe analog
  Vectorworks. Innere Kanten ohne Border, Aussenkanten gerundet.
  Active-Button: accent-fill + Bold.
- Custom-Massstab-Input: Pill-Chrome zurueck.

.native-control CSS-Klasse bleibt definiert (zukuenftige Verwendung), wird
aber in der Oberleiste nicht mehr referenziert.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 22:28:17 +02:00
karim e19bbafe38 .native-control auch fuer Buttons — View-Toggle + BarButton system-nativ
User-Screenshot: dropdowns waren native, aber View-Toggle (Top/Front/...)
und die Icon-Buttons (Camera/Gear/Zoom/etc.) sahen weiterhin custom aus.

Grund: globales `button { border: none; background: none; }` strippt das
System-Push-Button-Styling fuer alle <button>s. `.native-control` deckte
nur select + input ab.

Fix:
- `.native-control` erweitert auf `button.native-control`: revert auf
  alle Button-Reset-Properties → WebKit rendert macOS-Push-Button-Chrome
- `.is-active`-Modifier: bold + accent-Tint fuer Toggle-Active-State
  (macOS-Native hat keinen "pressed-Toggle"-Look fuer normale Buttons)
- BarButton: className="native-control" + active=true → is-active
- View-Toggle (Top/Front/Right/Iso/Persp): pill-Container weg,
  individuelle native buttons mit is-active wenn matchView() true

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 22:18:37 +02:00
karim 8ad9e23838 .native-control Opt-out: global select-Style ueberschrieb das System-Native
User-Screenshot: dropdowns sehen immer noch wie pills aus. Grund: das
globale `select { appearance: none; background-image: var(--select-arrow);
border-radius: 999px; ... }` in index.css uebermalte die Inline-Resets von
BarSelect — globale CSS-Specificity gewann.

Fix:
- Neue `.native-control`-Klasse in index.css setzt mit `revert` alle
  pill-Overrides zurueck → WebKit/macOS rendert wieder Default-Combobox
- BarSelect: className="native-control" am <select>
- Custom-Massstab-Input: className="native-control"

Andere Selects im Projekt behalten den pill-Look (Ebenen-/Geschoss-Manager
etc.) — nur die Oberleiste opt-out.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 22:14:53 +02:00
karim 536d42f097 BarSelect/BarButton: bare native chrome auch im Ruhezustand
User-Wunsch: nicht nur das Popup, auch das Feld selber soll system-nativ
aussehen. Vorher hatten die <select>-Elemente noch pill-Background +
Border + Radius — das uebermalte WebKits Native-Look.

BarSelect: alle Styling-Overrides entfernt (background/border/border-radius
weg). Nur Hoehe/Breite/Font/colorScheme bleibt — macOS rendert jetzt das
gewoelbte System-Combobox-Feld inkl. nativem Drop-Caret.

BarButton: analog umgestellt — kein eigener Container, native <button>-
Chrome. active-State via accent-color (CSS) + Icon-Tint.

Custom-Mode-Massstab-Input (1:N): pill-Styling weg, nativer <input>.

joinedRight/joinedLeft Props bleiben akzeptiert aber sind no-ops — native
Felder lassen sich nicht visuell verketten.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 22:12:15 +02:00
karim 2e6dc44923 BarSelect: system-natives <select> + colorScheme:dark (Versuch Vectorworks-Style)
User-Wunsch: Dropdowns wie bei Vectorworks — also nativer OS-Look statt
Custom-Caret + custom Popup.

BarSelect umgebaut:
- appearance:none entfernt → WebKit/macOS rendert seinen eigenen Down-Caret
- custom <Icon arrow_drop_down> entfernt
- pill-Container weg, das <select>-Element selbst traegt jetzt direkt das
  pill-Styling (Hintergrund, Border, Border-Radius)
- colorScheme: 'dark' inline und auch global in index.css gesetzt
  damit macOS-Native-Controls die Dark-Mode-Variante rendern

index.css:
- :root color-scheme: light (default)
- @prefers-color-scheme: dark → color-scheme: dark
- Wirkt auf alle nativen Controls (selects, scrollbars, checkboxen)

Resultat: Klick auf ein Dropdown oeffnet das echte macOS-Popup-Menue
(wie in Vectorworks) statt das HTML-Browser-Dropdown.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 22:08:56 +02:00
karim c8286b931b About-Dialog als eigenes Fenster (Eto-Form + WebView) statt Inline-Modal
User-Feedback: About sollte nicht als overlay im Panel erscheinen sondern
ein echtes OS-Fenster sein wie Kamera/Masse-Settings.

Neu:
- rhino/about.py: open_as_window() via panel_base.open_satellite_window
  (read-only, kein Bridge-Save/Cancel-Callback noetig)
- src/AboutApp.jsx: gleiche Inhalte wie der vorige Modal — Versionen,
  Autor, Website, Lizenz — in einer 440x380 Eto-Form
- src/main.jsx: mode 'about' → AboutApp
- openAbout() in rhinoBridge.js sendet OPEN_ABOUT an Oberleiste
- OberleisteBridge handler OPEN_ABOUT → about.open_as_window()

OberleisteApp:
- Logo-onClick aufgeräumt: openAbout() statt setAboutOpen(true)
- aboutOpen-State und die AboutModal-Komponente entfernt

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 22:07:23 +02:00
karim 76572968ca Oberleiste: Icon roh + Pill-Dropdowns, About-Modal, Overrides/Kombi migriert
User-Feedback: Icon-Box war zu plakativ, lieber roh daneben + Pill-Form.
Versionsnummer raus, Logo-Klick → About-Fenster.

BarSelect umgebaut:
- Icon roh links (vorher: separates Icon-Kompartiment mit Border)
- Select-Container pill-foermig (border-radius 999, vorher 4)
- joinedRight wird zu flachen rechten Pill-Kanten (statt rechteck)

BarButton: pill-foermig + joinedLeft mit flacher linker Kante.

View-Toggle: Pill-Container statt Rechteck.
Massstab Live-Zoom-Chip: Pill-Form.
Custom-Scale-Input: Pill-Form.

Logo:
- Versionsnummer-Span entfernt
- ganzes Logo wird zu Klick-Button → AboutModal-State
- AboutModal: zentrierter Backdrop-Dialog mit Launcher/Plugin-Versionen,
  Autor (Karim Gabriele Varano), Website (gabrielevarano.ch),
  Lizenz (Proprietaer)

Overrides + Kombi migriert (war noch im alten Stack-Layout):
- Overrides: BarButton (Toggle) + BarSelect (Preset) joinedRight + BarButton
  (Settings) joinedLeft, palette-Icon fuer Preset-Picker
- Kombi: BarSelect (layers-Icon) mit allen Aktionen im Dropdown
  (Speichern, Loeschen, Bearbeiten) + BarButton (edit) joinedLeft
- Stack-Layout (label-Spalte + 2 Reihen) ist weg, jetzt inline

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 22:00:07 +02:00
karim c22aef6b65 Oberleiste Vectorworks-Style: Icon links + rechteckige Bar-Widgets
User-Referenz: Vectorworks Topbar. Drei Cues uebernommen:
- Icon-Kompartiment links im Dropdown (statt UPPERCASE Group-Label oben)
- Rechteckige Form mit dezenter Trennlinie zwischen Icon-Box und Inhalt
- Verbundene Widgets (Select + Settings-Gear) als eine visuelle Einheit

Neue Komponenten in OberleisteApp.jsx:
- BarSelect: [Icon | Native-Select | Caret] in einer 24px-Container-Box.
  appearance:none entfernt den nativen Pfeil, eigener arrow_drop_down rechts.
  joinedRight-Flag fuer nahtloses Anschliessen an einen BarButton.
- BarButton: quadratischer Icon-Button, joinedLeft kein doppelter Border,
  active=true → Accent-Background (fuer Toggles wie Print-View).

Migrierte Gruppen:
- View: 5 Toggle-Buttons in einem zusammenhaengenden Rahmen (kein Group-Label),
  selektierter Button accent-gefuellt analog VW Custom-View-Highlight
- Display: BarSelect (lightbulb icon)
- Masse: BarSelect joinedRight + BarButton joinedLeft (zahnrad)
- Massstab: Live-Zoom-Chip (eigene Box), BarSelect (straighten icon),
  BarButton (percent / fit_screen / center_focus_strong)
- Print/Edit-Toggle: BarButton mit active-State

Stack-Block (Overrides + Kombi) bleibt erst — separate Architektur,
nicht im Vectorworks-Vergleich enthalten. Migration spaeter wenn Bedarf.

PILL_H bleibt fuer nicht-migrierte Stellen, BAR_H = 24 fuer die neuen.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 21:52:36 +02:00
karim 2ee4688fe3 Masse-Dropdown in Oberleiste + Satellite-Settings statt Dimensionen-Inline
User-Feedback: Mass-Style passt nicht ins Dimensionen-Panel, und der
Name "Mass-Style" gefaellt nicht. Umzug in die Oberleiste (analog Display)
+ Zahnrad oeffnet eigenes Settings-Fenster. UI-Begriff jetzt "Masse".

Frontend:
- OberleisteApp: neue Gruppe "Masse" mit Preset-Dropdown + Zahnrad-Button
  zwischen Display und Massstab
- MasseSettingsApp.jsx (neu): Satellite-Fenster mit Name/Raum-Rundung/
  Mass-Dezimalstellen/Mass-Einheit + Picker + Add/Delete
- DimensionenApp: MassStyleSection raus
- rhinoBridge: setMasseActive + openMasseSettings (Topbar);
  masseSetActive/masseSavePreset/masseDeletePreset (Settings-Fenster)

Backend:
- rhino/masse_settings.py (neu): Bridge fuer das Satellite-Fenster,
  Topics SET_ACTIVE / SAVE / DELETE, triggert regen_all_rooms + topbar refresh
- mass_style.regen_all_rooms(doc): neue cross-modul-Helper, queued
  Raum-Regen fuer alle raum_outline-Objekte
- oberleiste.py: massePresets + masseActiveId im State, SET_MASSE_ACTIVE
  + OPEN_MASSE_SETTINGS handler, Signature update
- dimensionen.py: Mass-Style-Endpoints + State raus (sind jetzt im
  OberleisteBridge bzw. MasseSettingsBridge)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 21:29:23 +02:00