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",
"chipFailed": "FEHLGESCHLAGEN"
},
"console": {
"maximizeTip": "Terminal maximieren / wiederherstellen"
},
"modals": {
"about": {
"title": "ÜBER",

View File

@@ -186,6 +186,9 @@
"chipDone": "DONE",
"chipFailed": "FAILED"
},
"console": {
"maximizeTip": "Maximize / restore the terminal"
},
"modals": {
"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 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!;
// Current task row (set by IslandsShellViewModel via Bind)

View File

@@ -24,6 +24,18 @@
<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" />
@@ -60,12 +72,22 @@
<Grid DockPanel.Dock="Top" ColumnDefinitions="Auto,*,Auto"
Background="{DynamicResource Surface2Brush}" Height="28">
<!-- Traffic-light dots -->
<!-- Traffic-light dots; green toggles console maximize -->
<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" />
<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>
<!-- Right cluster: info header (model · turns · diff) + status chip -->

View File

@@ -39,15 +39,22 @@
<Grid>
<!-- Task detail: description/steps card (upper) + pinned work console (lower) -->
<Grid IsVisible="{Binding IsTaskDetailVisible}"
Margin="14,12,14,12"
RowDefinitions="2*,*">
<Grid x:Name="DetailBodyGrid"
IsVisible="{Binding IsTaskDetailVisible}"
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">
<detail:DescriptionStepsCard VerticalAlignment="Top"/>
</ScrollViewer>
<detail:WorkConsole Grid.Row="1" Margin="0,10,0,0"/>
<!-- 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"
VerticalAlignment="Top"
Height="10"

View File

@@ -1,3 +1,4 @@
using System.ComponentModel;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
@@ -10,6 +11,8 @@ namespace ClaudeDo.Ui.Views.Islands;
public partial class DetailsIslandView : UserControl
{
private DetailsIslandViewModel? _subscribedVm;
public DetailsIslandView()
{
InitializeComponent();
@@ -18,6 +21,15 @@ public partial class DetailsIslandView : UserControl
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)
{
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)
{
var owner = TopLevel.GetTopLevel(this) as Window;