feat(ui): maximize work console via green traffic-light dot

This commit is contained in:
mika kuns
2026-06-05 10:27:47 +02:00
parent 2dfc4559b1
commit de4ad5dcf3
6 changed files with 79 additions and 6 deletions

View File

@@ -186,6 +186,9 @@
"chipDone": "FERTIG", "chipDone": "FERTIG",
"chipFailed": "FEHLGESCHLAGEN" "chipFailed": "FEHLGESCHLAGEN"
}, },
"console": {
"maximizeTip": "Terminal maximieren / wiederherstellen"
},
"modals": { "modals": {
"about": { "about": {
"title": "ÜBER", "title": "ÜBER",

View File

@@ -186,6 +186,9 @@
"chipDone": "DONE", "chipDone": "DONE",
"chipFailed": "FAILED" "chipFailed": "FAILED"
}, },
"console": {
"maximizeTip": "Maximize / restore the terminal"
},
"modals": { "modals": {
"about": { "about": {
"title": "ABOUT", "title": "ABOUT",

View File

@@ -62,6 +62,14 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
partial void OnIsNotesModeChanged(bool value) => OnPropertyChanged(nameof(IsTaskDetailVisible)); partial void OnIsNotesModeChanged(bool value) => OnPropertyChanged(nameof(IsTaskDetailVisible));
partial void OnIsPrepModeChanged(bool value) => OnPropertyChanged(nameof(IsTaskDetailVisible)); partial void OnIsPrepModeChanged(bool value) => OnPropertyChanged(nameof(IsTaskDetailVisible));
// Console maximize: green dot shrinks the description row to its MinHeight so
// the WorkConsole fills the rest. The row stays draggable and never overlaps.
// Applied in DetailsIslandView code-behind (RowDefinitions can't bind).
[ObservableProperty] private bool _isConsoleMaximized;
[RelayCommand]
private void ToggleConsoleMaximized() => IsConsoleMaximized = !IsConsoleMaximized;
public NotesEditorViewModel Notes { get; private set; } = null!; public NotesEditorViewModel Notes { get; private set; } = null!;
// Current task row (set by IslandsShellViewModel via Bind) // Current task row (set by IslandsShellViewModel via Bind)

View File

@@ -24,6 +24,18 @@
<Setter Property="TextElement.Foreground" Value="{StaticResource AccentBrush}" /> <Setter Property="TextElement.Foreground" Value="{StaticResource AccentBrush}" />
</Style> </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 --> <!-- Terminal prompt action: bracketed text, no button chrome -->
<Style Selector="Button.prompt-action"> <Style Selector="Button.prompt-action">
<Setter Property="Background" Value="Transparent" /> <Setter Property="Background" Value="Transparent" />
@@ -60,12 +72,22 @@
<Grid DockPanel.Dock="Top" ColumnDefinitions="Auto,*,Auto" <Grid DockPanel.Dock="Top" ColumnDefinitions="Auto,*,Auto"
Background="{DynamicResource Surface2Brush}" Height="28"> Background="{DynamicResource Surface2Brush}" Height="28">
<!-- Traffic-light dots --> <!-- Traffic-light dots; green toggles console maximize -->
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="6" <StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="6"
Margin="12,0" VerticalAlignment="Center"> Margin="12,0" VerticalAlignment="Center">
<Ellipse Classes="dot-red" /> <Ellipse Classes="dot-red" />
<Ellipse Classes="dot-yellow" /> <Ellipse Classes="dot-yellow" />
<Ellipse Classes="dot-green" /> <Button Classes="dot-btn"
Command="{Binding ToggleConsoleMaximizedCommand}"
ToolTip.Tip="{loc:Tr console.maximizeTip}">
<Panel>
<Ellipse Classes="dot-green" />
<PathIcon Data="{StaticResource Icon.ArrowOut}"
Width="6" Height="6"
Foreground="{DynamicResource MossBrightBrush}"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Panel>
</Button>
</StackPanel> </StackPanel>
<!-- Right cluster: info header (model · turns · diff) + status chip --> <!-- Right cluster: info header (model · turns · diff) + status chip -->

View File

@@ -39,15 +39,22 @@
<Grid> <Grid>
<!-- Task detail: description/steps card (upper) + pinned work console (lower) --> <!-- Task detail: description/steps card (upper) + pinned work console (lower) -->
<Grid IsVisible="{Binding IsTaskDetailVisible}" <Grid x:Name="DetailBodyGrid"
Margin="14,12,14,12" IsVisible="{Binding IsTaskDetailVisible}"
RowDefinitions="2*,*"> Margin="14,12,14,12">
<Grid.RowDefinitions>
<!-- MinHeight keeps the description visible: the console can never
overlap it, whether maximized (code-behind) or dragged. -->
<RowDefinition Height="2*" MinHeight="90"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto"> <ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto">
<detail:DescriptionStepsCard VerticalAlignment="Top"/> <detail:DescriptionStepsCard VerticalAlignment="Top"/>
</ScrollViewer> </ScrollViewer>
<detail:WorkConsole Grid.Row="1" Margin="0,10,0,0"/> <detail:WorkConsole Grid.Row="1" Margin="0,10,0,0"/>
<!-- Resize by dragging the console's top edge — a transparent splitter <!-- Resize by dragging the console's top edge — a transparent splitter
over the gap above the console; no standalone separator bar. --> over the gap above the console; no standalone separator bar.
Stays draggable while maximized. -->
<GridSplitter Grid.Row="1" <GridSplitter Grid.Row="1"
VerticalAlignment="Top" VerticalAlignment="Top"
Height="10" Height="10"

View File

@@ -1,3 +1,4 @@
using System.ComponentModel;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Layout; using Avalonia.Layout;
@@ -10,6 +11,8 @@ namespace ClaudeDo.Ui.Views.Islands;
public partial class DetailsIslandView : UserControl public partial class DetailsIslandView : UserControl
{ {
private DetailsIslandViewModel? _subscribedVm;
public DetailsIslandView() public DetailsIslandView()
{ {
InitializeComponent(); InitializeComponent();
@@ -18,6 +21,15 @@ public partial class DetailsIslandView : UserControl
private void OnDataContextChanged(object? sender, EventArgs e) private void OnDataContextChanged(object? sender, EventArgs e)
{ {
if (_subscribedVm is not null)
_subscribedVm.PropertyChanged -= OnVmPropertyChanged;
_subscribedVm = DataContext as DetailsIslandViewModel;
if (_subscribedVm is not null)
{
_subscribedVm.PropertyChanged += OnVmPropertyChanged;
ApplyConsoleMaximized(_subscribedVm.IsConsoleMaximized);
}
if (DataContext is DetailsIslandViewModel vm) if (DataContext is DetailsIslandViewModel vm)
{ {
vm.ShowDiffModal = async (diffVm) => vm.ShowDiffModal = async (diffVm) =>
@@ -49,6 +61,24 @@ public partial class DetailsIslandView : UserControl
} }
} }
private void OnVmPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(DetailsIslandViewModel.IsConsoleMaximized)
&& sender is DetailsIslandViewModel vm)
ApplyConsoleMaximized(vm.IsConsoleMaximized);
}
// Maximized: shrink the description row to its MinHeight (the console fills
// the rest). Restored: back to the 2:1 default. The GridSplitter keeps both
// states draggable; MinHeight stops the console from ever covering it.
private void ApplyConsoleMaximized(bool maximized)
{
var descRow = DetailBodyGrid.RowDefinitions[0];
descRow.Height = maximized
? new GridLength(descRow.MinHeight, GridUnitType.Pixel)
: new GridLength(2, GridUnitType.Star);
}
private async System.Threading.Tasks.Task ShowErrorDialogAsync(string message) private async System.Threading.Tasks.Task ShowErrorDialogAsync(string message)
{ {
var owner = TopLevel.GetTopLevel(this) as Window; var owner = TopLevel.GetTopLevel(this) as Window;