feat(ui): Cheat-Sheet-Fenster im Hilfe-Menue

Sechs Markdown-Sheets (git, Conventional Commits, Claude CLI, Claude-Config,
dotnet CLI, PowerShell) als EmbeddedResource, gerendert im modelosen, per
Topmost anpinnbaren CheatSheetsWindow. MarkdownView bekommt dafuer das Opt-in
CommandRows: `befehl` - beschreibung wird zu Mono-Chip + Copy-Button, jede
##-Sektion landet in einem Expander. Enthaelt ausserdem: Mission Control
wird beim Oeffnen aus dem Minimiert-Zustand geholt, nicht nur aktiviert.
This commit is contained in:
mika kuns
2026-09-07 08:17:37 +02:00
parent 7c7c33d677
commit 0db9fadee9
20 changed files with 749 additions and 7 deletions
@@ -0,0 +1,34 @@
using System.Linq;
using ClaudeDo.Ui.ViewModels.Modals;
using Xunit;
namespace ClaudeDo.Ui.Tests.ViewModels;
public class CheatSheetsViewModelTests
{
[Fact]
public void LoadsEverySheetWithTitleFromHeading()
{
var vm = new CheatSheetsViewModel();
Assert.Equal(6, vm.Sheets.Count);
Assert.Equal(
["Git", "Conventional Commits", "Claude CLI", "Claude Konfiguration", "dotnet CLI", "PowerShell"],
vm.Sheets.Select(s => s.Title));
// The heading is stripped so the window doesn't render the title twice.
Assert.All(vm.Sheets, s => Assert.StartsWith("## ", s.Body));
}
[Fact]
public void SelectSwitchesSheetAndIgnoresUnknownKeys()
{
var vm = new CheatSheetsViewModel();
Assert.Equal("git", vm.SelectedSheet!.Key);
vm.Select("dotnet-cli");
Assert.Equal("dotnet-cli", vm.SelectedSheet!.Key);
vm.Select("nope");
Assert.Equal("dotnet-cli", vm.SelectedSheet!.Key);
}
}
@@ -65,6 +65,7 @@ public class ListsIslandCreateListCancelTests : IDisposable
public Task<bool> ConfirmAsync(string message) => Task.FromResult(true);
public Task ShowErrorAsync(string message) => Task.CompletedTask;
public void ShowMissionControl(MissionControlViewModel vm) => throw new NotImplementedException();
public void ShowCheatSheets(CheatSheetsViewModel vm) => throw new NotImplementedException();
}
private sealed class ListSettingsProvider(Func<ListSettingsModalViewModel> create) : IServiceProvider
@@ -63,6 +63,7 @@ public class TasksIslandMoveToListTests : IDisposable
public Task ShowLogVisualizerAsync(LogVisualizerViewModel vm) => throw new NotImplementedException();
public Task ShowErrorAsync(string message) => Task.CompletedTask;
public void ShowMissionControl(MissionControlViewModel vm) => throw new NotImplementedException();
public void ShowCheatSheets(CheatSheetsViewModel vm) => throw new NotImplementedException();
}
private TasksIslandViewModel BuildViewModel() => new(new TestDbFactory(NewContext), worker: null);
@@ -0,0 +1,32 @@
using ClaudeDo.Ui.Views.Controls;
using Xunit;
namespace ClaudeDo.Ui.Tests.Views;
/// <summary>
/// Covers the command-row split only — rendering itself needs an Avalonia app, which this
/// project has no host for.
/// </summary>
public class MarkdownCommandRowTests
{
[Theory]
[InlineData("`git status -sb` — Kurzstatus mit Branch-Zeile", "git status -sb", "Kurzstatus mit Branch-Zeile")]
[InlineData("`git branch -d <name>` — löschen (`-D` erzwingt)", "git branch -d <name>", "löschen (`-D` erzwingt)")]
[InlineData("`dotnet build`", "dotnet build", "")]
public void SplitsCommandFromDescription(string line, string cmd, string desc)
{
Assert.True(MarkdownView.TrySplitCommand(line, out var actualCmd, out var actualDesc));
Assert.Equal(cmd, actualCmd);
Assert.Equal(desc, actualDesc);
}
[Theory]
[InlineData("Prosa ohne Befehl")]
[InlineData("Text mit `code` in der Mitte")]
[InlineData("`unclosed backtick")]
[InlineData("``")]
public void LeavesNonCommandBulletsToThePlainRenderer(string line)
{
Assert.False(MarkdownView.TrySplitCommand(line, out _, out _));
}
}