diff --git a/src/UniGetUI.Avalonia/App.axaml b/src/UniGetUI.Avalonia/App.axaml
index e948a7b790..0090c8aa0b 100644
--- a/src/UniGetUI.Avalonia/App.axaml
+++ b/src/UniGetUI.Avalonia/App.axaml
@@ -11,6 +11,7 @@
+
diff --git a/src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj b/src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj
index 66cf6a063e..66a3bfa672 100644
--- a/src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj
+++ b/src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj
@@ -126,6 +126,7 @@
+
diff --git a/src/UniGetUI.Avalonia/ViewModels/SidebarViewModel.cs b/src/UniGetUI.Avalonia/ViewModels/SidebarViewModel.cs
index 815d7d3cf3..0c20c6432e 100644
--- a/src/UniGetUI.Avalonia/ViewModels/SidebarViewModel.cs
+++ b/src/UniGetUI.Avalonia/ViewModels/SidebarViewModel.cs
@@ -60,7 +60,7 @@ partial void OnIsPaneOpenChanged(bool value)
OnPropertyChanged(nameof(BundlesBadgeCompactVisible));
}
- public double PaneWidth => IsPaneOpen ? 250 : 72;
+ public double PaneWidth => IsPaneOpen ? 250 : 64;
public bool UpdatesBadgeExpandedVisible => UpdatesBadgeVisible && IsPaneOpen;
public bool UpdatesBadgeCompactVisible => UpdatesBadgeVisible && !IsPaneOpen;
diff --git a/src/UniGetUI.Avalonia/Views/Controls/LogTextEditor.cs b/src/UniGetUI.Avalonia/Views/Controls/LogTextEditor.cs
new file mode 100644
index 0000000000..63791ae0aa
--- /dev/null
+++ b/src/UniGetUI.Avalonia/Views/Controls/LogTextEditor.cs
@@ -0,0 +1,104 @@
+using System.Text;
+using Avalonia;
+using Avalonia.Controls.Primitives;
+using Avalonia.Media;
+using Avalonia.Styling;
+using AvaloniaEdit;
+using AvaloniaEdit.Document;
+using AvaloniaEdit.Rendering;
+using UniGetUI.Avalonia.ViewModels.Pages.LogPages;
+
+namespace UniGetUI.Avalonia.Views.Controls;
+
+// Read-only colored log/console viewer shared by every log and command-output view: virtualized
+// rendering (smooth scroll), free character-level selection across lines, per-line colors, and a
+// theme-aware hyperlink color.
+public class LogTextEditor : TextEditor
+{
+ // Per physical line color, indexed by 0-based document line number.
+ private readonly List _lineColors = [];
+
+ // Use AvaloniaEdit's TextEditor theme/template; a subclass key has no matching ControlTheme.
+ protected override Type StyleKeyOverride => typeof(TextEditor);
+
+ public LogTextEditor()
+ {
+ IsReadOnly = true;
+ ShowLineNumbers = false;
+ WordWrap = true;
+ Background = Brushes.Transparent;
+ FontFamily = new FontFamily("Cascadia Mono,Consolas,Menlo,monospace");
+ FontSize = 12;
+ Padding = new Thickness(8);
+ HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
+ VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
+
+ TextArea.TextView.LineTransformers.Add(new SeverityColorizer(_lineColors));
+ UpdateLinkColor();
+ ActualThemeVariantChanged += (_, _) => UpdateLinkColor();
+ }
+
+ public void SetLines(IEnumerable lines)
+ {
+ _lineColors.Clear();
+ var sb = new StringBuilder();
+ bool first = true;
+
+ foreach (var item in lines)
+ {
+ foreach (string physicalLine in item.Text.Split('\n'))
+ {
+ if (!first) sb.Append('\n');
+ sb.Append(physicalLine);
+ _lineColors.Add(item.Foreground);
+ first = false;
+ }
+ }
+
+ Text = sb.ToString();
+ TextArea.TextView.Redraw();
+ }
+
+ public void AppendLine(LogLineItem line)
+ {
+ var sb = new StringBuilder();
+ foreach (string physicalLine in line.Text.Split('\n'))
+ {
+ if (Document.TextLength > 0 || sb.Length > 0)
+ sb.Append('\n');
+ sb.Append(physicalLine);
+ _lineColors.Add(line.Foreground);
+ }
+
+ Document.Insert(Document.TextLength, sb.ToString());
+ }
+
+ public void ClearLines()
+ {
+ _lineColors.Clear();
+ Text = string.Empty;
+ }
+
+ public void ScrollToBottom() => ScrollToEnd();
+
+ // AvaloniaEdit auto-links URLs; its default link brush is too dark to read on the dark theme.
+ private void UpdateLinkColor()
+ {
+ bool isDark = Application.Current?.ActualThemeVariant == ThemeVariant.Dark;
+ TextArea.TextView.LinkTextForegroundBrush =
+ new SolidColorBrush(isDark ? Color.FromRgb(100, 170, 255) : Color.FromRgb(0, 0, 205));
+ }
+
+ private sealed class SeverityColorizer(List lineColors) : DocumentColorizingTransformer
+ {
+ protected override void ColorizeLine(DocumentLine line)
+ {
+ if (line.Length == 0) return;
+ int index = line.LineNumber - 1;
+ if (index < 0 || index >= lineColors.Count) return;
+
+ IBrush brush = lineColors[index];
+ ChangeLinePart(line.Offset, line.EndOffset, element => element.TextRunProperties.SetForegroundBrush(brush));
+ }
+ }
+}
diff --git a/src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsCard.cs b/src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsCard.cs
index 4d28d527fe..4f1f7e48f4 100644
--- a/src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsCard.cs
+++ b/src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsCard.cs
@@ -6,6 +6,7 @@
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
+using UniGetUI.Avalonia.Views.Controls;
using ICommand = System.Windows.Input.ICommand;
namespace UniGetUI.Avalonia.Views.Controls.Settings;
@@ -23,7 +24,7 @@ public class SettingsCard : UserControl
private readonly ContentControl _descriptionPresenter;
private readonly ContentControl _contentPresenter;
private readonly StackPanel _descriptionRow;
- private readonly TextBlock _chevron;
+ private readonly SvgIcon _chevron;
// ── Styled properties ──────────────────────────────────────────────────
public static readonly StyledProperty