Sicherungspunkt vor dem Aufraeumen. Buendelt die Arbeit, die seit dem Abschluss der Avalonia-Portierung im Arbeitsverzeichnis lag. Oberflaeche - Entwurf aus Mockup/ umgesetzt: Theme.axaml (Farben je Thema, Barlow als mitgelieferte Schrift), Icons.axaml (Symbolgeometrien), Shell.axaml (eigene ControlThemes statt Fluent umzufaerben). - Neue Steuerelemente StrokeIcon und BlueprintFrame, Seiten fuer Token-Verbrauch und Agenten-Chats, Werkzeug-Einstellungen als Seite statt eigenem Fenster, Texteditor-Fenster. - ThemeManager mit hellem und dunklem Thema; die beiden Pinsel-Konverter entfallen, weil ein fester Farbwert den Themenwechsel nicht ueberlebt. Rocket.Chat - Neues Tool-Projekt (Client, Konfiguration, Workspace-Dateien) nach der Bauform des Telegram-Tools: rocketchat_poll als Tool-Job, geweckt wird nur, wenn wirklich etwas anliegt. - send_file ist freigabepflichtig, send_message bewusst nicht: Der Raum ist Arbeitsraum, der Schutz sitzt an der Raum-Allowlist. - Konzept-Doc um die Messung gegen die echte Instanz 8.7 ergaenzt; drei Annahmen waren falsch und sind korrigiert. Deploymentcenter - DC6 (Update anwenden) und DC7 (Erstinstallation ueber setup.json) erledigt, DC3 fuer win-x64/dev; deploy/publish.py als Release-Strecke. - AppHost.DisposeAsync gegen doppeltes Herunterfahren gesperrt - sonst ueberschreibt eine zweite Abmeldung den Wartungszustand am Watchdog. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8.1 KiB
Avalonia-Portierung — Leitfaden
Für alle, die weitere Ansichten von WinForms nach Avalonia übertragen. Stand: 2026-08-07.
1. Auftrag
Drei Bereiche des Hauptfensters sind noch Platzhalter. In dieser Reihenfolge portieren — sie steigen im Umfang, und jede baut auf dem Muster der vorigen auf:
| # | Bereich | WinForms-Vorlage | Daten aus |
|---|---|---|---|
| 1 | Info | frm_main.Designer.cs, Suchwort tabPage_Info |
AppHost.AppVersion, AppHost.BuildSummary, host.Instance |
| 2 | Sicherung | UI/BackupPanel.cs + UI/BackupPanel.Designer.cs |
host.InstancePath, host.Settings, Core.Backup.BackupService |
| 3 | Aufgaben (Jobs/Services/Verlauf) | frm_main.cs, Abschnitt WORKER TAB ab Zeile 932 |
host.Instance.Agents, App.Services.JobHistoryService |
Nicht anfassen: Chat und Einstellungen. Beide sind Entwurfsarbeit, nicht Übersetzung, und werden gesondert gemacht.
Die WinForms-Dateien liegen noch im Repository, sind aber nicht mehr Teil des Builds
(siehe Kommentar in ClawdDotNet.slnx). Sie sind Vorlage zum Lesen — nicht zum Kompilieren,
nicht zum Reparieren.
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.
// 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:
BindingPluginsist nicht mehr öffentlich. Das üblicheDisableAvaloniaDataAnnotationValidation()aus den 11er-Vorlagen entfällt ersatzlos — nicht nachbauen.ShutdownModevoll 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:
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:
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 = "";→ erzeugtTextsamt Benachrichtigung. - Befehle:
[RelayCommand] private void Speichern() { … }→ bindbar alsSpeichernCommand. - Formatierung gehört in
Styles/, nicht an einzelne Steuerelemente. Seit der Umsetzung des Entwurfs ausMockup/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ätzlichprimary,toolbar,ghost,flat,icon. - Symbole über
Controls/StrokeIcon.csmit einer Geometrie ausIcons.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 mitDataGrid. 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 eigeneControlThemeinShell.axamlschreiben statt zu versuchen, Fluent umzufärben. - Ordner öffnen:
Core.Storage.SystemShell.OpenFolder(pfad). Keinexplorer.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), musswindow.Closedals 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 = 2000als Vorbild). - Timer beendet?
DispatcherTimerin 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.
./Backupsund Ähnliches hängt vom Arbeitsverzeichnis ab und zeigt unter Linux ins Leere.AppPaths.DataDirectoryverwenden. - Kein
MessageBox, keinSystem.Windows.Forms, keinSystem.Drawing.
5. Abnahme
dotnet build ClawdDotNet.slnx
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.
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:
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?
AppHostgibtEngine,Scanner,Staging,StatusundUsageheraus; alle sind nullbar, wenn kein OpenRouter-Schlüssel hinterlegt ist. Diesen Fall mitdenken — die Anwendung läuft dann bewusst ohne Agenten.