fix(ui): use BorderOnly chrome; color diff +/- lines

Apply SystemDecorations=BorderOnly + ExtendClientAreaTitleBarHeightHint=-1
to WorktreesOverviewModalView and WorktreeModalView for reliable OS resize
borders. Replace SelectedFileDiff SelectableTextBlock with per-line
ItemsControl using WorktreeDiffLineKind coloring via DiffLineKindToBrushConverter.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-05-19 11:43:47 +02:00
parent 6670771040
commit 6c8048d0be
4 changed files with 75 additions and 16 deletions

View File

@@ -0,0 +1,24 @@
using System.Globalization;
using Avalonia.Data.Converters;
using Avalonia.Media;
using ClaudeDo.Ui.ViewModels.Modals;
namespace ClaudeDo.Ui.Converters;
public sealed class DiffLineKindToBrushConverter : IValueConverter
{
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
value is WorktreeDiffLineKind kind
? kind switch
{
WorktreeDiffLineKind.Added => new SolidColorBrush(Color.Parse("#66BB6A")),
WorktreeDiffLineKind.Removed => new SolidColorBrush(Color.Parse("#EF5350")),
WorktreeDiffLineKind.Hunk => new SolidColorBrush(Color.Parse("#42A5F5")),
WorktreeDiffLineKind.Header => new SolidColorBrush(Color.Parse("#9E9E9E")),
_ => new SolidColorBrush(Color.Parse("#CFD8DC")),
}
: new SolidColorBrush(Color.Parse("#CFD8DC"));
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotSupportedException();
}

View File

