feat(daily-prep): reuse SessionTerminal for prep log; fix invisible Sort icon; add Broom/List icons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-06-04 09:08:04 +02:00
parent fab17720cc
commit f7d1b37343
4 changed files with 54 additions and 43 deletions

View File

@@ -70,8 +70,14 @@
<!-- Icon.Trash -->
<StreamGeometry x:Key="Icon.Trash">M4 7h16M10 11v6M14 11v6M5 7l1 13a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1l1-13M9 7V4h6v3</StreamGeometry>
<!-- Icon.Sort -->
<StreamGeometry x:Key="Icon.Sort">M7 4v16M7 20l-3-3M7 4l-3 3M14 8h7M14 12h5M14 16h3</StreamGeometry>
<!-- Icon.Sort (filled bars, decreasing width) -->
<StreamGeometry x:Key="Icon.Sort">M4 6 H20 V8 H4 Z M4 11 H16 V13 H4 Z M4 16 H11 V18 H4 Z</StreamGeometry>
<!-- Icon.Broom (filled: handle + binding band + flared bristles) -->
<StreamGeometry x:Key="Icon.Broom">M11 3 H13 V10 H11 Z M8.5 10 H15.5 V12 H8.5 Z M9 12 H15 L17 21 H7 Z</StreamGeometry>
<!-- Icon.List (filled: square bullets + lines) -->
<StreamGeometry x:Key="Icon.List">M4 5 H6 V7 H4 Z M8 5 H20 V7 H8 Z M4 11 H6 V13 H4 Z M8 11 H20 V13 H8 Z M4 17 H6 V19 H4 Z M8 17 H20 V19 H8 Z</StreamGeometry>
<!-- Icon.X -->
<StreamGeometry x:Key="Icon.X">M6 6l12 12M18 6L6 18</StreamGeometry>

View File

@@ -292,7 +292,10 @@
</Border>
<!-- Session terminal — auto-sizes to output, scrolls internally after MaxHeight -->
<islands:SessionTerminalView MaxHeight="420"/>
<islands:SessionTerminalView MaxHeight="420"
Entries="{Binding Log}"
Label="{Binding BranchLine, StringFormat='claude-session · {0}'}"
IsRunning="{Binding IsRunning}" IsDone="{Binding IsDone}" IsFailed="{Binding IsFailed}"/>
</StackPanel>
</ScrollViewer>
@@ -300,27 +303,9 @@
<islands:NotesEditorView DataContext="{Binding Notes}"/>
</Panel>
<Panel IsVisible="{Binding IsPrepMode}">
<DockPanel>
<TextBlock DockPanel.Dock="Top" Margin="16,12"
Text="{loc:Tr details.prepTitle}" Classes="h2"/>
<ScrollViewer>
<ItemsControl ItemsSource="{Binding PrepLog}">
<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>
<islands:SessionTerminalView
Entries="{Binding PrepLog}" Label="daily-prep"
IsRunning="{Binding IsPrepRunning}"/>
</Panel>
</Grid>

View File

