fix(ui): make worktree state chips readable with on-theme tints

The state badge in the worktrees overview used bright off-palette Material colors
with hardcoded near-black text (via WorktreeStateColorConverter), which was hard
to read. Switch to the existing chip pattern (subtle tint background + matching
border + colored text): active=blue, merged=green, kept=amber, discarded=gray.
Drop the now-unused WorktreeStateColorConverter.
This commit is contained in:
Mika Kuns
2026-06-24 13:19:37 +02:00
parent ea16da2756
commit df84fc3f2c
5 changed files with 54 additions and 38 deletions

View File

@@ -54,7 +54,7 @@ Design/ — Tokens.axaml (design tokens; merged before styles) + IslandStyle
## Converters ## Converters
`StatusColorConverter` (+ `ConnectionColorConverter` in the same file), `WorktreeStateColorConverter`, `WorkerLogLevelToBrushConverter`, `DotBrushConverter`, `EqStatusConverter`, `IconKeyConverter`, `CheckboxBorderConverter`, `StrikeIfTrueConverter`, `BoolToItalicConverter`, `BoolToDraftOpacityConverter`, `NotNullToBoolConverter`, `UpperCaseConverter`, `DateOnlyToDateTimeConverter`. `StatusColorConverter` (+ `ConnectionColorConverter` in the same file), `WorkerLogLevelToBrushConverter`, `DotBrushConverter`, `EqStatusConverter`, `IconKeyConverter`, `CheckboxBorderConverter`, `StrikeIfTrueConverter`, `BoolToItalicConverter`, `BoolToDraftOpacityConverter`, `NotNullToBoolConverter`, `UpperCaseConverter`, `DateOnlyToDateTimeConverter`.
## Dialog Pattern ## Dialog Pattern

View File

@@ -1,30 +0,0 @@
using System.Globalization;
using Avalonia.Data.Converters;
using Avalonia.Media;
using ClaudeDo.Data.Models;
namespace ClaudeDo.Ui.Converters;
public sealed class WorktreeStateColorConverter : IValueConverter
{
private static readonly ISolidColorBrush Active = new SolidColorBrush(Color.Parse("#42A5F5"));
private static readonly ISolidColorBrush Merged = new SolidColorBrush(Color.Parse("#66BB6A"));
private static readonly ISolidColorBrush Discarded = new SolidColorBrush(Color.Parse("#9E9E9E"));
private static readonly ISolidColorBrush Kept = new SolidColorBrush(Color.Parse("#FFA726"));
private static readonly ISolidColorBrush Default = new SolidColorBrush(Colors.Gray);
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
value is WorktreeState state
? state switch
{
WorktreeState.Active => Active,
WorktreeState.Merged => Merged,
WorktreeState.Discarded => Discarded,
WorktreeState.Kept => Kept,
_ => Default,
}
: Default;
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotSupportedException();
}

View File

@@ -238,6 +238,43 @@
<Setter Property="Foreground" Value="#8FB9D6" /> <Setter Property="Foreground" Value="#8FB9D6" />
</Style> </Style>
<!-- Worktree-state chips (worktrees overview) -->
<!-- active → slate-blue (same hue as parked: a live worktree) -->
<Style Selector="Border.chip.wt-active">
<Setter Property="Background" Value="#22303A" />
<Setter Property="BorderBrush" Value="#3A5060" />
</Style>
<Style Selector="Border.chip.wt-active > TextBlock">
<Setter Property="Foreground" Value="#8FB9D6" />
</Style>
<!-- merged → green -->
<Style Selector="Border.chip.wt-merged">
<Setter Property="Background" Value="{StaticResource DoneTintBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource DoneTintBorderBrush}" />
</Style>
<Style Selector="Border.chip.wt-merged > TextBlock">
<Setter Property="Foreground" Value="{StaticResource StatusDoneBrush}" />
</Style>
<!-- kept → amber -->
<Style Selector="Border.chip.wt-kept">
<Setter Property="Background" Value="{StaticResource ReviewTintBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource ReviewTintBorderBrush}" />
</Style>
<Style Selector="Border.chip.wt-kept > TextBlock">
<Setter Property="Foreground" Value="{StaticResource StatusReviewBrush}" />
</Style>
<!-- discarded → muted gray (same as idle) -->
<Style Selector="Border.chip.wt-discarded">
<Setter Property="Background" Value="{StaticResource Surface2Brush}" />
<Setter Property="BorderBrush" Value="{StaticResource LineBrush}" />
</Style>
<Style Selector="Border.chip.wt-discarded > TextBlock">
<Setter Property="Foreground" Value="{StaticResource TextMuteBrush}" />
</Style>
<!-- ============================================================ --> <!-- ============================================================ -->
<!-- BUTTONS --> <!-- BUTTONS -->
<!-- ============================================================ --> <!-- ============================================================ -->

View File