@@ -5,6 +5,14 @@ using ClaudeDo.Data.Git;
namespace ClaudeDo.Ui.ViewModels.Modals; namespace ClaudeDo.Ui.ViewModels.Modals;
public enum WorktreeDiffLineKind { Header, Hunk, Added, Removed, Context }
public sealed partial class WorktreeDiffLineViewModel : ViewModelBase
{
public required string Text { get; init; }
public required WorktreeDiffLineKind Kind { get; init; }
}
public sealed partial class WorktreeNodeViewModel : ViewModelBase public sealed partial class WorktreeNodeViewModel : ViewModelBase
{ {
public required string Name { get; init; } public required string Name { get; init; }
@@ -19,11 +27,11 @@ public sealed partial class WorktreeModalViewModel : ViewModelBase
private readonly GitService _git; private readonly GitService _git;
public ObservableCollection<WorktreeNodeViewModel> Root { get; } = new(); public ObservableCollection<WorktreeNodeViewModel> Root { get; } = new();
public ObservableCollection<WorktreeDiffLineViewModel> SelectedFileDiffLines { get; } = new();
[ObservableProperty] private string _worktreePath = ""; [ObservableProperty] private string _worktreePath = "";
[ObservableProperty] private string? _baseCommit; [ObservableProperty] private string? _baseCommit;
[ObservableProperty] private WorktreeNodeViewModel? _selectedNode; [ObservableProperty] private WorktreeNodeViewModel? _selectedNode;
[ObservableProperty] private string _selectedFileDiff = "";
// Set by the view (same pattern as DiffModalViewModel.CloseAction) // Set by the view (same pattern as DiffModalViewModel.CloseAction)
public Action? CloseAction { get; set; } public Action? CloseAction { get; set; }
@@ -40,19 +48,33 @@ public sealed partial class WorktreeModalViewModel : ViewModelBase
private async Task LoadFileDiffAsync(WorktreeNodeViewModel? node) private async Task LoadFileDiffAsync(WorktreeNodeViewModel? node)
{ {
if (node is null || node.IsDirectory || string.IsNullOrEmpty(node.RelativePath)) SelectedFileDiffLines.Clear();
{
SelectedFileDiff = "";
return;
}
if (node is null || node.IsDirectory || string.IsNullOrEmpty(node.RelativePath))
return;
string diff;
try try
{ {
SelectedFileDiff = await _git.GetFileDiffAsync(WorktreePath, BaseCommit, node.RelativePath); diff = await _git.GetFileDiffAsync(WorktreePath, BaseCommit, node.RelativePath);
} }
catch catch
{ {
SelectedFileDiff = ""; return;
}
foreach (var line in diff.Split('\n'))
{
var kind = line switch
{
_ when line.StartsWith("+++") || line.StartsWith("---") => WorktreeDiffLineKind.Header,
_ when line.StartsWith("@@") => WorktreeDiffLineKind.Hunk,
_ when line.StartsWith('+') => WorktreeDiffLineKind.Added,
_ when line.StartsWith('-') => WorktreeDiffLineKind.Removed,
_ when line.StartsWith("diff ") || line.StartsWith("index ") || line.StartsWith("\\ ") => WorktreeDiffLineKind.Header,
_ => WorktreeDiffLineKind.Context,
};
SelectedFileDiffLines.Add(new WorktreeDiffLineViewModel { Text = line, Kind = kind });
} }
} }

View File

@@ -1,18 +1,24 @@
<Window xmlns="https://github.com/avaloniaui" <Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:ClaudeDo.Ui.ViewModels.Modals" xmlns:vm="using:ClaudeDo.Ui.ViewModels.Modals"
xmlns:converters="using:ClaudeDo.Ui.Converters"
x:Class="ClaudeDo.Ui.Views.Modals.WorktreeModalView" x:Class="ClaudeDo.Ui.Views.Modals.WorktreeModalView"
x:DataType="vm:WorktreeModalViewModel" x:DataType="vm:WorktreeModalViewModel"
Title="Worktree" Title="Worktree"
Width="1100" Height="720" Width="1100" Height="720"
MinWidth="640" MinHeight="400" MinWidth="640" MinHeight="400"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
SystemDecorations="None" SystemDecorations="BorderOnly"
ExtendClientAreaToDecorationsHint="True" ExtendClientAreaToDecorationsHint="True"
ExtendClientAreaTitleBarHeightHint="-1"
Background="Transparent" Background="Transparent"
CanResize="True" CanResize="True"
TransparencyLevelHint="AcrylicBlur"> TransparencyLevelHint="AcrylicBlur">
<Window.Resources>
<converters:DiffLineKindToBrushConverter x:Key="DiffLineKindToBrush"/>
</Window.Resources>
<Window.KeyBindings> <Window.KeyBindings>
<KeyBinding Gesture="Escape" Command="{Binding CloseCommand}"/> <KeyBinding Gesture="Escape" Command="{Binding CloseCommand}"/>
</Window.KeyBindings> </Window.KeyBindings>
@@ -77,11 +83,17 @@
HorizontalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
Margin="4,0,8,8"> Margin="4,0,8,8">
<SelectableTextBlock Text="{Binding SelectedFileDiff}" <ItemsControl ItemsSource="{Binding SelectedFileDiffLines}">
FontFamily="{DynamicResource MonoFont}" <ItemsControl.ItemTemplate>
FontSize="11" <DataTemplate DataType="vm:WorktreeDiffLineViewModel">
Foreground="{DynamicResource TextBrush}" <SelectableTextBlock Text="{Binding Text}"
TextWrapping="NoWrap"/> FontFamily="{DynamicResource MonoFont}"
FontSize="11"
Foreground="{Binding Kind, Converter={StaticResource DiffLineKindToBrush}}"
TextWrapping="NoWrap"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>

View File

@@ -9,8 +9,9 @@
CanResize="True" CanResize="True"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
Background="{DynamicResource SurfaceBrush}" Background="{DynamicResource SurfaceBrush}"
SystemDecorations="None" SystemDecorations="BorderOnly"
ExtendClientAreaToDecorationsHint="True"> ExtendClientAreaToDecorationsHint="True"
ExtendClientAreaTitleBarHeightHint="-1">
<Window.Resources> <Window.Resources>
<converters:WorktreeStateColorConverter x:Key="WorktreeStateColor"/> <converters:WorktreeStateColorConverter x:Key="WorktreeStateColor"/>