Grow the detail-pane Git tab into the review+merge cockpit: target, pre-flight mergeability, inspect actions, then the four review verbs (Approve & Merge / Send back / Park / Cancel) plus a demoted Reset (discard branch). The decision block is gated independently of the merge controls so sandbox (no-worktree) review tasks still get the buttons. - Add ParkReviewCommand (-> RejectReviewToIdleAsync) - Send back (reject-to-queue) disabled until feedback is entered - Remove the mislabeled [Continue]/[Reset] line from the Output tab - Accent dot on the Git tab while awaiting review
379 lines
19 KiB
XML
379 lines
19 KiB
XML
<UserControl xmlns="https://github.com/avaloniaui"
|
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
xmlns:vm="using:ClaudeDo.Ui.ViewModels.Islands"
|
|
xmlns:loc="using:ClaudeDo.Ui.Localization"
|
|
x:DataType="vm:DetailsIslandViewModel"
|
|
x:Class="ClaudeDo.Ui.Views.Islands.Detail.WorkConsole">
|
|
|
|
<UserControl.Styles>
|
|
<Style Selector="Button.tab-btn">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="BorderThickness" Value="0" />
|
|
<Setter Property="Padding" Value="12,8" />
|
|
<Setter Property="FontFamily" Value="{StaticResource MonoFont}" />
|
|
<Setter Property="FontSize" Value="{StaticResource FontSizeMono}" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextMuteBrush}" />
|
|
<Setter Property="CornerRadius" Value="0" />
|
|
</Style>
|
|
<Style Selector="Button.tab-btn:pointerover /template/ ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource Surface2Brush}" />
|
|
<Setter Property="TextElement.Foreground" Value="{StaticResource TextDimBrush}" />
|
|
</Style>
|
|
<Style Selector="Button.tab-btn.active /template/ ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource Surface3Brush}" />
|
|
<Setter Property="TextElement.Foreground" Value="{StaticResource AccentBrush}" />
|
|
</Style>
|
|
|
|
<!-- Traffic-light dot button: no chrome, just the ellipse -->
|
|
<Style Selector="Button.dot-btn">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="BorderThickness" Value="0" />
|
|
<Setter Property="Padding" Value="0" />
|
|
<Setter Property="CornerRadius" Value="0" />
|
|
<Setter Property="Cursor" Value="Hand" />
|
|
</Style>
|
|
<Style Selector="Button.dot-btn /template/ ContentPresenter">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
</Style>
|
|
|
|
<!-- Terminal prompt action: bracketed text, no button chrome -->
|
|
<Style Selector="Button.prompt-action">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="BorderThickness" Value="0" />
|
|
<Setter Property="Padding" Value="2,0" />
|
|
<Setter Property="CornerRadius" Value="0" />
|
|
<Setter Property="Cursor" Value="Hand" />
|
|
<Setter Property="FontFamily" Value="{StaticResource MonoFont}" />
|
|
<Setter Property="FontSize" Value="{StaticResource FontSizeMono}" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextMuteBrush}" />
|
|
</Style>
|
|
<Style Selector="Button.prompt-action /template/ ContentPresenter">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="TextElement.Foreground" Value="{StaticResource TextMuteBrush}" />
|
|
</Style>
|
|
<Style Selector="Button.prompt-action:pointerover /template/ ContentPresenter">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="TextElement.Foreground" Value="{StaticResource TextBrush}" />
|
|
</Style>
|
|
<Style Selector="Button.prompt-action.accent /template/ ContentPresenter">
|
|
<Setter Property="TextElement.Foreground" Value="{StaticResource AccentBrush}" />
|
|
</Style>
|
|
<Style Selector="Button.prompt-action.accent:pointerover /template/ ContentPresenter">
|
|
<Setter Property="TextElement.Foreground" Value="{StaticResource MossBrightBrush}" />
|
|
</Style>
|
|
</UserControl.Styles>
|
|
|
|
<!-- Outer terminal card — Padding="0" so header/strip span edge-to-edge;
|
|
ClipToBounds keeps tab content inside the rounded corners (no bottom clip). -->
|
|
<Border Classes="terminal" Padding="0" ClipToBounds="True">
|
|
<DockPanel LastChildFill="True">
|
|
|
|
<!-- ── Title bar ── -->
|
|
<Grid DockPanel.Dock="Top" ColumnDefinitions="Auto,*,Auto"
|
|
Background="{DynamicResource Surface2Brush}" Height="28">
|
|
|
|
<!-- Traffic-light dots (decorative) -->
|
|
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="6"
|
|
Margin="12,0" VerticalAlignment="Center">
|
|
<Ellipse Classes="dot-red" />
|
|
<Ellipse Classes="dot-yellow" />
|
|
<Ellipse Classes="dot-green" />
|
|
</StackPanel>
|
|
|
|
<!-- Right cluster: info header (model · turns · diff) + status chip -->
|
|
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="12"
|
|
Margin="0,0,8,0" VerticalAlignment="Center">
|
|
|
|
<StackPanel Orientation="Horizontal" Spacing="6" VerticalAlignment="Center">
|
|
<TextBlock Classes="meta" Text="{Binding Model}"
|
|
Foreground="{DynamicResource TextMuteBrush}" />
|
|
<TextBlock Classes="meta" Text="·"
|
|
Foreground="{DynamicResource TextFaintBrush}" />
|
|
<TextBlock Classes="meta" Text="{Binding TurnsText}"
|
|
Foreground="{DynamicResource TextMuteBrush}" />
|
|
<TextBlock Classes="meta" Text="·"
|
|
Foreground="{DynamicResource TextFaintBrush}" />
|
|
<TextBlock Classes="diff-add" Text="{Binding DiffAddText}" />
|
|
<TextBlock Classes="diff-del" Text="{Binding DiffDelText}" />
|
|
</StackPanel>
|
|
|
|
<Panel VerticalAlignment="Center">
|
|
<Border Classes="live-chip pulsing"
|
|
IsVisible="{Binding IsRunning}">
|
|
<StackPanel Orientation="Horizontal" Spacing="5" VerticalAlignment="Center">
|
|
<Ellipse VerticalAlignment="Center" />
|
|
<TextBlock Text="{loc:Tr session.chipLive}" VerticalAlignment="Center" />
|
|
</StackPanel>
|
|
</Border>
|
|
<Border Classes="live-chip done"
|
|
IsVisible="{Binding IsDone}">
|
|
<StackPanel Orientation="Horizontal" Spacing="5" VerticalAlignment="Center">
|
|
<Ellipse VerticalAlignment="Center" Fill="{DynamicResource MossBrush}" />
|
|
<TextBlock Text="{loc:Tr session.chipDone}" VerticalAlignment="Center"
|
|
Foreground="{DynamicResource MossBrush}" />
|
|
</StackPanel>
|
|
</Border>
|
|
<Border Classes="live-chip failed"
|
|
IsVisible="{Binding IsFailed}">
|
|
<StackPanel Orientation="Horizontal" Spacing="5" VerticalAlignment="Center">
|
|
<Ellipse VerticalAlignment="Center" Fill="{DynamicResource BloodBrush}" />
|
|
<TextBlock Text="{loc:Tr session.chipFailed}" VerticalAlignment="Center"
|
|
Foreground="{DynamicResource BloodBrush}" />
|
|
</StackPanel>
|
|
</Border>
|
|
</Panel>
|
|
|
|
</StackPanel>
|
|
|
|
</Grid>
|
|
|
|
<!-- ── Roadblock band ── -->
|
|
<Border DockPanel.Dock="Top"
|
|
IsVisible="{Binding ShowRoadblock}"
|
|
Background="{DynamicResource ErrorTintBrush}"
|
|
BorderBrush="{DynamicResource BloodBrush}"
|
|
BorderThickness="0,1"
|
|
Padding="14,8">
|
|
<StackPanel Spacing="6">
|
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
|
<PathIcon Data="{StaticResource Icon.Warning}"
|
|
Foreground="{DynamicResource BloodBrush}"
|
|
Width="14" Height="14" VerticalAlignment="Center" />
|
|
<TextBlock Classes="meta" Text="{Binding RoadblockMessage}"
|
|
Foreground="{DynamicResource BloodBrush}"
|
|
TextWrapping="Wrap" VerticalAlignment="Center" />
|
|
</StackPanel>
|
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
|
<Button Classes="btn accent" Content="Continue"
|
|
Command="{Binding ContinueCommand}"
|
|
IsVisible="{Binding ShowContinue}" />
|
|
<Button Classes="btn" Content="Reset & Retry"
|
|
Command="{Binding ResetAndRetryCommand}"
|
|
IsVisible="{Binding ShowResetAndRetry}" />
|
|
</StackPanel>
|
|
</StackPanel>
|
|
</Border>
|
|
|
|
<!-- ── Tab strip ── -->
|
|
<Border DockPanel.Dock="Top"
|
|
Background="{DynamicResource Surface2Brush}"
|
|
BorderBrush="{DynamicResource LineBrush}"
|
|
BorderThickness="0,0,0,1">
|
|
<StackPanel Orientation="Horizontal">
|
|
<Button Classes="tab-btn"
|
|
Classes.active="{Binding IsOutputTab}"
|
|
Content="Output"
|
|
Command="{Binding SelectTabCommand}"
|
|
CommandParameter="output" />
|
|
<Button Classes="tab-btn"
|
|
Classes.active="{Binding IsGitTab}"
|
|
Command="{Binding SelectTabCommand}"
|
|
CommandParameter="git">
|
|
<StackPanel Orientation="Horizontal" Spacing="6">
|
|
<TextBlock Text="Git" VerticalAlignment="Center" />
|
|
<!-- Review-pending dot: where to act when a task awaits review -->
|
|
<Ellipse Width="6" Height="6" VerticalAlignment="Center"
|
|
Fill="{DynamicResource AccentBrush}"
|
|
IsVisible="{Binding IsWaitingForReview}" />
|
|
</StackPanel>
|
|
</Button>
|
|
<Button Classes="tab-btn"
|
|
Classes.active="{Binding IsSessionTab}"
|
|
Content="Session"
|
|
IsVisible="{Binding HasChildOutcomes}"
|
|
Command="{Binding SelectTabCommand}"
|
|
CommandParameter="session" />
|
|
</StackPanel>
|
|
</Border>
|
|
|
|
<!-- ── Tab body (bottom inset keeps content clear of the rounded corner) ── -->
|
|
<Grid Margin="0,0,0,8">
|
|
|
|
<!-- Output: log + review footer, both gated on IsOutputTab -->
|
|
<DockPanel IsVisible="{Binding IsOutputTab}" LastChildFill="True">
|
|
|
|
<!-- Session outcome: the run's result summary, incl. any roadblocks
|
|
reported (or the error for a hard failure). -->
|
|
<Border DockPanel.Dock="Top"
|
|
Margin="12,8,12,4" Padding="10,8"
|
|
IsVisible="{Binding ShowSessionOutcome}"
|
|
Background="{DynamicResource Surface2Brush}"
|
|
BorderBrush="{DynamicResource LineBrush}"
|
|
BorderThickness="1" CornerRadius="8">
|
|
<StackPanel Spacing="6">
|
|
<TextBlock Classes="section-label" Text="OUTCOME" />
|
|
<ScrollViewer MaxHeight="160" VerticalScrollBarVisibility="Auto">
|
|
<SelectableTextBlock Text="{Binding SessionOutcome}"
|
|
TextWrapping="Wrap"
|
|
Foreground="{DynamicResource TextDimBrush}"
|
|
FontFamily="{StaticResource MonoFont}"
|
|
FontSize="{StaticResource FontSizeMono}" />
|
|
</ScrollViewer>
|
|
</StackPanel>
|
|
</Border>
|
|
|
|
<ScrollViewer Name="LogScroll"
|
|
VerticalScrollBarVisibility="Visible"
|
|
AllowAutoHide="False"
|
|
Padding="12,8,12,4">
|
|
<ItemsControl ItemsSource="{Binding Log}">
|
|
<ItemsControl.ItemTemplate>
|
|
<DataTemplate DataType="vm:LogLineViewModel">
|
|
<Grid ColumnDefinitions="60,*" Margin="0,1">
|
|
<TextBlock Grid.Column="0"
|
|
Classes="log-ts"
|
|
Text="{Binding TimestampFormatted}" />
|
|
<SelectableTextBlock Grid.Column="1"
|
|
Text="{Binding Text}" Tag="{Binding ClassName}"
|
|
Foreground="{DynamicResource TextDimBrush}"
|
|
TextWrapping="Wrap" />
|
|
</Grid>
|
|
</DataTemplate>
|
|
</ItemsControl.ItemTemplate>
|
|
</ItemsControl>
|
|
</ScrollViewer>
|
|
|
|
</DockPanel>
|
|
|
|
<!-- Git: the review + merge cockpit -->
|
|
<ScrollViewer IsVisible="{Binding IsGitTab}" Padding="14,10">
|
|
<StackPanel Spacing="14">
|
|
|
|
<!-- Merge controls — shown whenever there's a worktree / unit to merge.
|
|
Header reads REVIEW while a decision is pending, otherwise MERGE. -->
|
|
<StackPanel Spacing="14" IsVisible="{Binding Merge.ShowMergeSection}">
|
|
<TextBlock Classes="section-label" Text="REVIEW"
|
|
IsVisible="{Binding IsWaitingForReview}" />
|
|
<TextBlock Classes="section-label" Text="MERGE"
|
|
IsVisible="{Binding !IsWaitingForReview}" />
|
|
|
|
<!-- Change summary (review only) -->
|
|
<StackPanel Orientation="Horizontal" Spacing="6"
|
|
IsVisible="{Binding IsWaitingForReview}">
|
|
<TextBlock Classes="diff-add" Text="{Binding DiffAddText}" />
|
|
<TextBlock Classes="diff-del" Text="{Binding DiffDelText}" />
|
|
</StackPanel>
|
|
|
|
<!-- Target branch + pre-flight mergeability -->
|
|
<StackPanel Spacing="4">
|
|
<TextBlock Classes="field-label" Text="Target branch" />
|
|
<ComboBox ItemsSource="{Binding Merge.MergeTargetBranches}"
|
|
SelectedItem="{Binding Merge.SelectedMergeTarget, Mode=TwoWay}"
|
|
HorizontalAlignment="Stretch" />
|
|
</StackPanel>
|
|
|
|
<StackPanel Spacing="0">
|
|
<TextBlock Classes="meta" Text="{Binding Merge.MergePreviewText}" TextWrapping="Wrap"
|
|
Foreground="{DynamicResource MossBrush}"
|
|
IsVisible="{Binding Merge.MergeIsClean}" />
|
|
<TextBlock Classes="meta" Text="{Binding Merge.MergePreviewText}" TextWrapping="Wrap"
|
|
Foreground="{DynamicResource BloodBrush}"
|
|
IsVisible="{Binding Merge.MergeIsConflict}" />
|
|
<TextBlock Classes="meta" Text="{Binding Merge.MergePreviewText}" TextWrapping="Wrap"
|
|
Foreground="{DynamicResource TextMuteBrush}"
|
|
IsVisible="{Binding Merge.ShowMergePreviewMuted}" />
|
|
</StackPanel>
|
|
|
|
<!-- Inspect: diff / worktree / combined diff -->
|
|
<WrapPanel Orientation="Horizontal">
|
|
<Button Classes="btn" Content="Open Diff" Margin="0,0,8,8"
|
|
Command="{Binding Merge.OpenDiffCommand}" />
|
|
<Button Classes="btn" Margin="0,0,8,8"
|
|
ToolTip.Tip="{loc:Tr agent.openWorktreeTip}"
|
|
Command="{Binding Merge.OpenWorktreeCommand}">
|
|
<StackPanel Orientation="Horizontal" Spacing="5">
|
|
<TextBlock Text="Worktree" />
|
|
<PathIcon Data="{StaticResource Icon.ArrowOut}" Width="11" Height="11" />
|
|
</StackPanel>
|
|
</Button>
|
|
<Button Classes="btn" Content="Review Combined Diff" Margin="0,0,8,8"
|
|
Command="{Binding Merge.ReviewCombinedDiffCommand}" />
|
|
</WrapPanel>
|
|
</StackPanel>
|
|
|
|
<!-- Review decision — feedback + the four review verbs. Always present while
|
|
awaiting review, even for sandbox runs with no worktree to merge. -->
|
|
<StackPanel Spacing="10" IsVisible="{Binding IsWaitingForReview}">
|
|
<Border Height="1" Background="{DynamicResource LineBrush}"
|
|
IsVisible="{Binding Merge.ShowMergeSection}" />
|
|
|
|
<TextBox Name="ReviewInput"
|
|
KeyDown="OnReviewInputKeyDown"
|
|
Text="{Binding ReviewFeedback, Mode=TwoWay}"
|
|
AcceptsReturn="True"
|
|
TextWrapping="Wrap"
|
|
MaxHeight="120"
|
|
PlaceholderText="Feedback for a re-run…"
|
|
FontFamily="{StaticResource MonoFont}"
|
|
FontSize="{StaticResource FontSizeMono}" />
|
|
|
|
<WrapPanel Orientation="Horizontal">
|
|
<Button Classes="btn accent" Content="Approve & Merge" Margin="0,0,8,8"
|
|
Command="{Binding ApproveReviewCommand}" />
|
|
<Button Classes="btn" Content="Send back" Margin="0,0,8,8"
|
|
ToolTip.Tip="{loc:Tr session.reviewContinueTip}"
|
|
Command="{Binding RejectReviewCommand}" />
|
|
<Button Classes="btn" Content="Park" Margin="0,0,8,8"
|
|
ToolTip.Tip="Set aside — back to Idle, keeps the worktree"
|
|
Command="{Binding ParkReviewCommand}" />
|
|
<Button Classes="btn" Content="Cancel" Margin="0,0,8,8"
|
|
Command="{Binding CancelReviewCommand}" />
|
|
</WrapPanel>
|
|
|
|
<Button Classes="prompt-action" Content="Reset (discard branch)…"
|
|
ToolTip.Tip="{loc:Tr session.reviewResetTip}"
|
|
Command="{Binding ResetReviewCommand}" />
|
|
</StackPanel>
|
|
</StackPanel>
|
|
</ScrollViewer>
|
|
|
|
<!-- Session: subtask outcomes (review lives in Output, merge in Git) -->
|
|
<ScrollViewer IsVisible="{Binding IsSessionTab}" Padding="14,10">
|
|
<StackPanel Spacing="14">
|
|
|
|
<!-- Attention band: a child failed, was cancelled, still needs its own
|
|
review, or reported roadblocks. The parent stays waiting until resolved. -->
|
|
<Border IsVisible="{Binding HasChildrenNeedingAttention}"
|
|
Background="{DynamicResource ErrorTintBrush}"
|
|
BorderBrush="{DynamicResource BloodBrush}"
|
|
BorderThickness="1" CornerRadius="8" Padding="10,8">
|
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
|
<PathIcon Data="{StaticResource Icon.Warning}"
|
|
Foreground="{DynamicResource BloodBrush}"
|
|
Width="14" Height="14" VerticalAlignment="Center" />
|
|
<TextBlock Classes="meta" Text="{Binding ChildrenAttentionText}"
|
|
Foreground="{DynamicResource BloodBrush}"
|
|
VerticalAlignment="Center" />
|
|
</StackPanel>
|
|
</Border>
|
|
|
|
<!-- Child outcomes -->
|
|
<StackPanel Spacing="6" IsVisible="{Binding HasChildOutcomes}">
|
|
<TextBlock Classes="section-label" Text="OUTCOMES" />
|
|
<ItemsControl ItemsSource="{Binding ChildOutcomes}">
|
|
<ItemsControl.ItemTemplate>
|
|
<DataTemplate x:DataType="vm:ChildOutcomeRowViewModel">
|
|
<Grid ColumnDefinitions="*,Auto,Auto" Margin="0,2">
|
|
<TextBlock Grid.Column="0" Text="{Binding Title}"
|
|
TextTrimming="CharacterEllipsis"
|
|
VerticalAlignment="Center" />
|
|
<TextBlock Grid.Column="1" Text="{Binding RoadblockText}"
|
|
IsVisible="{Binding HasRoadblock}"
|
|
Foreground="#E0A030"
|
|
Margin="8,0" VerticalAlignment="Center" />
|
|
<TextBlock Grid.Column="2" Text="{Binding StatusLabel}"
|
|
Opacity="0.75" VerticalAlignment="Center" />
|
|
</Grid>
|
|
</DataTemplate>
|
|
</ItemsControl.ItemTemplate>
|
|
</ItemsControl>
|
|
</StackPanel>
|
|
|
|
</StackPanel>
|
|
</ScrollViewer>
|
|
|
|
</Grid>
|
|
</DockPanel>
|
|
</Border>
|
|
</UserControl>
|