@@ -24,7 +24,12 @@ public sealed partial class WorktreeOverviewRowViewModel : ViewModelBase
[ObservableProperty] private string _path = ""; [ObservableProperty] private string _path = "";
[ObservableProperty] private string _branchName = ""; [ObservableProperty] private string _branchName = "";
[ObservableProperty] private string _baseCommit = ""; [ObservableProperty] private string _baseCommit = "";
[ObservableProperty][NotifyPropertyChangedFor(nameof(IsActive))] private WorktreeState _state; [ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsActive))]
[NotifyPropertyChangedFor(nameof(IsMerged))]
[NotifyPropertyChangedFor(nameof(IsDiscarded))]
[NotifyPropertyChangedFor(nameof(IsKept))]
private WorktreeState _state;
[ObservableProperty] private string? _diffStat; [ObservableProperty] private string? _diffStat;
[ObservableProperty][NotifyPropertyChangedFor(nameof(AgeText))] private DateTime _createdAt; [ObservableProperty][NotifyPropertyChangedFor(nameof(AgeText))] private DateTime _createdAt;
[ObservableProperty] private bool _pathExistsOnDisk; [ObservableProperty] private bool _pathExistsOnDisk;
@@ -40,6 +45,9 @@ public sealed partial class WorktreeOverviewRowViewModel : ViewModelBase
public string AgeText => FormatAge(DateTime.UtcNow - CreatedAt); public string AgeText => FormatAge(DateTime.UtcNow - CreatedAt);
public bool IsActive => State == WorktreeState.Active; public bool IsActive => State == WorktreeState.Active;
public bool IsMerged => State == WorktreeState.Merged;
public bool IsDiscarded => State == WorktreeState.Discarded;
public bool IsKept => State == WorktreeState.Kept;
public bool IsRunning => TaskStatus == TaskStatus.Running; public bool IsRunning => TaskStatus == TaskStatus.Running;
private static string FormatAge(TimeSpan ts) private static string FormatAge(TimeSpan ts)

View File

@@ -1,7 +1,6 @@
<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"
xmlns:ctl="using:ClaudeDo.Ui.Views.Controls" xmlns:ctl="using:ClaudeDo.Ui.Views.Controls"
xmlns:loc="using:ClaudeDo.Ui.Localization" xmlns:loc="using:ClaudeDo.Ui.Localization"
x:Class="ClaudeDo.Ui.Views.Modals.WorktreesOverviewModalView" x:Class="ClaudeDo.Ui.Views.Modals.WorktreesOverviewModalView"
@@ -16,7 +15,6 @@
ExtendClientAreaTitleBarHeightHint="-1"> ExtendClientAreaTitleBarHeightHint="-1">
<Window.Resources> <Window.Resources>
<converters:WorktreeStateColorConverter x:Key="WorktreeStateColor"/>
<DataTemplate x:Key="WorktreeRowTemplate" x:DataType="vm:WorktreeOverviewRowViewModel"> <DataTemplate x:Key="WorktreeRowTemplate" x:DataType="vm:WorktreeOverviewRowViewModel">
<Border Classes="task-row" <Border Classes="task-row"
Classes.selected="{Binding IsSelected}" Classes.selected="{Binding IsSelected}"
@@ -79,10 +77,13 @@
<TextBlock Grid.Column="2" Classes="meta" VerticalAlignment="Center" <TextBlock Grid.Column="2" Classes="meta" VerticalAlignment="Center"
Text="{Binding MergeOutcome}" Text="{Binding MergeOutcome}"
IsVisible="{Binding HasOutcome}"/> IsVisible="{Binding HasOutcome}"/>
<Border Grid.Column="3" CornerRadius="3" Padding="6,2" VerticalAlignment="Center" <Border Grid.Column="3" Classes="chip"
Background="{Binding State, Converter={StaticResource WorktreeStateColor}}"> Classes.wt-active="{Binding IsActive}"
<TextBlock Classes="meta" Text="{Binding State}" Foreground="{DynamicResource DeepBrush}" Classes.wt-merged="{Binding IsMerged}"
HorizontalAlignment="Center"/> Classes.wt-discarded="{Binding IsDiscarded}"
Classes.wt-kept="{Binding IsKept}"
HorizontalAlignment="Left" VerticalAlignment="Center">
<TextBlock Text="{Binding State}"/>
</Border> </Border>
<TextBlock Grid.Column="4" Classes="meta" Text="{Binding DiffStat}" VerticalAlignment="Center"/> <TextBlock Grid.Column="4" Classes="meta" Text="{Binding DiffStat}" VerticalAlignment="Center"/>
<TextBlock Grid.Column="5" Classes="meta" Text="{Binding AgeText}" VerticalAlignment="Center"/> <TextBlock Grid.Column="5" Classes="meta" Text="{Binding AgeText}" VerticalAlignment="Center"/>