feat(ui): rework review into terminal footer and add Git tab

Move review feedback into a prompt-style footer on the Output tab with
Retry/Reset actions, relocate Approve and all merge/worktree controls to a
new Git tab, and reduce the Session tab to subtask outcomes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-06-05 08:34:29 +02:00
parent 6c65158be8
commit 8819a56496

View File

@@ -127,6 +127,11 @@
Content="Output" Content="Output"
Command="{Binding SelectTabCommand}" Command="{Binding SelectTabCommand}"
CommandParameter="output" /> CommandParameter="output" />
<Button Classes="tab-btn"
Classes.active="{Binding IsGitTab}"
Content="Git"
Command="{Binding SelectTabCommand}"
CommandParameter="git" />
<Button Classes="tab-btn" <Button Classes="tab-btn"
Classes.active="{Binding IsSessionTab}" Classes.active="{Binding IsSessionTab}"
Content="Session" Content="Session"
@@ -138,61 +143,78 @@
<!-- ── Tab body (bottom inset keeps content clear of the rounded corner) ── --> <!-- ── Tab body (bottom inset keeps content clear of the rounded corner) ── -->
<Grid Margin="0,0,0,8"> <Grid Margin="0,0,0,8">
<!-- Output: log rendered directly on the console body (no nested card) --> <!-- Output: log + review footer, both gated on IsOutputTab -->
<ScrollViewer Name="LogScroll" <DockPanel IsVisible="{Binding IsOutputTab}" LastChildFill="True">
IsVisible="{Binding IsOutputTab}"
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>
<!-- Session: review (top) + merge/worktree + outcomes — each gated on state --> <!-- Review footer (terminal prompt) — only while awaiting review -->
<ScrollViewer IsVisible="{Binding IsSessionTab}" Padding="14,10"> <Border DockPanel.Dock="Bottom"
<StackPanel Spacing="14"> IsVisible="{Binding IsWaitingForReview}"
Background="{DynamicResource Surface2Brush}"
<!-- Review controls --> BorderBrush="{DynamicResource LineBrush}"
<StackPanel Spacing="8" IsVisible="{Binding IsWaitingForReview}"> BorderThickness="0,1,0,0"
<TextBlock Classes="section-label" Text="REVIEW" /> Padding="10,6">
<TextBlock Classes="field-label" Text="Feedback" /> <DockPanel LastChildFill="True">
<TextBox Text="{Binding ReviewFeedback, Mode=TwoWay}" <StackPanel DockPanel.Dock="Right" Orientation="Horizontal" Spacing="8"
VerticalAlignment="Bottom" Margin="8,0,0,0">
<Button Classes="btn accent" Content="Retry"
Command="{Binding RejectReviewCommand}" />
<Button Classes="btn" Content="Reset"
Command="{Binding ParkReviewCommand}" />
</StackPanel>
<TextBlock DockPanel.Dock="Left" Text="&#x276F;"
FontFamily="{StaticResource MonoFont}"
Foreground="{DynamicResource TextMuteBrush}"
VerticalAlignment="Top" Margin="0,4,8,0" />
<TextBox Name="ReviewInput"
KeyDown="OnReviewInputKeyDown"
Text="{Binding ReviewFeedback, Mode=TwoWay}"
AcceptsReturn="True" AcceptsReturn="True"
TextWrapping="Wrap" TextWrapping="Wrap"
MinHeight="60" MaxHeight="160"
MaxHeight="180" PlaceholderText="Feedback for the next run…"
PlaceholderText="Optional feedback for the next run…" Background="Transparent"
Padding="8" BorderThickness="0"
Background="{DynamicResource Surface2Brush}" Padding="0,2"
BorderBrush="{DynamicResource LineBrush}" FontFamily="{StaticResource MonoFont}"
BorderThickness="1" FontSize="{StaticResource FontSizeMono}" />
CornerRadius="8" /> </DockPanel>
<StackPanel Orientation="Horizontal" Spacing="8"> </Border>
<Button Classes="btn accent" Content="Approve"
Command="{Binding ApproveReviewCommand}" /> <ScrollViewer Name="LogScroll"
<Button Classes="btn" Content="Reject" VerticalScrollBarVisibility="Visible"
Command="{Binding RejectReviewCommand}" /> AllowAutoHide="False"
<Button Classes="btn" Content="Park" Padding="12,8,12,4">
Command="{Binding ParkReviewCommand}" /> <ItemsControl ItemsSource="{Binding Log}">
<Button Classes="btn" Content="Cancel" <ItemsControl.ItemTemplate>
Command="{Binding CancelReviewCommand}" /> <DataTemplate DataType="vm:LogLineViewModel">
</StackPanel> <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: merge target, approve, diff, worktree -->
<ScrollViewer IsVisible="{Binding IsGitTab}" Padding="14,10">
<StackPanel Spacing="14">
<!-- Approve (review-gated) -->
<StackPanel Spacing="8" IsVisible="{Binding IsWaitingForReview}">
<TextBlock Classes="section-label" Text="REVIEW" />
<Button Classes="btn accent" Content="Approve"
Command="{Binding ApproveReviewCommand}" />
</StackPanel> </StackPanel>
<!-- Merge & worktree management --> <!-- Merge & worktree management (moved from Session tab) -->
<StackPanel Spacing="10" IsVisible="{Binding ShowMergeSection}"> <StackPanel Spacing="10" IsVisible="{Binding ShowMergeSection}">
<TextBlock Classes="section-label" Text="MERGE &amp; WORKTREE" /> <TextBlock Classes="section-label" Text="MERGE &amp; WORKTREE" />
<StackPanel Spacing="4"> <StackPanel Spacing="4">
@@ -201,7 +223,6 @@
SelectedItem="{Binding SelectedMergeTarget, Mode=TwoWay}" SelectedItem="{Binding SelectedMergeTarget, Mode=TwoWay}"
HorizontalAlignment="Stretch" /> HorizontalAlignment="Stretch" />
</StackPanel> </StackPanel>
<!-- Mergeability at a glance: green when clean, red on conflict, muted otherwise -->
<StackPanel Spacing="0"> <StackPanel Spacing="0">
<TextBlock Classes="meta" Text="{Binding MergePreviewText}" TextWrapping="Wrap" <TextBlock Classes="meta" Text="{Binding MergePreviewText}" TextWrapping="Wrap"
Foreground="{DynamicResource MossBrush}" Foreground="{DynamicResource MossBrush}"
@@ -240,6 +261,13 @@
Converter={x:Static ObjectConverters.IsNotNull}}" /> Converter={x:Static ObjectConverters.IsNotNull}}" />
</StackPanel> </StackPanel>
</StackPanel>
</ScrollViewer>
<!-- Session: subtask outcomes (review lives in Output, merge in Git) -->
<ScrollViewer IsVisible="{Binding IsSessionTab}" Padding="14,10">
<StackPanel Spacing="14">
<!-- Child outcomes --> <!-- Child outcomes -->
<StackPanel Spacing="6" IsVisible="{Binding HasChildOutcomes}"> <StackPanel Spacing="6" IsVisible="{Binding HasChildOutcomes}">
<TextBlock Classes="section-label" Text="OUTCOMES" /> <TextBlock Classes="section-label" Text="OUTCOMES" />
@@ -267,7 +295,7 @@
Classes="meta" Classes="meta"
Foreground="{DynamicResource TextMuteBrush}" Foreground="{DynamicResource TextMuteBrush}"
TextWrapping="Wrap" TextWrapping="Wrap"
Text="Nothing to manage yet — review and merge controls appear here once the run finishes." /> Text="Nothing to manage yet — subtask outcomes appear here once the run finishes. Review in the Output tab, merge in the Git tab." />
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>