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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
Eye-Knopf bleibt jetzt auch in „Nur aktive"-Mode sichtbar (dimmend, statt
zu verschwinden). Klick auf Eye in „Nur aktive" oder „Alle anzeigen"
wechselt automatisch zu „Ausgewählte" damit die Aktion wirkt.
Backend (layer_builder.apply_visibility): neuer e_mode 'all_force'
ueberschreibt das Eye-Flag (zeigt alle Ebenen). 'all' respektiert weiter
das Flag (= „Ausgewählte" im UI).
Frontend (EbenenManager):
- MODES: 'all_force'=„Alle anzeigen" hinzu, 'all' umbenannt zu „Ausgewählte"
- EbeneRow: eyeIcon/eyeOpacity/eyeTitle nach Mode, analog GeschossManager
- handleToggleVisible + Master-Eye: bei active/all_force → onModeChange('all')
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Swisstopo Ortho
- AddPictureFrame statt Mesh+Material — Rhinos eigener Picture-Pfad mit
embedBitmap=True + selfIllumination=True macht die Textur in allen
Display-Modi sichtbar (Wireframe / Shaded / Rendered / Raytraced)
- asMesh=False (Brep-Variante) — asMesh=True ist auf Mac Rhino 8 broken
(alle Pictures landen am gleichen Punkt unabhaengig von der Plane)
- Per-Tile Sub-Ebenen unter 80_swisstopo (z.B. 80_swisstopo/2763-1254_Ortho)
via dossier_ebenen JSON registriert → erscheinen im Dossier-Ebenen-Manager
mit eigener Visibility
- target_layer_idx wird vor AddPictureFrame als Active-Layer gesetzt,
Picture landet direkt auf richtigem Sub-Layer (Move-danach broeselte
das Material)
- Regex-Fix in _parse_swisstopo_tile_bbox: Separator zwischen den beiden
Coords MUSS Hyphen sein, sonst matcht es faelschlich auf `_YEAR_EAST_`
Patterns wie `_2025_2763_`
Oberleiste
- DOSSIER. Logo (Krungthep + Petrol-Punkt) + Launcher-Version
(via __LAUNCHER_VERSION__ Vite-Define aus launcher/package.json)
- Overrides + Kombi vertikal gestapelt, gleiche Label-Spalte + Dropdown-
Breite → Dropdowns auf gleicher X-Linie
- View-Icons neu zugeordnet:
Top=view_quilt (Raster), Front=north (Pfeil), Persp=view_in_ar (3D)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Swisstopo
- swissBUILDINGS3D 3.0 + Variant-Toggle (separated/solid) im Dialog
- Auto-Fallback auf 2.0 wenn 3.0-Tiles ueber 200 MB sind (Stadt-Fall)
- Defensiver Variant-Filter auf 3 Ebenen (Item, Asset, ZIP-Extract) — keine
Doppelimporte mehr
- Auto-Skala korrigiert jetzt die importierten Objekte (×1000) statt die
User-bbox zu schrumpfen — Buildings bleiben in m-Doc-Skala
- merge_grids: XYZ-Tiles werden vor dem Mesh-Bau vereint, kein 1m-Streifen
zwischen Tiles mehr
- Layer-Konsolidierung: Build_*/Roof_*/Wall_*/Floor_* DWG-Source-Layer
werden auf Sub-Sub-Layer unter 81_Swissbuildings/{Build,Roof,Wall,Floor}
gemappt; solid-Variante landet flach direkt auf dem Parent
- 0-Kote m.ü.M (Projekt-Nullpunkt) wird beim Import als Z-Offset angewandt
Hierarchische Ebenen
- dossier_ebenen unterstuetzt jetzt 'children'-Array (rekursiv)
- layer_builder.build_layers rekursiv (Parent + Children unter jedem Geschoss)
- apply_visibility/update_layer_style/set_ebene_visible/set_ebene_locked
walken den Tree (Sub-Sub-Layer mit gleichem Code-Prefix werden mit-gepflegt)
- EbenenManager mit Chevron-Toggle + Indent pro Level + Context-Menue-Item
'Sub-Ebene hinzufuegen'
- rhinoBridge.applyVisibility schickt Children-Tree (nicht nur Top-Level) —
sonst kommen Sub-Toggles nicht beim Backend an
- Visibility-Key in App.jsx rekursiv durch Children — useEffect feuert jetzt
auch bei Sub-Eye-Toggles
0-Kote m.ü.M
- Eingabefeld im Geschoss-Settings-Dialog (projektweit)
- Speicherung als dossier_project_zero_mum in doc.Strings
- Wird im Swisstopo-Import als Z-Offset (m + doc-units) angewandt
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Frontend:
- src/SwisstopoApp.jsx NEU: Satelliten-Fenster mit Adresse-Suche, Radius-
Wahl, Daten-Checkboxen (Gebäude/Terrain/Luftbild), Origin-Handling, Live-
Log
- ElementeApp Swisstopo-Gruppe: Importer-Button + Karte-Button
Backend:
- rhino/swisstopo.py NEU: STAC-API-Client, Geocoding via swisstopo Search,
LV95↔WGS84-Konvertierung, GeoTIFF/XYZ-Cache, mesh_from_grid + Ortho-Material
- swissBUILDINGS3D 2.0 (DXF/DWG) via STAC; Per-Tile-Filter (_NNNN-NN_)
schuetzt vor versehentlichem Download der 3.5 GB National-Geodatabase
- swissALTI3D als XYZ-ZIP mit zipfile-Extraction, raeumliches Sub-Sampling
statt Zeilen-Step (keine Streifen mehr im Terrain-Mesh)
- SWISSIMAGE 10cm GeoTIFF als RenderMaterial-DiffuseBitmap mit planarem
UV-Mapping auf den Terrain-Mesh-bbox
Robustheit:
- Auto-Skala-Erkennung: Rhinos DXF-Parser scaliert je nach \$INSUNITS auf
unerwartete Doc-Units; wir messen aus ersten 50 Objekten + snappen auf
Zehnerpotenz (1, 0.001, 1000)
- bbox + origin_shift in doc-units (m_to_unit aus UnitScale + Auto-Detect)
- Tags via UserString dossier_swisstopo_kind=buildings/terrain fuer
Replace-Detection bei erneutem Import desselben Gebiets
- BBox-Clip jetzt OPTIONAL (Default OFF, Checkbox) — bei InstanceReferences
GetBoundingBox + Delete teuer
- Batch-Transform via System.Collections.Generic.List[Guid] statt
Python-Loop (Python.NET-Overload-Match)
- Listener-Suppression in elemente.py + gestaltung.py + dimensionen.py
via sticky dossier_swisstopo_busy — kein Per-Object-Spam mehr bei
Selection/Add/Delete waehrend 5000+ Imports
- Auto-Zoom via view.ZoomBoundingBox(combined) statt Select-Loop
- Year-Dedupe auf Tile-Coord (Pattern YYYY oder YYYY-MM unterstuetzt) fuer
alle Collections — aeltere Versionen werden ausgefiltert
- Download-Safety: > 200 MB wird abgebrochen + Live-Progress alle 2 MB
mit UI-Yield via Rhino.RhinoApp.Wait()
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
UI:
- GeschossManager: Master-Eye + Master-Lock im Header (analog EbenenManager).
Scheren-Button pro Geschoss togglet hasClipping. Auge ganz links wie bei
Ebenen. Eye-Logik klar 4-Wege: aktive Z immer hell+on, in 'active'/'all_force'
fuer non-active gedimmt, sonst spiegelt Flag direkt. Schrift wird NIE gegrayt.
Neuer Mode 'all_force' = "Alle anzeigen" (ignoriert Eye), 'all' jetzt mit
Label "Ausgewaehlte" (respektiert Eye). Klick aufs Auge in 'active'/'all_force'
wechselt automatisch in "Ausgewaehlte" damit Aktion sofort wirkt.
- layer_builder.apply_visibility: neuer z_mode 'all_force' vor visible-Check —
zeigt jede Z auch wenn Eye=false war.
- elemente._cmd_create_oeffnung: gruene Live-Preview (vertikales Oeffnungs-
Rechteck + Breiten-Marker + Diagonale) waehrend Fenster/Tuer-Platzierung
entlang der Wand-Achse. Brueest-Offset aus Geschoss-UK korrekt im Z.
Performance Cold-Start:
- panel_base: Inlined-HTML als Modul-Cache (1x build, n-mal mount). Pro
Panel-Mount nur noch str.replace + LoadHtml. Spart bei 10 Panels 9x
~395 KB Disk-Read + Regex-Pass. Cache-Key = mtime von dist/index.html.
- Timing-Instrumentierung: _t_mark + print_startup_summary. Hook in startup.py
feuert 3s nach Plugin-Load + listet Wall-time, Top-10, Aggregat pro Phase.
- OberleisteBridge: Command-Enumeration (~1000 Commands) jetzt lazy via
Rhino.RhinoApp.Idle statt synchron im __init__. Cold-Start nicht blockiert,
Autocomplete kommt ~1 Frame spaeter.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Ebenenkombination raus aus Ebenen-Panel, in Oberleiste-Topbar +
Editor-Satellite (AusschnittLayerDialog embedded). doc.Strings
haelt active_comb_name, auto-clear bei manueller Eye/Lock-Aenderung.
- EbenenSettingsDialog jetzt Satellite mit Ebene-Picker-Dropdown
(auto-save on switch via SAVE_KEEP).
- Per-Ausschnitt Einstellungen-Satellite (Massstab, Display, Overrides,
Ebenenkombi). Alte 'Sichtbarkeit bearbeiten'-Option entfernt.
- Layouts/Ausschnitte: Top-Header weg, Sticky-Footer mit Anzahl +
Aktionen. LayoutDialog ist jetzt Satellite mit Format-Live-Preview.
- Panel-Captions + Default-Ebenen-Namen auf Mixed-Case (Ausschnitte,
Ebenen, Waende ...). Nur DOSSIER bleibt caps.
- DimensionenApp: Card-Optik raus, REF-Wuerfel mit Kreisen statt
Quadraten + Hover-Scale.
- GeschossManager angeglichen an EbenenManager: Rechtsklick-Menue,
Lock-Button, Delete-X, Duplizieren. layer_builder honoriert z.locked.
- Active Sublayer folgt jetzt dem Geschoss-Wechsel (gleicher Code
unter neuem Parent).
Performance Geschoss-Wechsel:
- elemente._send_state() ersetzt durch _notify_active_geschoss()
(Partial-Push statt 200+ Elements re-enumerieren).
- _apply_visibility dedupe via sticky last-applied-signature
(STATE_SYNC-Echo loopt nicht mehr durch alle Layer).
- _update_clipping nur wenn alt oder neu hasClipping=True.
- Redundante doc.Views.Redraw() im CPlane-Pfad entfernt — die folgende
apply_visibility-Roundtrip redrawt 30ms spaeter ohnehin.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
UX-Cleanup:
- Globaler AN/AUS-Toggle entfernt — den gibt's bereits in der
Oberleiste, doppelt war redundant.
- Reload/Refresh-Button entfernt — Backend re-applied automatisch
bei jeder Regel-Aenderung, manuelles Reload nicht noetig.
- + (Neue Regel) wurde aus dem Header in eine neue Sektion
UNTER der Kombinationen-Card verschoben.
Neues Feature: Rule-Templates (einzelne wiederverwendbare Regeln)
- Storage: ~/Library/.../override_rule_templates.json (cross-doc,
parallel zu den Kombinationen-Presets)
- API in overrides.py: list/save/load/delete_rule_template
- Bridge-Messages: SAVE_RULE_TEMPLATE, DELETE_RULE_TEMPLATE,
ADD_FROM_TEMPLATE
- State enthaelt jetzt ruleTemplates: [{name, rule}]
UI:
- Neuer Bereich "Neue Regel" unter Kombinationen: [+ leer] +
[+ Aus Vorlage ▼ dropdown]
- Vorlage waehlen → insert auf hoechste Prio (gleich wie addRule)
- Im Dropdown unten: "🗑 <name> loeschen" zum Entfernen einer Vorlage
- Im Rule-Kontextmenue: neuer Eintrag "Als Vorlage speichern…"
fragt nach Name, speichert die Regel cross-doc
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
OVERRIDES war als gedocktes Panel zu schmal. Jetzt: kein Panel mehr,
sondern ein echtes Rhino-Fenster (Eto.Form + WebView, frei verschieb-
und resizable), das vom Oberleiste-Gear-Button geoeffnet wird.
panel_base.open_satellite_window:
- Akzeptiert jetzt optional einen `bridge`-Parameter. Wenn gegeben,
wird die Custom-Bridge (z.B. OverridesBridge) statt der einfachen
inline SAVE/CANCEL-Bridge benutzt. So koennen vollwertige Panels
(mit bidirektionalem Mess-Verkehr) als Satellite-Fenster laufen.
overrides_panel.py:
- register_and_open entfaellt — Overrides wird nicht mehr als Panel
registriert.
- Neue Funktion open_as_window(): erstellt OverridesBridge, registriert
sie in sticky["overrides_bridge"] und oeffnet als Satellite-Window.
Listener werden lazy beim ersten Aufruf installiert
(_ensure_listeners_once).
oberleiste.py:
- OPEN_OVERRIDES_PANEL ruft jetzt overrides_panel.open_as_window()
statt RhinoUI.Panels.OpenPanel().
OberleisteApp.jsx:
- Settings-Gear (ToolButton mit icon="settings") nach dem Preset-
Dropdown im Overrides-Bereich. Click ruft openOverridesPanel() →
oeffnet das Satelliten-Fenster.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Zwei Dinge:
1. embedded-Mode in den Dialog-Komponenten — wenn TRUE, kein Backdrop +
keine MaxWidth-Constraint, das Dialog fuellt das ganze WebView statt
wie ein kleines zentriertes Fenster IN dem WebView gerendert zu werden
(= "Fenster im Fenster"-Effekt). Betroffen:
- GeschossSettingsDialog
- EbenenSettingsDialog
- GeschossDialog
Satelliten-Apps (GeschossSettingsApp, EbenenSettingsApp,
GeschossDialogApp) passen jetzt `embedded` durch.
2. GeschossDialog (= der grosse Mehrfach-Editor hinter dem Pencil-Button)
laeuft jetzt auch als Satelliten-Fenster — selbe Architektur wie die
Settings-Dialoge. Backend hat neuen Handler _open_geschoss_dialog und
neuen Message OPEN_GESCHOSS_DIALOG. Auf Save: ganze z-Liste replace
+ _apply(save_z=True).
GeschossManager braucht den inline-Dialog-State nicht mehr; Pencil-Button
ruft openGeschossDialog(zeichnungsebenen).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Statt Overlay-im-Panel oeffnet sich der Settings-Dialog jetzt als
echtes Rhino-Fenster (verschiebbar, resizable, mehrere parallel).
Infrastruktur in panel_base.py:
- load_inline akzeptiert jetzt `params` (dict) und injiziert sie
als window.PANEL_PARAMS — Satelliten-Apps lesen ihren initialen
State daraus.
- Neue Funktion open_satellite_window(mode, params, title, size,
on_save, on_cancel): erstellt Eto.Forms.Form mit eingebetteter
WebView, eigenem Inline-Bridge fuer SAVE/CANCEL-Messages, ruft
Callbacks auf und schliesst das Fenster.
Backend rhinopanel.py:
- Neue Message-Handler OPEN_GESCHOSS_SETTINGS und OPEN_EBENEN_SETTINGS.
- _open_geschoss_settings: oeffnet das Satelliten-Fenster mit dem
Geschoss als Payload. on_save: replace im doc.Strings z-Liste +
_apply(save_z=True).
- _open_ebenen_settings: gleich, aber fuer Ebene + hatchPatterns.
Neue React-Entries:
- GeschossSettingsApp.jsx: wrappt GeschossSettingsDialog, liest
window.PANEL_PARAMS, schickt SAVE/CANCEL direkt via document.title-
Bridge.
- EbenenSettingsApp.jsx: gleich fuer EbenenSettingsDialog.
main.jsx-Switch erweitert um 'geschoss_settings' und 'ebenen_settings'.
GeschossManager und EbenenManager:
- Inline-Dialog-State und -Rendering entfernt.
- onSettings ruft jetzt openGeschossSettings(z) / openEbenenSettings(e)
in der Bridge auf → Backend oeffnet das Satelliten-Fenster.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>