feat(ui): worktree modal with tree view and M/A badges

Adds WorktreeModalView/ViewModel showing git status --porcelain as a
recursive file tree with M/A/D/? status badges. Wires the Worktree
button in AgentStripView to OpenWorktreeCommand on DetailsIslandViewModel.
Adds GetStatusPorcelainAsync to GitService.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-04-20 10:31:12 +02:00
parent 4d68543cf2
commit abd7733c90
6 changed files with 209 additions and 1 deletions

View File

@@ -30,7 +30,6 @@ public partial class AgentStripView : UserControl
var owner = TopLevel.GetTopLevel(this) as Window;
if (owner == null) return;
var modal = new WorktreeModalView { DataContext = worktreeVm };
worktreeVm.CloseCommand.Subscribe(_ => modal.Close());
await modal.ShowDialog(owner);
};
}

View File

@@ -0,0 +1,66 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:ClaudeDo.Ui.ViewModels.Modals"
x:Class="ClaudeDo.Ui.Views.Modals.WorktreeModalView"
x:DataType="vm:WorktreeModalViewModel"
Title="Worktree"
Width="640" Height="720"
WindowStartupLocation="CenterOwner"
SystemDecorations="None"
ExtendClientAreaToDecorationsHint="True"
Background="Transparent"
CanResize="False"
TransparencyLevelHint="AcrylicBlur">
<Window.KeyBindings>
<KeyBinding Gesture="Escape" Command="{Binding CloseCommand}"/>
</Window.KeyBindings>
<Border Classes="island" Margin="12">
<DockPanel>
<!-- Title strip -->
<Border DockPanel.Dock="Top" Height="36"
PointerPressed="OnTitleBarPressed">
<Grid ColumnDefinitions="*,Auto" Margin="14,0">
<TextBlock Grid.Column="0" Text="Worktree" VerticalAlignment="Center"
FontFamily="{DynamicResource MonoFont}" FontSize="12"
Foreground="{DynamicResource TextMuteBrush}"/>
<Button Grid.Column="1" Classes="icon-btn" Content="✕"
Command="{Binding CloseCommand}" VerticalAlignment="Center"/>
</Grid>
</Border>
<!-- Path strip -->
<Border DockPanel.Dock="Top" Padding="14,0,14,8">
<TextBlock Text="{Binding WorktreePath}"
FontFamily="{DynamicResource MonoFont}" FontSize="11"
Foreground="{DynamicResource TextFaintBrush}"
TextTrimming="CharacterEllipsis"/>
</Border>
<!-- File tree -->
<TreeView DockPanel.Dock="Top" ItemsSource="{Binding Root}"
Background="Transparent" Margin="8,0,8,8">
<TreeView.ItemTemplate>
<TreeDataTemplate DataType="vm:WorktreeNodeViewModel"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="{Binding Name}"
FontFamily="{DynamicResource MonoFont}" FontSize="12"
Foreground="{DynamicResource TextBrush}"/>
<Border Tag="{Binding Status}" CornerRadius="3" Padding="4,0"
VerticalAlignment="Center"
IsVisible="{Binding Status, Converter={x:Static ObjectConverters.IsNotNull}}">
<TextBlock Text="{Binding Status}"
FontFamily="{DynamicResource MonoFont}" FontSize="10"
Foreground="{DynamicResource TextBrush}"/>
</Border>
</StackPanel>
</TreeDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</DockPanel>
</Border>
</Window>

View File

@@ -0,0 +1,26 @@
using Avalonia.Controls;
using Avalonia.Input;
using ClaudeDo.Ui.ViewModels.Modals;
namespace ClaudeDo.Ui.Views.Modals;
public partial class WorktreeModalView : Window
{
public WorktreeModalView()
{
InitializeComponent();
}
protected override void OnDataContextChanged(EventArgs e)
{
base.OnDataContextChanged(e);
if (DataContext is WorktreeModalViewModel vm)
vm.CloseAction = Close;
}
private void OnTitleBarPressed(object? sender, PointerPressedEventArgs e)
{
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
BeginMoveDrag(e);
}
}