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>
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>
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>
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>
Rhinos Parser collapsed mehrere \\par in Folge zu einem einzelnen
Linebreak. Loesung: jeder zusaetzliche \\par bekommt einen
{ }-Space-Group vor sich, damit der leere Paragraph "Inhalt" hat
und als echte Leerzeile gerendert wird.
Why: User-Test "TITEL\\n\\nTEXT" renderte als zwei aufeinander-
folgende Zeilen ohne Leerzeile dazwischen, obwohl die zweite \\n
korrekt in pending_pars gezaehlt wurde.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Zurueck zur Groups-Form ({...} pro Run), aber mit Two-Pass-fonttbl
das Pass 1 bereits korrekt populiert. Zwischen den Groups \\par
fuer Paragraph-Breaks — der einzige Linebreak-Token den Rhinos
TextEntity-Parser tatsaechlich rendert. \\line aus dem letzten
Versuch wurde von Rhino ignoriert.
pending_pars-Counter sammelt Newlines ueber Run-Grenzen hinweg,
sodass auch mehrere aufeinanderfolgende \\n (= Leerzeilen) erhalten
bleiben. Fuehrender \\par wird unterdrueckt (first_emitted-Flag),
damit der Text nicht mit einer Leerzeile beginnt.
Why: Per-Run-Groups isolieren Font-State (Fix fuer "letzte Schrift
dominiert"), waehrend \\par die Mehrzeiligkeit liefert die Rhino
versteht. Two-Pass garantiert dass fonttbl alle benutzten Fonts
enthaelt.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Rhinos TextEntity-RTF-Parser rendert \par offenbar nicht als
Zeilenumbruch innerhalb eines Textfeldes. \line dagegen funktioniert
als soft line break.
Globales pending_newlines-Counting ueber alle Runs hinweg: jedes \n
im Text-Run wird zu einem \line, der erst VOR dem naechsten echten
Text emittiert wird. Damit bleiben auch Leerzeilen (mehrere \n
hintereinander) als mehrere \line erhalten.
Why: User-Vergleich Screenshots — WYSIWYG zeigt korrekte Leerzeile
zwischen Heading und Paragraph, Rhino rendert beide Runs auf der
gleichen Zeile konkateniert.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Log-Analyse vom User: RTF preview zeigte
'{\\fonttbl{\\f0\\fnil\\fcharset0 Georgia;}}'
ABER der Body nutzt
'\\f1\\cf1\\fs20\\b ... Lorem ...'
\\f1 ist NICHT in der fonttbl! → Rhino fallback auf Default
\\cf1 ist NICHT in der colortbl (es gibt keine)! → ignoriert
Root cause: ich hatte die fonttbl/colortbl eagerly geschrieben BEVOR
die Runs ueberhaupt verarbeitet waren. fonts/colors-Listen wurden waehrend
des Body-Loops um neue Eintraege erweitert, aber der schon-emittierte
Header sah die nie.
Fix: Two-Pass.
PASS 1: Body bauen, dabei font_idx/color_idx aufrufen → fonts/colors-
Listen werden komplett gefuellt.
PASS 2: RTF-Header schreiben mit JETZT vollstaendigen Tables, dann Body
anhaengen.
Plus Leerzeilen: aufeinanderfolgende \\n in den Runs erzeugen jetzt
ein leeres Paragraph mit Space (" "), damit Rhinos Parser den \\par
nicht mit dem naechsten kollabiert. Resultat:
Lorem...\\par
\\par ← Leerzeile (echtes Space im leeren Paragraph)
Consectetur...
Plus Frame-Wrap-Diagnostic: "[TEXT-EDITOR] wrap: width=... applied_attr=
FormatWidth/TextWidth/None" damit ich sehen kann ob die Wrap-Property
ueberhaupt gesetzt wird in dieser Rhino-Version.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
User: Mixed-Fonts gehen immer noch nicht — Rhino zeigt nur die letzte
gesetzte Schrift, Bold-State auch verloren.
_runs_to_rtf neu: klassisches Inline-RTF ohne {} Groups. Pro Run werden
ALLE Codes explizit gesetzt (auch Reset-Codes \\b0 \\i0 \\ulnone \\cf0
\\nosupersub) und alle in EINER Zeile mit dem Text:
\\pard \\f0\\cf0\\fs60\\b\\i0\\ulnone\\nosupersub Georgia bold
\\par \\f1\\cf0\\fs20\\b0\\i0\\ulnone\\nosupersub Helvetica regular
Damit ist klar: jeder Run hat eigene state-Definitions. RTF-Parser
nimmt nicht "letzte Code wirkt auf alles".
_commit-Reihenfolge bei RichText geaendert: KEIN _apply_font wenn RTF
verwendet wird. te.Font wuerde sonst die per-Run \\fN Codes
ueberschreiben.
Method-Switch: te.SetRichText(rtf, dimstyle) zuerst probiert (robuster
API), te.RichText property als Fallback, _apply_font als letzter
Notfall.
Diagnostic: RTF wird jetzt mit Length + 300-char-preview gedruckt
("[TEXT-EDITOR] RTF len=... preview=..."). Bitte Log copy-pasten bei
weiteren Issues damit ich seh was tatsaechlich Rhino erreicht.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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-Bug: Heading (Helvetica Bold) + Paragraph (Georgia Regular) als
2 Zeilen ergibt in Rhino: alle Text in einer Zeile, mit Bold vom
Heading aber Font+Size vom LETZTEN definierten Paragraph (Georgia).
Root cause: Mein RTF-Generator emitted alle Codes hintereinander ohne
{}-Grouping. RTF-Parser ohne Scope nimmt das letzte \\f und \\fs als
globalen State und appliziert auf ALLES.
Fix _runs_to_rtf:
- Pro Run: text per \\n splitten, zwischen Segmenten \\par emitten
- Jedes nicht-leere Segment wird in {} Group gewrapped mit eigenen
Codes (\\fN \\cfN \\fsN \\b \\i \\ul \\super \\sub)
- Nur AKTIVE Toggles in der Group (kein \\b0/\\i0/\\ulnone mehr —
Default ausserhalb der Group ist plain)
- Resultat: {\\f0\\b Lorem ...}\\par {\\f1 Consectetur ...}
Jede Group ist isoliert, kein Cross-Run-State-Leak
_escape_no_par: \\n bleibt als Literal-Newline durchgelassen (splitting
geschieht im outer loop, nicht im escape).
_commit-Reihenfolge nochmal aufgeraeumt:
1. Plane
2. Defaults (Height, Font, Align) — gilt fuer alles
3. Content (RichText wenn nontrivial, sonst PlainText)
4. Wrap (FormatWidth + TextIsWrapped) — NACH RichText damit nicht
zurueckgesetzt
5. Frame/Mask/DrawForward
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>
1. Dialog-Positionierung: vp.WorldToScreen existiert nicht auf dieser
Rhino-Version. Ersetzt durch vp.WorldToClient → System.Drawing.Point
(viewport-lokale Pixel) + view.ScreenRectangle fuer absolute Position.
→ Dialog poppt jetzt wirklich neben dem Frame statt random.
2. TextArea-Hoehe: DynamicLayout expandiert die TextArea nicht
zuverlaessig (zeigte sich als 1-Zeilen-Streifen mit Buttons riesig
daneben). Fix: ta.Size = drawing.Size(...) explizit setzen.
3. 5-arg Font(face, FontWeight, FontStyle, underline, strike): Python.NET
3.0 erlaubt keinen bool→Enum-Cast mehr (Log: "int can not be converted
to Enum implicitly"). Fix: echte Enums Rhino.DocObjects.Font.FontWeight.
Bold/Normal + FontStyle.Italic/Upright benutzen. Damit funktioniert
auch Underline-Support endlich.
apply_settings_to_selection: kompletter Rewrite — statt Duplicate-Modify
wird eine FRESH TextEntity gebaut + alle Properties (Plane, PlainText,
TextHeight, Font, Align) gesetzt + per Replace eingebunden. DimStyle
wird auf Guid.Empty entkoppelt damit nicht die Style das Font-Setting
ueberschreibt. Sollte Bold/Italic-Un-Toggle-Bug fixen.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
User: Frame wird gezeichnet aber Editor erscheint nicht — kann nicht
schreiben. Eto.Form mit WindowStyle.None + Form.Show() funktioniert auf
Mac WebKit/Rhino-Build nicht zuverlaessig (kein Render oder hinter Rhino-
Window).
Fix: Eto.Dialog mit ShowModal — laeuft proven auf Mac+Windows. Dialog
hat normale Chrome (Title-Bar, OK/Cancel-Buttons) aber wird neben dem
gepickten Frame positioniert (via vp.WorldToScreen + view.ScreenRectangle
→ Dialog.Location 20px rechts vom Frame). Tradeoff zu "inline im
Viewport": Dialog hat Rahmen, aber ist sichtbar und funktional.
Workflow: pick Frame → Dialog poppt neben Frame mit TextArea + OK/Cancel
+ Cmd/Ctrl+Enter Shortcut + Esc-Abbruch.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
User-Wunsch:
- Rechteck als Frame statt 2-Punkte-Linie picken
- Direkt im Viewport schreiben statt zentrierter Dialog
- Bold/Italic/Underline echt anwendbar
Aenderungen:
_pick_text_frame: DynamicDraw-Event auf GetPoint zeichnet Live-
Rechteck-Vorschau in petrol-accent waehrend User die 2. Ecke picked
(analog Rhinos _Rectangle). Returns jetzt (p1, p2, origin, w, h).
_inline_editor (neu, ersetzt _frame_editor_dialog): chromeloses
Eto.Form (WindowStyle.None_) absolut positioniert ueber dem gepickten
Frame im Viewport via vp.WorldToScreen + view.ScreenRectangle. TextArea
fuellt den Frame. Cmd+Enter / Ctrl+Enter = commit, Esc = abbrechen.
Look-and-feel: schreibst direkt "im" Feld auf der Arbeitsflaeche.
_apply_font: erweitert um 5-arg Font(face,bold,italic,underline,strike)
Konstruktor als Pfad 1 (falls Rhino-Version das unterstuetzt). Fallback
3-arg Font, FromQuartetProperties, FindOrCreate.
apply_settings_to_selection: vor jedem Font-Set ein PlainText-Reset
damit eventuelle Rich-Text-Formatierungs-Runs nicht das neue te.Font
ueberschreiben — vermutlicher Bold-Toggle-Bug-Fix. Underline jetzt im
patch-Loop mit drin.
read_selection_settings: liest auch font.Underlined wenn vorhanden.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
User: Bold/Kursiv liessen sich nicht zurueck auf normal stellen.
Diagnose: te.Font.QuartetName kann je nach RhinoCommon-Version den Bold/
Italic-Suffix im Namen mitfuehren (z.B. "Helvetica-Bold"). Dann liest
read_selection_settings face="Helvetica-Bold" → wird in updateTs ans
Backend zurueckgeschickt → _apply_font ruft FindOrCreate("Helvetica-Bold",
False, False) → das matcht intern wieder die Bold-Variante = bleibt fett.
Fix in _apply_font:
- Suffix-Stripping: -Bold, -Italic, -Oblique, -BoldItalic etc. werden vom
face-String entfernt damit nur die Base-Family ("Helvetica") bleibt
- FromQuartetProperties zuerst (konstruiert Font direkt, unabhaengig vom
FontTable-Cache). FindOrCreate als Fallback.
- Diagnostic print: "[TEXT] _apply_font face=... bold=... italic=..."
damit sich nachvollziehen laesst was tatsaechlich angewendet wird
Plus textSettings/textStyles im State-Sig hinzugefuegt damit Idle-Pushes
Aenderungen nicht dedupelt verschlucken.
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>