@@ -3,7 +3,7 @@
xmlns:vm="using:ClaudeDo.Ui.ViewModels.Islands"
xmlns:loc="using:ClaudeDo.Ui.Localization"
x:Class="ClaudeDo.Ui.Views.Islands.SessionTerminalView"
x:DataType="vm:DetailsIslandViewModel">
x:Name="Root">
<Border Classes="terminal" Margin="18,8,18,0">
<DockPanel LastChildFill="True">
@@ -14,14 +14,14 @@
<!-- Session label -->
<TextBlock Grid.Column="1"
Classes="meta"
Text="{Binding BranchLine, StringFormat='claude-session · {0}'}"
Text="{Binding #Root.Label}"
LetterSpacing="0.8"
Foreground="{DynamicResource TextMuteBrush}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<!-- LIVE chip -->
<Border Grid.Column="2" Classes="live-chip pulsing"
IsVisible="{Binding IsRunning}"
IsVisible="{Binding #Root.IsRunning}"
Margin="0,0,8,0" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal" Spacing="5" VerticalAlignment="Center">
<Ellipse VerticalAlignment="Center"/>
@@ -30,7 +30,7 @@
</Border>
<!-- DONE chip -->
<Border Grid.Column="2" Classes="live-chip done"
IsVisible="{Binding IsDone}"
IsVisible="{Binding #Root.IsDone}"
Margin="0,0,8,0" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal" Spacing="5" VerticalAlignment="Center">
<Ellipse VerticalAlignment="Center" Fill="{DynamicResource MossBrush}"/>
@@ -40,7 +40,7 @@
</Border>
<!-- FAILED chip -->
<Border Grid.Column="2" Classes="live-chip failed"
IsVisible="{Binding IsFailed}"
IsVisible="{Binding #Root.IsFailed}"
Margin="0,0,8,0" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal" Spacing="5" VerticalAlignment="Center">
<Ellipse VerticalAlignment="Center" Fill="{DynamicResource BloodBrush}"/>
@@ -55,7 +55,7 @@
VerticalScrollBarVisibility="Visible"
AllowAutoHide="False"
Padding="10,8,10,12">
<ItemsControl ItemsSource="{Binding Log}">
<ItemsControl ItemsSource="{Binding #Root.Entries}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:LogLineViewModel">
<Grid ColumnDefinitions="60,*" Margin="0,1">

View File

@@ -1,30 +1,50 @@
using System.Collections;
using System.Collections.Specialized;
using Avalonia;
using Avalonia.Controls;
using ClaudeDo.Ui.ViewModels.Islands;
namespace ClaudeDo.Ui.Views.Islands;
public partial class SessionTerminalView : UserControl
{
public static readonly StyledProperty<IEnumerable?> EntriesProperty =
AvaloniaProperty.Register<SessionTerminalView, IEnumerable?>(nameof(Entries));
public static readonly StyledProperty<string?> LabelProperty =
AvaloniaProperty.Register<SessionTerminalView, string?>(nameof(Label));
public static readonly StyledProperty<bool> IsRunningProperty =
AvaloniaProperty.Register<SessionTerminalView, bool>(nameof(IsRunning));
public static readonly StyledProperty<bool> IsDoneProperty =
AvaloniaProperty.Register<SessionTerminalView, bool>(nameof(IsDone));
public static readonly StyledProperty<bool> IsFailedProperty =
AvaloniaProperty.Register<SessionTerminalView, bool>(nameof(IsFailed));
public IEnumerable? Entries { get => GetValue(EntriesProperty); set => SetValue(EntriesProperty, value); }
public string? Label { get => GetValue(LabelProperty); set => SetValue(LabelProperty, value); }
public bool IsRunning { get => GetValue(IsRunningProperty); set => SetValue(IsRunningProperty, value); }
public bool IsDone { get => GetValue(IsDoneProperty); set => SetValue(IsDoneProperty, value); }
public bool IsFailed { get => GetValue(IsFailedProperty); set => SetValue(IsFailedProperty, value); }
private INotifyCollectionChanged? _subscribedCollection;
public SessionTerminalView() { InitializeComponent(); }
private DetailsIslandViewModel? _boundVm;
protected override void OnDataContextChanged(EventArgs e)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnDataContextChanged(e);
if (_boundVm is not null)
_boundVm.Log.CollectionChanged -= OnLogChanged;
_boundVm = DataContext as DetailsIslandViewModel;
if (_boundVm is not null)
_boundVm.Log.CollectionChanged += OnLogChanged;
base.OnPropertyChanged(change);
if (change.Property != EntriesProperty) return;
if (_subscribedCollection is not null)
_subscribedCollection.CollectionChanged -= OnEntriesChanged;
_subscribedCollection = change.NewValue as INotifyCollectionChanged;
if (_subscribedCollection is not null)
_subscribedCollection.CollectionChanged += OnEntriesChanged;
}
private void OnLogChanged(object? sender, NotifyCollectionChangedEventArgs e)
private void OnEntriesChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action != NotifyCollectionChangedAction.Add) return;
// Scroll after the next layout pass so the freshly-added (wrapping) line
// is measured first — otherwise ScrollToEnd stops short and clips it.
EventHandler? handler = null;
handler = (_, _) =>
{