feat(ui): Log Visualizer overlay reachable from a clickable footer log line

This commit is contained in:
Mika Kuns
2026-06-23 09:05:17 +02:00
parent 08a4f97a78
commit c4f74a7aea
14 changed files with 241 additions and 9 deletions

View File

@@ -215,15 +215,28 @@
</StackPanel>
</Button>
<!-- Right: worker log line -->
<TextBlock DockPanel.Dock="Right"
Classes="meta"
Text="{Binding WorkerLogText}"
IsVisible="{Binding IsWorkerLogVisible}"
Foreground="{Binding WorkerLogLevel, Converter={StaticResource WorkerLogLevelToBrush}}"
LetterSpacing="1.4"
TextTrimming="CharacterEllipsis"
VerticalAlignment="Center"/>
<!-- Right: worker log line — click to open the Log Visualizer overlay -->
<Button DockPanel.Dock="Right"
Command="{Binding OpenLogVisualizerCommand}"
Background="Transparent" BorderThickness="0" Padding="6,0"
Cursor="Hand" VerticalAlignment="Center"
ToolTip.Tip="{loc:Tr modals.logVisualizer.openTooltip}">
<Panel>
<TextBlock Classes="meta"
Text="{loc:Tr modals.logVisualizer.footerHint}"
IsVisible="{Binding !IsWorkerLogVisible}"
Foreground="{DynamicResource TextMuteBrush}"
LetterSpacing="1.4"
VerticalAlignment="Center"/>
<TextBlock Classes="meta"
Text="{Binding WorkerLogText}"
IsVisible="{Binding IsWorkerLogVisible}"
Foreground="{Binding WorkerLogLevel, Converter={StaticResource WorkerLogLevelToBrush}}"
LetterSpacing="1.4"
TextTrimming="CharacterEllipsis"
VerticalAlignment="Center"/>
</Panel>
</Button>
<!-- Right: prime status notification -->
<TextBlock DockPanel.Dock="Right"

View File

@@ -0,0 +1,59 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:ClaudeDo.Ui.ViewModels.Modals"
xmlns:ctl="using:ClaudeDo.Ui.Views.Controls"
xmlns:converters="using:ClaudeDo.Ui.Converters"
xmlns:loc="using:ClaudeDo.Ui.Localization"
x:Class="ClaudeDo.Ui.Views.Modals.LogVisualizerView"
x:DataType="vm:LogVisualizerViewModel"
Title="{loc:Tr modals.logVisualizer.title}"
Width="760" Height="520"
WindowDecorations="None"
ExtendClientAreaToDecorationsHint="True"
WindowStartupLocation="CenterOwner"
Background="{DynamicResource SurfaceBrush}">
<Window.Resources>
<converters:WorkerLogLevelToBrushConverter x:Key="WorkerLogLevelToBrush"/>
</Window.Resources>
<Window.KeyBindings>
<KeyBinding Gesture="Escape" Command="{Binding CloseCommand}"/>
</Window.KeyBindings>
<ctl:ModalShell Title="{loc:Tr modals.logVisualizer.title}" CloseCommand="{Binding CloseCommand}">
<DockPanel LastChildFill="True">
<!-- Toolbar: filter · status · refresh -->
<Grid DockPanel.Dock="Top" ColumnDefinitions="Auto,*,Auto" Margin="14,10">
<CheckBox Grid.Column="0"
Content="{loc:Tr modals.logVisualizer.warnErrorOnly}"
IsChecked="{Binding WarnErrorOnly}"
VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Classes="meta"
Text="{Binding StatusText}"
HorizontalAlignment="Center" VerticalAlignment="Center"
Foreground="{DynamicResource TextMuteBrush}"/>
<Button Grid.Column="2" Classes="btn"
Content="{loc:Tr modals.logVisualizer.refresh}"
Command="{Binding RefreshCommand}"/>
</Grid>
<!-- Last 30 min, oldest-first -->
<Border Classes="terminal" Margin="14,0,14,14">
<ScrollViewer VerticalScrollBarVisibility="Visible" AllowAutoHide="False" Padding="10,8">
<ItemsControl ItemsSource="{Binding Rows}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:LogVisualizerRow">
<Grid ColumnDefinitions="64,*" Margin="0,1">
<TextBlock Grid.Column="0" Classes="log-ts" Text="{Binding Time}"/>
<SelectableTextBlock Grid.Column="1"
Text="{Binding Message}"
Foreground="{Binding Level, Converter={StaticResource WorkerLogLevelToBrush}}"
TextWrapping="Wrap"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Border>
</DockPanel>
</ctl:ModalShell>
</Window>

View File

@@ -0,0 +1,10 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ClaudeDo.Ui.Views.Modals;
public partial class LogVisualizerView : Window
{
public LogVisualizerView() => InitializeComponent();
private void InitializeComponent() => AvaloniaXamlLoader.Load(this);
}

View File

@@ -104,6 +104,13 @@ public sealed class WindowDialogService : IDialogService
await dlg.ShowDialog(_owner);
}
public async Task ShowLogVisualizerAsync(LogVisualizerViewModel vm)
{
var dlg = new LogVisualizerView { DataContext = vm };
vm.CloseAction = () => dlg.Close();
await dlg.ShowDialog(_owner);
}
public Task<bool> ConfirmAsync(string message)
{
var tcs = new TaskCompletionSource<bool>();