Fruehjahrsputz: WinForms-Altlast entfernt, Dokumentation nachgezogen
Die Avalonia-Portierung ist abgeschlossen, damit ist die in ClawdDotNet.slnx angekuendigte Aufgabe "WinForms-Oberflaeche entfernen" faellig. Der Stand davor liegt unter dem Tag vor-fruehjahrsputz-2026-08. Entfernt (56 Dateien, seit dem Herausloesen der Anwendungsschicht nicht mehr Teil des Builds): ClawdDotNet.csproj, Program.cs, sieben frm_*-Formulare, UI/, Models/, EmbeddedUI/, Properties/, Resources/, Services/, das alte Anwendungssymbol und Deploy-Build.ps1 (ersetzt durch deploy/publish.py). Dazu configs/*.json - Beispielkonfigurationen aus der Zeit vor dem Instanzverzeichnis, auf die nur noch die alten Prompts verwiesen. Die vier Entwicklungs-Prompts der Anfangszeit ziehen nach docs/archiv/ um, mit README, das ihren Stand einordnet. Eine Regel darin gilt weiter - die Pflichtfelder fetchedAt/dataAsOf/source der Internet-Tools -, deshalb Archiv statt Loeschen; der WebSearch-Plan verweist auf den neuen Pfad. Toter Code - PlaceholderPageViewModel samt Ansicht: Es gibt keinen Platzhalter-Bereich mehr, seit alle neun Seiten portiert sind. - Snappier als direkter Paketverweis: MongoDB.Driver loest es ohnehin auf dieselbe Fassung auf, der Verweis hob nichts an. Zwei Fehler, die dabei sichtbar wurden - Die taegliche Sicherung lief ins Leere. Die Oberflaeche bot sie an und schrieb Uhrzeit, Zielordner und Anzahl in die Einstellungen, aber der BackupScheduler wurde nirgends erzeugt. Jetzt am AppHost verdrahtet und in den geordneten Abbau aufgenommen. - SettingsPageViewModel hielt die vier Sicherungs-Einstellungen doppelt. Aus der Ansicht waren sie laengst verschwunden, gelesen und beim Speichern zurueckgeschrieben wurden sie weiter: Wer die Uhrzeit auf der Sicherungs-Seite aenderte und danach die Einstellungen speicherte, bekam den alten Wert zurueck. Pakete: keine bekannten Sicherheitsluecken mehr - SQLitePCLRaw.bundle_e_sqlite3 auf 2.1.13 angehoben. Microsoft.Data.Sqlite bringt 2.1.11 mit, darin steckt GHSA-2m69-gcr7-jv3q (NU1903, hoch). - SharpCompress bleibt als direkter Verweis stehen. Beim Aufraeumen erst als ungenutzt entfernt - dabei kam die von MongoDB.Driver gezogene Fassung 0.30.1 mit GHSA-6c8g-7p36-r338 zurueck. Der Verweis ist eine Anhebung, kein Ballast; das steht jetzt als Kommentar dabei. Dokumentation - Roadmap mit Statusblock: A1 und A3 erledigt, A2 nur zur Haelfte - Gate, Policy und Dienst greifen, aber keine Ansicht ruft ApproveAsync auf, ein gestagter Aufruf liegt unbeantwortet. Das ist jetzt Punkt 1 der Reihung. Rocket.Chat steht und kollidiert mit A5 (Matrix) - Entscheidung faellig. - Avalonia-Portierungsleitfaden -> Oberflaechen-Leitfaden: kein Auftrag mehr, sondern Beschreibung des Stands. - Bestandsaufnahme und Linux-Analyse als datierte Befunde gekennzeichnet; der teure Teil der Linux-Analyse (8.900 Zeilen WinForms) ist hinfaellig. - Verweise auf frm_*, WebView2 und ClawdDotNet.csproj in den lebenden Dokumenten richtiggestellt. Build fehlerfrei, 585 Tests gruen (6 uebersprungen). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
# Oberfläche — Leitfaden
|
||||
|
||||
Für alle, die an `src/ClawdDotNet.Desktop` arbeiten.
|
||||
Stand: 2026-08-23.
|
||||
|
||||
---
|
||||
|
||||
## 1. Stand
|
||||
|
||||
**Die Portierung ist abgeschlossen.** Es gibt keine Platzhalter mehr und keine
|
||||
WinForms-Vorlage, gegen die man vergleichen könnte: Die alte Oberfläche
|
||||
(`ClawdDotNet.csproj`, `frm_*.cs`, `UI/`, `Models/`, `EmbeddedUI/`) ist am
|
||||
2026-08-23 aus dem Arbeitsbaum entfernt worden. Wer sie doch einmal braucht,
|
||||
findet sie im Tag `vor-fruehjahrsputz-2026-08`.
|
||||
|
||||
Neun Bereiche, alle nativ in Avalonia:
|
||||
|
||||
| Gruppe | Bereich | Ansichtsmodell |
|
||||
|---|---|---|
|
||||
| Arbeit | Chat, Agenten, Aufgaben | `ChatPageViewModel`, `AgentsPageViewModel`, `TasksPageViewModel` |
|
||||
| Analyse | Token-Verbrauch, Agenten-Chats | `TokenUsagePageViewModel`, `AgentChatsPageViewModel` |
|
||||
| System | Sicherung, Protokoll, Einstellungen, Info | `BackupPageViewModel`, `LogPageViewModel`, `SettingsPageViewModel`, `InfoPageViewModel` |
|
||||
|
||||
Dazu die Fenster `InstancePickerWindow`, `LicenseWindow`, `TextEditorWindow` und
|
||||
die Dialoge zum Anlegen von Agent, Auftrag und Dienst.
|
||||
|
||||
Das Erscheinungsbild folgt dem Entwurf in `Mockup/` — wer daran etwas ändert,
|
||||
liest zuerst `Mockup/extracted/mockup/Implementierungsleitfaden.md`.
|
||||
|
||||
**Was in der Oberfläche noch fehlt**, siehe [Roadmap](Roadmap.md):
|
||||
die Freigabe-Ansicht für gestagte Aufrufe (A2) und die Schaltfläche
|
||||
„Fehler melden" (DC1).
|
||||
|
||||
---
|
||||
|
||||
## 2. Drei Regeln, die nicht verletzt werden dürfen
|
||||
|
||||
### 2.1 Der Schichtschnitt
|
||||
|
||||
```
|
||||
src/ClawdDotNet.App ← Fachlogik. KEIN Verweis auf Avalonia. Niemals.
|
||||
src/ClawdDotNet.Desktop ← Oberfläche. Darf App und Core verwenden.
|
||||
```
|
||||
|
||||
`ClawdDotNet.App` muss ohne Fenster laufen — darauf setzt der geplante systemd-Dienst auf.
|
||||
Sobald dort ein `using Avalonia…` steht, ist der Schnitt kaputt und fällt erst Wochen
|
||||
später auf.
|
||||
|
||||
**Faustregel:** Alles, was Dateien liest, rechnet oder mit der Engine spricht, gehört nach
|
||||
`App`. Alles, was etwas anzeigt, nach `Desktop`.
|
||||
|
||||
### 2.2 Fäden
|
||||
|
||||
Ereignisse aus `AgentEngine`, `TaskScanner`, `OpenRouterStatusService` und
|
||||
`BackupScheduler` kommen auf **Hintergrundfäden**. Eine `ObservableCollection` von dort aus
|
||||
zu ändern wirft entweder oder beschädigt still die Anzeige.
|
||||
|
||||
```csharp
|
||||
// Aus einem Ereignis der Fachschicht heraus:
|
||||
Dispatcher.UIThread.Post(() => Lines.Add(neu));
|
||||
|
||||
// Wenn ein Rückgabewert gebraucht wird:
|
||||
await Dispatcher.UIThread.InvokeAsync(() => …);
|
||||
```
|
||||
|
||||
Ein `DispatcherTimer` läuft dagegen bereits auf dem Oberflächenfaden — dort ist kein
|
||||
Wechsel nötig (siehe `LogPageViewModel`).
|
||||
|
||||
### 2.3 Avalonia **12**, nicht 11
|
||||
|
||||
Praktisch alle Anleitungen im Netz sind für Avalonia 11 und lassen sich hier nicht
|
||||
übernehmen. Bekannte Unterschiede:
|
||||
|
||||
- `BindingPlugins` ist nicht mehr öffentlich. Das übliche
|
||||
`DisableAvaloniaDataAnnotationValidation()` aus den 11er-Vorlagen **entfällt ersatzlos** —
|
||||
nicht nachbauen.
|
||||
- `ShutdownMode` voll qualifizieren: `Avalonia.Controls.ShutdownMode`.
|
||||
|
||||
Diese Fehler brechen den Build. Das ist gut — sie fallen sofort auf.
|
||||
|
||||
---
|
||||
|
||||
## 3. Das Muster
|
||||
|
||||
Der Logs-Bereich ist als vollständiges Beispiel gebaut. Drei Dateien, drei Aufgaben:
|
||||
|
||||
**`src/ClawdDotNet.App/Services/LogTail.cs`** — die Fachlogik. Liest Dateien, kennt keine
|
||||
Oberfläche, wäre ohne Fenster lauffähig.
|
||||
|
||||
**`src/ClawdDotNet.Desktop/ViewModels/LogPageViewModel.cs`** — das Ansichtsmodell. Erbt von
|
||||
`PageViewModel`, hält Zustand und Befehle. Kennt keine Steuerelemente.
|
||||
|
||||
**`src/ClawdDotNet.Desktop/Views/LogPageView.axaml`** — die Ansicht. Nur Aufbau und
|
||||
Bindungen.
|
||||
|
||||
### Ein neuer Bereich in vier Schritten
|
||||
|
||||
**1.** Ansichtsmodell anlegen, von `PageViewModel` erbend:
|
||||
|
||||
```csharp
|
||||
public sealed partial class InfoPageViewModel : PageViewModel
|
||||
{
|
||||
public InfoPageViewModel(AppHost? host) : base("Info") { … }
|
||||
}
|
||||
```
|
||||
|
||||
`AppHost?` ist **nullbar** — der Entwurfsmodus des Editors erzeugt das Ansichtsmodell ohne
|
||||
laufenden Aufbau. Bei `null` einfach nichts starten und Beispielwerte zeigen.
|
||||
|
||||
**2.** Ansicht anlegen: `Views/InfoPageView.axaml` + `.axaml.cs`. Der Name muss der
|
||||
Konvention folgen — `ViewLocator` sucht `…ViewModels.FooViewModel` → `…Views.FooView`.
|
||||
Passt der Name nicht, steht der gesuchte Typ im Fenster statt der Ansicht.
|
||||
|
||||
**3.** In `MainWindowViewModel` den Platzhalter ersetzen:
|
||||
|
||||
```csharp
|
||||
new PlaceholderPageViewModel("Info", "…") // vorher
|
||||
new InfoPageViewModel(host) // nachher
|
||||
```
|
||||
|
||||
**4.** `x:DataType` in der AXAML setzen. Ohne das greifen die kompilierten Bindungen nicht
|
||||
und Tippfehler in Bindungspfaden fallen erst zur Laufzeit auf.
|
||||
|
||||
### Werkzeugkasten
|
||||
|
||||
- Zustand: `[ObservableProperty] private string _text = "";` → erzeugt `Text` samt
|
||||
Benachrichtigung.
|
||||
- Befehle: `[RelayCommand] private void Speichern() { … }` → bindbar als
|
||||
`SpeichernCommand`.
|
||||
- Formatierung gehört in `Styles/`, nicht an einzelne Steuerelemente. Seit der
|
||||
Umsetzung des Entwurfs aus `Mockup/` liegt sie in drei Dateien:
|
||||
`Theme.axaml` (Farben je Thema, Schriften), `Icons.axaml` (Symbolgeometrien),
|
||||
`Shell.axaml` (Steuerelement-Vorlagen und Stilklassen).
|
||||
Klassen: `h1`, `h2`, `kicker`, `label`, `caption`, `muted`, `mono`, `card`,
|
||||
`console`, `hr`, `sep`, `toolbar`, `thead`, `tr`, `th`, `td`, `tag`,
|
||||
`statusbar`, `topbar`, `sidebar`, `nav`; an Schaltflächen zusätzlich
|
||||
`primary`, `toolbar`, `ghost`, `flat`, `icon`.
|
||||
- Symbole über `Controls/StrokeIcon.cs` mit einer Geometrie aus `Icons.axaml`.
|
||||
Die Farbe wird geerbt — nicht gesetzt.
|
||||
- Rahmen mit Eckmarken über `Controls/BlueprintFrame.cs`. Sparsam: nur Dialoge
|
||||
und die Info-Karte.
|
||||
- Tabellen von Hand aus `Border.thead` + `ListBox.table`, nicht mit `DataGrid`.
|
||||
Das Paket ist nicht mehr referenziert.
|
||||
- **Avalonia 12 hat die Ressourcenschlüssel des Fluent-Themas umgebaut.** Die aus
|
||||
11er-Anleitungen bekannten Namen (`ButtonBackground`, `TextControlBackground`,
|
||||
`ControlCornerRadius` …) existieren nicht mehr; ein Setter darauf ist wirkungslos
|
||||
und fällt nicht auf. Für neue Steuerelemente deshalb eine eigene `ControlTheme`
|
||||
in `Shell.axaml` schreiben statt zu versuchen, Fluent umzufärben.
|
||||
- Ordner öffnen: `Core.Storage.SystemShell.OpenFolder(pfad)`. **Kein** `explorer.exe`.
|
||||
- Dateinamen erzeugen: `Core.Storage.PortableFileName.Sanitize(name)`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Prüfliste für die leisen Fehler
|
||||
|
||||
Diese Klasse bricht weder den Build noch die Tests. Vor jeder Abgabe durchgehen:
|
||||
|
||||
- [ ] **Fenster-Schließen behandelt?** Wartet der Code auf eine Antwort aus einem Fenster
|
||||
(`TaskCompletionSource`), muss `window.Closed` als Abbruch gelten. Sonst hängt der
|
||||
Ablauf lautlos für immer.
|
||||
- [ ] **Sammlungen nur vom Oberflächenfaden geändert?** Siehe 2.2.
|
||||
- [ ] **Wächst etwas unbegrenzt?** Listen, die im Betrieb volllaufen, brauchen eine
|
||||
Obergrenze (`LogPageViewModel.MaxLines = 2000` als Vorbild).
|
||||
- [ ] **Timer beendet?** `DispatcherTimer` in einem Ansichtsmodell läuft weiter, auch wenn
|
||||
der Bereich nicht sichtbar ist. Bei teuren Abfragen anhalten.
|
||||
- [ ] **Farben aus dem Thema?** Keine festen Farbwerte — die Anwendung läuft hell und
|
||||
dunkel. `{DynamicResource …}` verwenden.
|
||||
- [ ] **Keine relativen Pfade.** `./Backups` und Ähnliches hängt vom Arbeitsverzeichnis ab
|
||||
und zeigt unter Linux ins Leere. `AppPaths.DataDirectory` verwenden.
|
||||
- [ ] **Kein `MessageBox`, kein `System.Windows.Forms`, kein `System.Drawing`.**
|
||||
|
||||
---
|
||||
|
||||
## 5. Abnahme
|
||||
|
||||
```bash
|
||||
dotnet build ClawdDotNet.slnx
|
||||
```
|
||||
|
||||
```bash
|
||||
dotnet test tests/ClawdDotNet.Core.Tests/ClawdDotNet.Core.Tests.csproj
|
||||
```
|
||||
|
||||
Beide müssen fehlerfrei sein — 567 Tests, keine neuen Fehlschläge.
|
||||
|
||||
**Und dann tatsächlich starten.** Die Oberfläche hat keine Testabdeckung; die Fehler aus
|
||||
Abschnitt 4 fallen ausschließlich beim Laufen auf.
|
||||
|
||||
```bash
|
||||
dotnet run --project src/ClawdDotNet.Desktop
|
||||
```
|
||||
|
||||
Hinweis: Ein Starttest hinterlässt unter Windows einen Prozess, der die `.exe` sperrt und
|
||||
den nächsten Build mit `MSB3021` scheitern lässt. Aufräumen mit:
|
||||
|
||||
```bash
|
||||
powershell -Command "Get-Process ClawdDotNet -EA SilentlyContinue | Stop-Process -Force"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Wenn etwas unklar ist
|
||||
|
||||
Lieber nachfragen als raten. Zwei Dinge sind besonders leicht falsch zu machen:
|
||||
|
||||
- **Was gehört in welche Schicht?** Im Zweifel nach `App` — von dort kann die Oberfläche
|
||||
es holen, umgekehrt nicht.
|
||||
- **Wie kommen Daten aus der Engine in die Ansicht?** `AppHost` gibt `Engine`, `Scanner`,
|
||||
`Staging`, `Status` und `Usage` heraus; alle sind **nullbar**, wenn kein
|
||||
OpenRouter-Schlüssel hinterlegt ist. Diesen Fall mitdenken — die Anwendung läuft dann
|
||||
bewusst ohne Agenten.
|
||||
Reference in New Issue
Block a user