M28: Übersicht Fokus-Modus + einheitliches Close-Button-Layout
Klick auf einen Knoten in der Übersicht öffnet jetzt ein schwebendes Popup mit der kompletten verbundenen Kette (neu OverviewGraph. connectedChain, volle transitive Hülle unabhängig vom Knotentyp, bewusst getrennt von der bestehenden highlightedNodeIDs), sauber im selben Spalten-Layout neu angeordnet, Rest des Diagramms abgedunkelt. Nicht-modales Overlay statt .sheet, damit die rechte Seitenleiste währenddessen bedienbar bleibt (Knoten direkt aus dem Popup heraus bearbeitbar). Popup-Größe passt sich automatisch dem Inhalt an, keine Scrollbalken. Der dabei entstandene Close-Button-Header (Titel + Spacer + X, fest oben, Divider direkt darunter) wurde auf alle vier Popup-Formulare der App vereinheitlicht: Experte-Bearbeiten-Sheet und die drei Devices-Sheets (Rohdaten, Netzwerk-Test, Port-Scan) — dort ersetzt er jeweils den bisherigen einzelnen "Schließen"-Button unten. Mehrere Design-Iterationen live mit dem User durchgespielt (Trennlinie im Canvas → separates Panel → Popup → .sheet → non-modales Overlay), finale Version live bestätigt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -29,3 +29,44 @@ Nachbesserung 2 (User-Feedback: Zeilen jetzt abgeschnitten, Fenster breiter mach
|
||||
Nachbesserung 3 (User-Screenshot: an den äußeren Rändern noch viel ungenutzter Platz). Ursache gefunden: `TextField(titleKey:text:)` zeigt auf macOS den Titel als feste Beschriftung vor dem Wert an (kein iOS-Platzhalter, der beim Tippen verschwindet) — bei 30+ Zeilen fraß "Parameter"/"Wert" so bei jeder einzelnen Zeile erneut Breite. Fix: echte Grid-Spalten für Parameter/Wert mit einmaliger Kopfzeile statt Pro-Zeile-Label, leere TextField-Titel (`.textFieldStyle(.plain)`), Wert-Spalte mit `maxWidth: .infinity`.
|
||||
|
||||
Nachbesserung 4 (User-Feedback: letzte Zeile überschneidet sich mit dem Button "Parameter hinzufügen", zwischen Zeilen soll eine Trennlinie sein): horizontale `Divider()` nach jeder Zeile ergänzt (spannt alle 5 Grid-Spalten), 12pt Abstand unter dem Grid vor dem Button. Build grün, noch nicht visuell/live geprüft (kein Router hier erreichbar).
|
||||
|
||||
### 3. Übersicht: Fokus-Modus beim Anklicken eines Items
|
||||
**Status:** fixed (live bestätigt)
|
||||
|
||||
Wunsch: Klick auf einen Node in der Übersicht (Graph-Ansicht, `OverviewView.swift`) soll den Node in den freien Bereich schieben, alle anderen Nodes ausfaden, und die Parents/Childs des aktuell gewählten Nodes nach unten schieben und sauber neu anordnen.
|
||||
|
||||
Ist-Zustand (`OverviewView.swift`): Klick setzt aktuell nur `selectedNodeID` und highlightet die verbundenen Kanten/Nodes (`OverviewGraph.highlightedNodeIDs`) — Positionen ändern sich nicht. Es gibt bereits ein Spalten-Layout (`OverviewLayout`, feste `columnOrder`/`columnWidth`) plus manuelle Drag-Offsets pro Node (`nodeOffsets`), die on top der berechneten Grid-Positionen liegen und über einen "Zurücksetzen"-Button löschbar sind (nicht persistiert, geht bei Neuladen verloren).
|
||||
|
||||
Klärung (User-Antworten):
|
||||
1. Horizontale Trennlinie über den Canvas; ausgewählter Node + Kette werden unterhalb dieser Trennlinie dargestellt (eigener Bereich, getrennt von der normalen Graph-Darstellung darüber).
|
||||
2. Fokus-Modus ist temporär — aufhebbar (Klick auf leere Fläche / erneuter Klick), keine dauerhafte Layout-Änderung, `nodeOffsets` bleibt unberührt.
|
||||
3. Ausfaden = abdunkeln (nicht ausblenden).
|
||||
4. Komplette verbundene Kette (nicht nur 1 Ebene) — deckt sich mit dem bereits vorhandenen `highlightedNodeIDs` (rekursiv).
|
||||
|
||||
5. Ketten-Layout im unteren Bereich: gleiches Spalten-Layout wie oben (`OverviewLayout`-Logik wiederverwendet, nur auf die Kette beschränkt) — kein neuer Layout-Algorithmus nötig.
|
||||
|
||||
Umsetzung (`OverviewGraph.swift`, `OverviewView.swift`, `OverviewGraphTests.swift`, `L10n.swift`):
|
||||
- Neue Methode `OverviewGraph.connectedChain(startingAt:)` — volle rekursive Kette unabhängig vom Node-Typ (bewusst getrennt von `highlightedNodeIDs`, das nur bei Interfaces mehr als 1 Hop expandiert — eine andere, bereits live bestätigte, bewusst enger gefasste Funktion). Mit Test abgedeckt.
|
||||
- `OverviewView`: Fokus-Modus hängt direkt an `viewModel.selectedNodeID` (kein separater Zustand) — Klick auf Node selektiert wie bisher, macht das aber jetzt gleichzeitig zum Fokus-Trigger; erneuter Klick auf denselben Node hebt ihn wieder auf (bestehende Toggle-Logik, kein neuer Code nötig).
|
||||
- Kette wird per `OverviewLayout.layout(for:)` auf ein gefiltertes Sub-Graph-Objekt neu angeordnet (`focusSubLayout`) und unterhalb einer Trennlinie eingeblendet (`effectivePositions` verschiebt nur Kettenknoten dorthin, alle anderen bleiben an ihrer normalen Position).
|
||||
- Ausfaden: nicht-Ketten-Nodes bekommen `opacity(0.25)`; `EdgesCanvas` bekommt die Kette als `highlightedNodeIDs` übergeben, wodurch dessen bereits vorhandene Dimm-Logik für Kanten automatisch mitgreift (keine Kante kann je über die Trennlinie laufen, da eine Kette per Definition die transitive Hülle ist).
|
||||
- Leere-Fläche-Klick hebt Fokus auf: `EdgesCanvas` bekommt einen neuen `onBackgroundTap`-Callback, der bei ihrem bereits vorhandenen Tap-Gesture-Handler greift, wenn kein Edge getroffen wurde.
|
||||
- Dragging ist für Nodes deaktiviert, solange sie im Fokus-Streifen sitzen (Position wird bei jeder Auswahl neu berechnet, ein gespeicherter Drag-Offset würde sonst nach Verlassen des Fokus unerwartet wieder auftauchen).
|
||||
|
||||
Build grün, alle Tests (inkl. neuem `testConnectedChainExpandsTransitivelyForEveryNodeKind`) grün, App startet ohne Absturz. Noch nicht visuell/live geprüft — kein Tool für native-macOS-UI-Interaktion hier verfügbar, bitte im Übersicht-Tab gegentesten (Klick auf Node → Fokus-Streifen unten, Klick auf leere Fläche/erneuter Klick → Fokus verlassen, anderen Kettenknoten anklicken → Fokus wechselt).
|
||||
|
||||
Nachbesserung 1 (User-Feedback: nach Klick soll die Fokus-Ebene in den sichtbaren Viewport geschoben werden, ohne Scrollbars) — inzwischen durch Nachbesserung 2 ersetzt: `viewportSize` per `GeometryReader` mitverfolgt, Auto-Zoom+Auto-Scroll auf einen `"focusAnchor"`-Marker im Fokus-Streifen.
|
||||
|
||||
Nachbesserung 2 (User-Feedback: statt Trennlinie/eingebettetem Bereich ein separates Panel mit Breite des Original-Viewports, Höhe automatisch an Inhalt angepasst, eigener Close-Button) — ersetzt Nachbesserung 1 komplett: Der Divider-Ansatz (Kette in denselben zoombaren Canvas verschoben, Auto-Fit-Zoom+Auto-Scroll) ist raus. Hauptdiagramm bleibt jetzt unverändert an Ort und Stelle (keine Repositionierung mehr, kein `combinedCanvasSize`/`fitScale`/`ScrollViewReader` mehr) — nicht zur Kette gehörende Nodes werden weiterhin abgedunkelt (`opacity(0.25)`). Neues `focusPanel(subGraph:subLayout:)` erscheint als eigener Bereich unterhalb der Haupt-`ScrollView` (in einem `VStack`, kein Overlay): Breite = `viewportSize.width` (Breite der Haupt-Diagramm-Scrollview, per `GeometryReader` gemessen), Höhe = `subLayout.canvasSize.height + 56` (passt sich der Kettengröße an, kein künstliches Clamping). Eigene Kopfzeile mit "Fokus"-Titel + `xmark.circle.fill`-Button zum Schließen. Kette selbst per eigenem `EdgesCanvas`+Node-Loop gerendert (gleiches `OverviewLayout`, alle internen Kanten hell da komplett als `highlightedNodeIDs` durchgereicht), eigener Hover/Tooltip-State (`panelHoveredEdge`/`panelHoverPoint`), da eigener Koordinatenraum, getrennt von der Haupt-Diagramm-Tooltip-Logik. Klick auf einen Node im Panel wechselt den Fokus (kein Toggle-Aus mehr dort, dafür gibt's jetzt den Close-Button); Hintergrund-Klick/erneuter Klick auf denselben Node oben im Hauptdiagramm schließen weiterhin zusätzlich. Drag-Deaktivierung für Kettenknoten ist wieder raus (nicht mehr nötig, da Hauptdiagramm nicht mehr verschoben wird). Build+Tests grün, App startet ohne Absturz. Noch nicht live geprüft.
|
||||
|
||||
Nachbesserung 3 (User-Feedback: von Panel auf Popup wechseln) — inzwischen durch Nachbesserung 4 ersetzt: eigenes `ZStack`-Overlay mit Scrim + abgerundeter Karte statt fester VStack-Zeile.
|
||||
|
||||
Nachbesserung 4 (User-Feedback: "passt nicht", stattdessen wie beim Bearbeiten-Formular — Größe/Breite automatisch anpassen, Scrollbalken verhindern, Close-Button) — Sizing-Teil bleibt, Präsentation wurde in Nachbesserung 5 wieder korrigiert: natives `.sheet(isPresented:)` (gleiches Muster wie `ExpertItemEditView`), kein `ScrollView` mehr, `.fixedSize()` auf `subLayout.canvasSize` (exakte Content-Größe statt fixer Viewport-Breite).
|
||||
|
||||
Nachbesserung 5 (User-Feedback: Seitenleiste muss gleichzeitig bedienbar bleiben, um Knoten zu bearbeiten): `.sheet` ist ein echtes macOS-Modal — blockiert das gesamte Fenster inkl. der rechten Seitenleiste (`detailPanel`, "Bearbeiten"-Button), das war der Bug. Zurück auf nicht-modales `ZStack`-Overlay + Scrim (wie Nachbesserung 3), aber mit der exakten Content-Größe aus Nachbesserung 4 (kein `ScrollView`, `.fixedSize()` auf `subLayout.canvasSize`) statt der alten festen Viewport-Breite. `isFocusPresented`-Binding wieder entfernt. Sidebar bleibt jetzt durchgehend bedienbar, da sie eine eigene `HSplitView`-Pane ist, die vom Overlay nie erreicht wird. Build+Tests grün, App startet ohne Absturz.
|
||||
|
||||
Nachbesserung 6 (User-Wunsch: den Close-Button-Stil aus dem Fokus-Popup bei allen anderen Formularen/Sheets ebenfalls nachziehen) — betrifft nicht mehr nur Fund #3, sondern alle Sheet-Formulare der App: `ExpertItemEditView` (Experte-Bearbeiten-Sheet, `ExpertMenuDetailView.swift`) bekommt eine neue Kopfzeile mit Menü-Titel + `xmark.circle.fill`-Button (schließt via `cancelEditing()+dismiss()`, wie das bestehende "Abbrechen" unten — bleibt zusätzlich erhalten, da es semantisch mehr ist als nur schließen). Die drei Devices-Sheets (`RawFieldsSheet`, `NetworkToolResultSheet`, `PortScanResultSheet` in `DevicesView.swift`) hatten je nur einen "Schließen"-Textbutton unten — durch dieselbe Kopfzeile mit X-Button ersetzt (kein doppelter Close mehr). Neue L10n-Zeile "Schließen, ohne zu speichern" ergänzt.
|
||||
|
||||
Nachbesserung 7 (User-Feedback: alle Close-Buttons müssen in der Header-Leiste des Popups liegen, nicht doppelt): das untere "Abbrechen" in `ExpertItemEditView` war seit Nachbesserung 6 doppelt zum neuen Kopfzeilen-X — entfernt, einziger Close-Button ist jetzt der im Header. "Anlegen"/"Speichern" bleibt unten als reine Save-Aktion (kein Close). Geprüft: alle übrigen "Abbrechen"-Vorkommen in der App gehören zu nativen `.confirmationDialog`/`.alert`-Dialogen (keine Header-Leiste, nicht betroffen).
|
||||
|
||||
Nachbesserung 8 (User-Wunsch: Verhalten+Layout des Close-Buttons bei ALLEN Popups exakt wie beim Fokus-Popup der Übersicht) — Rückfrage gestellt, ob der Header beim Experte-Bearbeiten-Sheet dafür aus der scrollenden Form raus und fest oben stehen soll (mehr Umbau) oder nur optisch angeglichen wird; User: fest oben, wie Fokus-Popup. Umgesetzt in allen vier Popups (`ExpertItemEditView`, `RawFieldsSheet`, `NetworkToolResultSheet`, `PortScanResultSheet`): identische Struktur wie `OverviewView.focusPanel` — `HStack{Titel; Spacer; xmark.circle.fill-Button}.padding(10)` gefolgt direkt von `Divider()`, außerhalb jeder Form/ScrollView/Section, bleibt beim Scrollen des restlichen Inhalts immer sichtbar. Beim Experte-Sheet wanderte der Header dafür vor die `Form` (Mindesthöhe leicht erhöht: 480→520/idealHeight 620→660, damit durch den zusätzlichen festen Header nichts gestaucht wirkt). Bei den drei Devices-Sheets wurde die bisher gemeinsame Erklärungs-Caption+Divider+Content-Gruppe vom Header getrennt und bekam eigenes Padding. Build+Tests grün, App startet ohne Absturz. Noch nicht live geprüft — bisher nur der Fokus-Popup-Teil (Fund #3) wurde live bestätigt "sieht gut", der Rest noch nicht.
|
||||
|
||||
Reference in New Issue
Block a user