diff --git a/src/ClaudeDo.App/App.axaml b/src/ClaudeDo.App/App.axaml
index edd82c3..0c7445f 100644
--- a/src/ClaudeDo.App/App.axaml
+++ b/src/ClaudeDo.App/App.axaml
@@ -15,6 +15,9 @@
+
+
+
diff --git a/src/ClaudeDo.Ui/Converters/DotBrushConverter.cs b/src/ClaudeDo.Ui/Converters/DotBrushConverter.cs
new file mode 100644
index 0000000..d24bbad
--- /dev/null
+++ b/src/ClaudeDo.Ui/Converters/DotBrushConverter.cs
@@ -0,0 +1,24 @@
+using System.Globalization;
+using Avalonia;
+using Avalonia.Data.Converters;
+using Avalonia.Media;
+
+namespace ClaudeDo.Ui.Converters;
+
+public class DotBrushConverter : IValueConverter
+{
+ public static DotBrushConverter Instance { get; } = new();
+
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ var key = value?.ToString();
+ if (string.IsNullOrEmpty(key)) key = "Moss";
+ var resourceKey = $"{key}Brush";
+ if (Application.Current?.TryGetResource(resourceKey, null, out var res) == true)
+ return res as IBrush;
+ return Brushes.Transparent;
+ }
+
+ public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ => throw new NotSupportedException();
+}
diff --git a/src/ClaudeDo.Ui/Converters/IconKeyConverter.cs b/src/ClaudeDo.Ui/Converters/IconKeyConverter.cs
new file mode 100644
index 0000000..39a7c6d
--- /dev/null
+++ b/src/ClaudeDo.Ui/Converters/IconKeyConverter.cs
@@ -0,0 +1,28 @@
+using System.Globalization;
+using Avalonia;
+using Avalonia.Data.Converters;
+using Avalonia.Media;
+
+namespace ClaudeDo.Ui.Converters;
+
+///
+/// Converts an icon key string (e.g. "Sun") to the matching StreamGeometry resource "Icon.Sun".
+///
+public class IconKeyConverter : IValueConverter
+{
+ public static IconKeyConverter Instance { get; } = new();
+
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ if (value is not string key || string.IsNullOrEmpty(key))
+ return null;
+
+ var resourceKey = $"Icon.{key}";
+ if (Application.Current?.TryGetResource(resourceKey, null, out var res) == true)
+ return res as StreamGeometry;
+ return null;
+ }
+
+ public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ => throw new NotSupportedException();
+}
diff --git a/src/ClaudeDo.Ui/Converters/UpperCaseConverter.cs b/src/ClaudeDo.Ui/Converters/UpperCaseConverter.cs
new file mode 100644
index 0000000..a9f49de
--- /dev/null
+++ b/src/ClaudeDo.Ui/Converters/UpperCaseConverter.cs
@@ -0,0 +1,15 @@
+using System.Globalization;
+using Avalonia.Data.Converters;
+
+namespace ClaudeDo.Ui.Converters;
+
+public class UpperCaseConverter : IValueConverter
+{
+ public static UpperCaseConverter Instance { get; } = new();
+
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ => value?.ToString()?.ToUpperInvariant();
+
+ public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ => throw new NotSupportedException();
+}
diff --git a/src/ClaudeDo.Ui/Design/IslandStyles.axaml b/src/ClaudeDo.Ui/Design/IslandStyles.axaml
index 9a39539..cc83799 100644
--- a/src/ClaudeDo.Ui/Design/IslandStyles.axaml
+++ b/src/ClaudeDo.Ui/Design/IslandStyles.axaml
@@ -24,6 +24,60 @@
M5 5l14 14M19 5L5 19
M3 3 h18 v18 h-18 z M6 12 l4 4 8-8
+
+
+
+
+
+
+
+ M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8z M12 3v2M12 19v2M3 12h2M19 12h2M5.5 5.5l1.4 1.4M17.1 17.1l1.4 1.4M5.5 18.5l1.4-1.4M17.1 6.9l1.4-1.4
+
+
+ M3 12h4l2-6 4 12 2-8 2 2h4
+
+
+ M12 3.5l2.6 5.3 5.8.8-4.2 4.1 1 5.8-5.2-2.7-5.2 2.7 1-5.8-4.2-4.1 5.8-.8z
+
+
+ M3.5 5a2 2 0 0 1 2-2h13a2 2 0 0 1 2 2v15a2 2 0 0 1-2 2h-13a2 2 0 0 1-2-2z M3.5 10h17M8 3v4M16 3v4
+
+
+ M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12z M12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6z
+
+
+ M3 13h5l1 2h6l1-2h5M3 13l3-8h12l3 8v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z
+
+
+ M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z
+
+
+ M11 4a7 7 0 1 0 0 14A7 7 0 0 0 11 4z M20 20l-3.5-3.5
+
+
+ M12 5v14M5 12h14
+
+
+ M5 12m-1.3 0a1.3 1.3 0 1 0 2.6 0 1.3 1.3 0 1 0-2.6 0 M12 12m-1.3 0a1.3 1.3 0 1 0 2.6 0 1.3 1.3 0 1 0-2.6 0 M19 12m-1.3 0a1.3 1.3 0 1 0 2.6 0 1.3 1.3 0 1 0-2.6 0
+
+
+ M6 3a2 2 0 1 0 0 4 2 2 0 0 0 0-4z M6 19a2 2 0 1 0 0 4 2 2 0 0 0 0-4z M18 5a2 2 0 1 0 0 4 2 2 0 0 0 0-4z M6 7v10M6 13c0-4 12-2 12-4
+
+
+ M8 8h12a1.5 1.5 0 0 1 1.5 1.5v12A1.5 1.5 0 0 1 20 23H8a1.5 1.5 0 0 1-1.5-1.5v-12A1.5 1.5 0 0 1 8 8z M16 8V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3
+
+
+ M4 7h16M10 11v6M14 11v6M5 7l1 13a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1l1-13M9 7V4h6v3
+
+
+ M7 4v16M7 20l-3-3M7 4l-3 3M14 8h7M14 12h5M14 16h3
+
+
+ M6 6l12 12M18 6L6 18
+
+
+ M4 12l5 5 11-11
+
@@ -346,6 +400,7 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/ClaudeDo.Ui/ViewModels/Islands/ListNavItemViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Islands/ListNavItemViewModel.cs
index b524b67..f6969c1 100644
--- a/src/ClaudeDo.Ui/ViewModels/Islands/ListNavItemViewModel.cs
+++ b/src/ClaudeDo.Ui/ViewModels/Islands/ListNavItemViewModel.cs
@@ -10,4 +10,5 @@ public sealed partial class ListNavItemViewModel : ViewModelBase
[ObservableProperty] private int _count;
[ObservableProperty] private bool _isActive;
public string? IconKey { get; init; }
+ public string? DotColorKey { get; init; }
}
diff --git a/src/ClaudeDo.Ui/ViewModels/Islands/ListsIslandViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Islands/ListsIslandViewModel.cs
index 59614c2..19a4625 100644
--- a/src/ClaudeDo.Ui/ViewModels/Islands/ListsIslandViewModel.cs
+++ b/src/ClaudeDo.Ui/ViewModels/Islands/ListsIslandViewModel.cs
@@ -18,30 +18,63 @@ public sealed partial class ListsIslandViewModel : ViewModelBase
public void RequestFocusSearch() => FocusSearchRequested?.Invoke(this, EventArgs.Empty);
public ObservableCollection Items { get; } = new();
+ public ObservableCollection SmartLists { get; } = new();
+ public ObservableCollection UserLists { get; } = new();
[ObservableProperty] private string _searchText = "";
[ObservableProperty] private ListNavItemViewModel? _selectedList;
+ public string UserName { get; } = Environment.UserName;
+ public string MachineName { get; } = Environment.MachineName;
+ public string UserInitials { get; }
+
public ListsIslandViewModel(IDbContextFactory dbFactory)
{
_dbFactory = dbFactory;
+ var parts = Environment.UserName.Split('.', '_', '-', ' ');
+ UserInitials = parts.Length >= 2
+ ? $"{parts[0][0]}{parts[1][0]}".ToUpperInvariant()
+ : Environment.UserName.Length >= 2
+ ? Environment.UserName[..2].ToUpperInvariant()
+ : Environment.UserName.ToUpperInvariant();
}
public async Task LoadAsync(CancellationToken ct = default)
{
Items.Clear();
- Items.Add(new ListNavItemViewModel { Id = "smart:my-day", Name = "My Day", Kind = ListKind.Smart, IconKey = "Sun" });
- Items.Add(new ListNavItemViewModel { Id = "smart:important", Name = "Important", Kind = ListKind.Smart, IconKey = "Star" });
- Items.Add(new ListNavItemViewModel { Id = "smart:planned", Name = "Planned", Kind = ListKind.Smart, IconKey = "Calendar" });
- Items.Add(new ListNavItemViewModel { Id = "virtual:running", Name = "Running", Kind = ListKind.Virtual, IconKey = "Pulse" });
- Items.Add(new ListNavItemViewModel { Id = "virtual:review", Name = "Review", Kind = ListKind.Virtual, IconKey = "Eye" });
+ SmartLists.Clear();
+ UserLists.Clear();
+
+ var smart = new[]
+ {
+ new ListNavItemViewModel { Id = "smart:my-day", Name = "My Day", Kind = ListKind.Smart, IconKey = "Sun" },
+ new ListNavItemViewModel { Id = "smart:important", Name = "Important", Kind = ListKind.Smart, IconKey = "Star" },
+ new ListNavItemViewModel { Id = "smart:planned", Name = "Planned", Kind = ListKind.Smart, IconKey = "Calendar" },
+ new ListNavItemViewModel { Id = "virtual:running", Name = "Running", Kind = ListKind.Virtual, IconKey = "Activity" },
+ new ListNavItemViewModel { Id = "virtual:review", Name = "Review", Kind = ListKind.Virtual, IconKey = "Eye" },
+ };
+ foreach (var s in smart) { Items.Add(s); SmartLists.Add(s); }
await using var ctx = await _dbFactory.CreateDbContextAsync(ct);
var lists = new ListRepository(ctx);
var seedNames = new HashSet(new[] { "My Day", "Important", "Planned" });
+ var dotColors = new[] { "Moss", "Peat", "Sage" };
+ int idx = 0;
foreach (var l in await lists.GetAllAsync(ct))
- if (!seedNames.Contains(l.Name))
- Items.Add(new ListNavItemViewModel { Id = $"user:{l.Id}", Name = l.Name, Kind = ListKind.User, IconKey = "Folder" });
+ {
+ if (seedNames.Contains(l.Name)) continue;
+ var item = new ListNavItemViewModel
+ {
+ Id = $"user:{l.Id}",
+ Name = l.Name,
+ Kind = ListKind.User,
+ IconKey = "Folder",
+ DotColorKey = dotColors[idx % dotColors.Length],
+ };
+ Items.Add(item);
+ UserLists.Add(item);
+ idx++;
+ }
await RefreshCountsAsync(ct);
SelectedList = Items.FirstOrDefault();