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;
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 required string Name { get; init; }
@@ -19,11 +27,11 @@ public sealed partial class WorktreeModalViewModel : ViewModelBase
private readonly GitService _git;
public ObservableCollection<WorktreeNodeViewModel> Root { get; } = new();
public ObservableCollection<WorktreeDiffLineViewModel> SelectedFileDiffLines { get; } = new();
[ObservableProperty] private string _worktreePath = "";
[ObservableProperty] private string? _baseCommit;
[ObservableProperty] private WorktreeNodeViewModel? _selectedNode;
[ObservableProperty] private string _selectedFileDiff = "";
// Set by the view (same pattern as DiffModalViewModel.CloseAction)
public Action? CloseAction { get; set; }
@@ -40,19 +48,33 @@ public sealed partial class WorktreeModalViewModel : ViewModelBase
private async Task LoadFileDiffAsync(WorktreeNodeViewModel? node)
{
if (node is null || node.IsDirectory || string.IsNullOrEmpty(node.RelativePath))
{
SelectedFileDiff = "";
return;
}
SelectedFileDiffLines.Clear();
if (node is null || node.IsDirectory || string.IsNullOrEmpty(node.RelativePath))
return;
string diff;
try
{
SelectedFileDiff = await _git.GetFileDiffAsync(WorktreePath, BaseCommit, node.RelativePath);
diff = await _git.GetFileDiffAsync(WorktreePath, BaseCommit, node.RelativePath);
}
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"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:ClaudeDo.Ui.ViewModels.Modals"
xmlns:converters="using:ClaudeDo.Ui.Converters"
x:Class="ClaudeDo.Ui.Views.Modals.WorktreeModalView"
x:DataType="vm:WorktreeModalViewModel"
Title="Worktree"
Width="1100" Height="720"
MinWidth="640" MinHeight="400"
WindowStartupLocation="CenterOwner"
SystemDecorations="None"
SystemDecorations="BorderOnly"
ExtendClientAreaToDecorationsHint="True"
ExtendClientAreaTitleBarHeightHint="-1"
Background="Transparent"
CanResize="True"
TransparencyLevelHint="AcrylicBlur">
<Window.Resources>
<converters:DiffLineKindToBrushConverter x:Key="DiffLineKindToBrush"/>
</Window.Resources>
<Window.KeyBindings>
<KeyBinding Gesture="Escape" Command="{Binding CloseCommand}"/>
</Window.KeyBindings>
@@ -77,11 +83,17 @@
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
Margin="4,0,8,8">
<SelectableTextBlock Text="{Binding SelectedFileDiff}"
<ItemsControl ItemsSource="{Binding SelectedFileDiffLines}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:WorktreeDiffLineViewModel">
<SelectableTextBlock Text="{Binding Text}"
FontFamily="{DynamicResource MonoFont}"
FontSize="11"
Foreground="{DynamicResource TextBrush}"
Foreground="{Binding Kind, Converter={StaticResource DiffLineKindToBrush}}"
TextWrapping="NoWrap"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>

View File

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