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:
@@ -70,8 +70,14 @@
|
|||||||
<!-- Icon.Trash -->
|
<!-- 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>
|
<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 -->
|
<!-- Icon.Sort (filled bars, decreasing width) -->
|
||||||
<StreamGeometry x:Key="Icon.Sort">M7 4v16M7 20l-3-3M7 4l-3 3M14 8h7M14 12h5M14 16h3</StreamGeometry>
|
<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 -->
|
<!-- Icon.X -->
|
||||||
<StreamGeometry x:Key="Icon.X">M6 6l12 12M18 6L6 18</StreamGeometry>
|
<StreamGeometry x:Key="Icon.X">M6 6l12 12M18 6L6 18</StreamGeometry>
|
||||||
|
|||||||
@@ -292,7 +292,10 @@
|
|||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- Session terminal — auto-sizes to output, scrolls internally after MaxHeight -->
|
<!-- 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>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
@@ -300,27 +303,9 @@
|
|||||||
<islands:NotesEditorView DataContext="{Binding Notes}"/>
|
<islands:NotesEditorView DataContext="{Binding Notes}"/>
|
||||||
</Panel>
|
</Panel>
|
||||||
<Panel IsVisible="{Binding IsPrepMode}">
|
<Panel IsVisible="{Binding IsPrepMode}">
|
||||||
<DockPanel>
|
<islands:SessionTerminalView
|
||||||
<TextBlock DockPanel.Dock="Top" Margin="16,12"
|
Entries="{Binding PrepLog}" Label="daily-prep"
|
||||||
Text="{loc:Tr details.prepTitle}" Classes="h2"/>
|
IsRunning="{Binding IsPrepRunning}"/>
|
||||||
<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>
|
|
||||||
</Panel>
|
</Panel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
xmlns:vm="using:ClaudeDo.Ui.ViewModels.Islands"
|
xmlns:vm="using:ClaudeDo.Ui.ViewModels.Islands"
|
||||||
xmlns:loc="using:ClaudeDo.Ui.Localization"
|
xmlns:loc="using:ClaudeDo.Ui.Localization"
|
||||||
x:Class="ClaudeDo.Ui.Views.Islands.SessionTerminalView"
|
x:Class="ClaudeDo.Ui.Views.Islands.SessionTerminalView"
|
||||||
x:DataType="vm:DetailsIslandViewModel">
|
x:Name="Root">
|
||||||
<Border Classes="terminal" Margin="18,8,18,0">
|
<Border Classes="terminal" Margin="18,8,18,0">
|
||||||
<DockPanel LastChildFill="True">
|
<DockPanel LastChildFill="True">
|
||||||
|
|
||||||
@@ -14,14 +14,14 @@
|
|||||||
<!-- Session label -->
|
<!-- Session label -->
|
||||||
<TextBlock Grid.Column="1"
|
<TextBlock Grid.Column="1"
|
||||||
Classes="meta"
|
Classes="meta"
|
||||||
Text="{Binding BranchLine, StringFormat='claude-session · {0}'}"
|
Text="{Binding #Root.Label}"
|
||||||
LetterSpacing="0.8"
|
LetterSpacing="0.8"
|
||||||
Foreground="{DynamicResource TextMuteBrush}"
|
Foreground="{DynamicResource TextMuteBrush}"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"/>
|
VerticalAlignment="Center"/>
|
||||||
<!-- LIVE chip -->
|
<!-- LIVE chip -->
|
||||||
<Border Grid.Column="2" Classes="live-chip pulsing"
|
<Border Grid.Column="2" Classes="live-chip pulsing"
|
||||||
IsVisible="{Binding IsRunning}"
|
IsVisible="{Binding #Root.IsRunning}"
|
||||||
Margin="0,0,8,0" VerticalAlignment="Center">
|
Margin="0,0,8,0" VerticalAlignment="Center">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="5" VerticalAlignment="Center">
|
<StackPanel Orientation="Horizontal" Spacing="5" VerticalAlignment="Center">
|
||||||
<Ellipse VerticalAlignment="Center"/>
|
<Ellipse VerticalAlignment="Center"/>
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
</Border>
|
</Border>
|
||||||
<!-- DONE chip -->
|
<!-- DONE chip -->
|
||||||
<Border Grid.Column="2" Classes="live-chip done"
|
<Border Grid.Column="2" Classes="live-chip done"
|
||||||
IsVisible="{Binding IsDone}"
|
IsVisible="{Binding #Root.IsDone}"
|
||||||
Margin="0,0,8,0" VerticalAlignment="Center">
|
Margin="0,0,8,0" VerticalAlignment="Center">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="5" VerticalAlignment="Center">
|
<StackPanel Orientation="Horizontal" Spacing="5" VerticalAlignment="Center">
|
||||||
<Ellipse VerticalAlignment="Center" Fill="{DynamicResource MossBrush}"/>
|
<Ellipse VerticalAlignment="Center" Fill="{DynamicResource MossBrush}"/>
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
</Border>
|
</Border>
|
||||||
<!-- FAILED chip -->
|
<!-- FAILED chip -->
|
||||||
<Border Grid.Column="2" Classes="live-chip failed"
|
<Border Grid.Column="2" Classes="live-chip failed"
|
||||||
IsVisible="{Binding IsFailed}"
|
IsVisible="{Binding #Root.IsFailed}"
|
||||||
Margin="0,0,8,0" VerticalAlignment="Center">
|
Margin="0,0,8,0" VerticalAlignment="Center">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="5" VerticalAlignment="Center">
|
<StackPanel Orientation="Horizontal" Spacing="5" VerticalAlignment="Center">
|
||||||
<Ellipse VerticalAlignment="Center" Fill="{DynamicResource BloodBrush}"/>
|
<Ellipse VerticalAlignment="Center" Fill="{DynamicResource BloodBrush}"/>
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
VerticalScrollBarVisibility="Visible"
|
VerticalScrollBarVisibility="Visible"
|
||||||
AllowAutoHide="False"
|
AllowAutoHide="False"
|
||||||
Padding="10,8,10,12">
|
Padding="10,8,10,12">
|
||||||
<ItemsControl ItemsSource="{Binding Log}">
|
<ItemsControl ItemsSource="{Binding #Root.Entries}">
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate DataType="vm:LogLineViewModel">
|
<DataTemplate DataType="vm:LogLineViewModel">
|
||||||
<Grid ColumnDefinitions="60,*" Margin="0,1">
|
<Grid ColumnDefinitions="60,*" Margin="0,1">
|
||||||
|
|||||||
@@ -1,30 +1,50 @@
|
|||||||
|
using System.Collections;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using ClaudeDo.Ui.ViewModels.Islands;
|
|
||||||
|
|
||||||
namespace ClaudeDo.Ui.Views.Islands;
|
namespace ClaudeDo.Ui.Views.Islands;
|
||||||
|
|
||||||
public partial class SessionTerminalView : UserControl
|
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(); }
|
public SessionTerminalView() { InitializeComponent(); }
|
||||||
|
|
||||||
private DetailsIslandViewModel? _boundVm;
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||||
|
|
||||||
protected override void OnDataContextChanged(EventArgs e)
|
|
||||||
{
|
{
|
||||||
base.OnDataContextChanged(e);
|
base.OnPropertyChanged(change);
|
||||||
if (_boundVm is not null)
|
if (change.Property != EntriesProperty) return;
|
||||||
_boundVm.Log.CollectionChanged -= OnLogChanged;
|
|
||||||
_boundVm = DataContext as DetailsIslandViewModel;
|
if (_subscribedCollection is not null)
|
||||||
if (_boundVm is not null)
|
_subscribedCollection.CollectionChanged -= OnEntriesChanged;
|
||||||
_boundVm.Log.CollectionChanged += OnLogChanged;
|
|
||||||
|
_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;
|
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;
|
EventHandler? handler = null;
|
||||||
handler = (_, _) =>
|
handler = (_, _) =>
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user