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:
24
src/ClaudeDo.Ui/Converters/DiffLineKindToBrushConverter.cs
Normal file
24
src/ClaudeDo.Ui/Converters/DiffLineKindToBrushConverter.cs
Normal 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();
|
||||||
|
}
|
||||||
@@ -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 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate DataType="vm:WorktreeDiffLineViewModel">
|
||||||
|
<SelectableTextBlock Text="{Binding Text}"
|
||||||
FontFamily="{DynamicResource MonoFont}"
|
FontFamily="{DynamicResource MonoFont}"
|
||||||
FontSize="11"
|
FontSize="11"
|
||||||
Foreground="{DynamicResource TextBrush}"
|
Foreground="{Binding Kind, Converter={StaticResource DiffLineKindToBrush}}"
|
||||||
TextWrapping="NoWrap"/>
|
TextWrapping="NoWrap"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -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"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user