feat(ui): coloured dots for icon-less lists, guide line under chevron
Lists without an icon show a dot in a stable identity colour (same palette/hash as Mission Control tiles); "Dot" is the new picker default.
This commit is contained in:
@@ -54,6 +54,9 @@
|
|||||||
The old stroke version filled into a solid slab. -->
|
The old stroke version filled into a solid slab. -->
|
||||||
<StreamGeometry x:Key="Icon.Inbox">F0 M3 5 H21 V19 H3 Z M5 7 H19 V12 H5 Z M5 14 H19 V17 H5 Z M11 7.6 H13 V9.4 H15 L12 12 L9 9.4 H11 Z</StreamGeometry>
|
<StreamGeometry x:Key="Icon.Inbox">F0 M3 5 H21 V19 H3 Z M5 7 H19 V12 H5 Z M5 14 H19 V17 H5 Z M11 7.6 H13 V9.4 H15 L12 12 L9 9.4 H11 Z</StreamGeometry>
|
||||||
|
|
||||||
|
<!-- Icon.Dot — picker preview for "no icon"; the sidebar renders it as a coloured Ellipse instead. -->
|
||||||
|
<StreamGeometry x:Key="Icon.Dot">M12 7a5 5 0 1 0 0 10a5 5 0 1 0 0-10z</StreamGeometry>
|
||||||
|
|
||||||
<!-- Icon.Folder -->
|
<!-- Icon.Folder -->
|
||||||
<StreamGeometry x:Key="Icon.Folder">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</StreamGeometry>
|
<StreamGeometry x:Key="Icon.Folder">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</StreamGeometry>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using ClaudeDo.Data.Models;
|
using ClaudeDo.Data.Models;
|
||||||
|
using ClaudeDo.Ui.ViewModels.MissionControl;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
namespace ClaudeDo.Ui.ViewModels.Islands;
|
namespace ClaudeDo.Ui.ViewModels.Islands;
|
||||||
@@ -30,7 +31,15 @@ public sealed partial class ListNavItemViewModel : ViewModelBase
|
|||||||
// DropHintAbove/Below, which mark an insertion point for list reordering.
|
// DropHintAbove/Below, which mark an insertion point for list reordering.
|
||||||
[ObservableProperty] private bool _isTaskDropTarget;
|
[ObservableProperty] private bool _isTaskDropTarget;
|
||||||
// Settable (not init) so a list-settings icon change refreshes the row in place.
|
// Settable (not init) so a list-settings icon change refreshes the row in place.
|
||||||
[ObservableProperty] private string? _iconKey;
|
[ObservableProperty]
|
||||||
|
[NotifyPropertyChangedFor(nameof(IsDot))]
|
||||||
|
private string? _iconKey;
|
||||||
|
|
||||||
|
/// <summary>No icon picked — the row shows a coloured dot instead of a glyph.</summary>
|
||||||
|
public bool IsDot => IconKey is null or ListsIslandViewModel.ListIcon;
|
||||||
|
|
||||||
|
/// <summary>Identity colour of the dot, stable per list id across restarts.</summary>
|
||||||
|
public string DotColorKey => $"Identity{ConPtyPaneViewModel.StableIndex(Id, 6) + 1}";
|
||||||
// The sidebar folder this row sits in (ListEntity.FolderId); null = top level.
|
// The sidebar folder this row sits in (ListEntity.FolderId); null = top level.
|
||||||
[ObservableProperty] private string? _folderId;
|
[ObservableProperty] private string? _folderId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ public sealed partial class ConPtyPaneViewModel : ViewModelBase, IDisposable
|
|||||||
|
|
||||||
// FNV-1a, not string.GetHashCode: the latter is randomized per process, so the same task would
|
// FNV-1a, not string.GetHashCode: the latter is randomized per process, so the same task would
|
||||||
// change colour on every app start.
|
// change colour on every app start.
|
||||||
private static int StableIndex(string value, int buckets)
|
/// <summary>Process-stable bucket for <paramref name="value"/>; also colours the sidebar list dots.</summary>
|
||||||
|
internal static int StableIndex(string value, int buckets)
|
||||||
{
|
{
|
||||||
uint hash = 2166136261;
|
uint hash = 2166136261;
|
||||||
foreach (var c in value)
|
foreach (var c in value)
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase
|
|||||||
|
|
||||||
public ObservableCollection<string> CommitTypeOptions { get; } = new(CommitTypeRegistry.Types);
|
public ObservableCollection<string> CommitTypeOptions { get; } = new(CommitTypeRegistry.Types);
|
||||||
|
|
||||||
public const string DefaultIcon = "Text";
|
public const string DefaultIcon = "Dot";
|
||||||
|
|
||||||
/// <summary>Default icon for folders, kept apart from <see cref="DefaultIcon"/> so lists never look like folders.</summary>
|
/// <summary>Default icon for folders, kept apart from <see cref="DefaultIcon"/> so lists never look like folders.</summary>
|
||||||
public const string DefaultFolderIcon = "Folder";
|
public const string DefaultFolderIcon = "Folder";
|
||||||
@@ -123,7 +123,7 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase
|
|||||||
public static readonly string[] IconOptions =
|
public static readonly string[] IconOptions =
|
||||||
[
|
[
|
||||||
// general
|
// general
|
||||||
"Folder", "Star", "Sun", "Calendar", "Clock", "Inbox", "Mail", "Text", "Search", "Grid",
|
"Dot", "Folder", "Star", "Sun", "Calendar", "Clock", "Inbox", "Mail", "Text", "Search", "Grid",
|
||||||
// dev
|
// dev
|
||||||
"Code", "Terminal", "Database", "Server", "Chip", "Bolt", "Wand", "Flask", "Shield",
|
"Code", "Terminal", "Database", "Server", "Chip", "Bolt", "Wand", "Flask", "Shield",
|
||||||
// status / flow
|
// status / flow
|
||||||
|
|||||||
@@ -73,9 +73,12 @@
|
|||||||
</Border>
|
</Border>
|
||||||
<!-- Icon (list-settings → Icon tab; "Folder" when unset) -->
|
<!-- Icon (list-settings → Icon tab; "Folder" when unset) -->
|
||||||
<PathIcon Grid.Column="1" Classes="list-icon"
|
<PathIcon Grid.Column="1" Classes="list-icon"
|
||||||
Width="14" Height="14"
|
Width="14" Height="14" IsVisible="{Binding !IsDot}"
|
||||||
Data="{Binding IconKey, Converter={StaticResource IconKey}}"
|
Data="{Binding IconKey, Converter={StaticResource IconKey}}"
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
<Ellipse Grid.Column="1" Width="8" Height="8" IsVisible="{Binding IsDot}"
|
||||||
|
Fill="{Binding DotColorKey, Converter={StaticResource DotBrush}}"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
<!-- Name -->
|
<!-- Name -->
|
||||||
<TextBlock Grid.Column="2" Classes="list-label"
|
<TextBlock Grid.Column="2" Classes="list-label"
|
||||||
Text="{Binding Name}" ToolTip.Tip="{Binding Name}"
|
Text="{Binding Name}" ToolTip.Tip="{Binding Name}"
|
||||||
@@ -141,10 +144,9 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- Guide line drops from under the folder icon's centre (padding 10 + chevron 12 + half
|
<!-- Guide line drops from under the chevron's centre (padding 10 + half the 9px glyph). -->
|
||||||
of the 20px icon slot); children indent one icon slot past it. -->
|
|
||||||
<Panel IsVisible="{Binding !IsCollapsed}">
|
<Panel IsVisible="{Binding !IsCollapsed}">
|
||||||
<Border Width="1" HorizontalAlignment="Left" Margin="31,0,0,4"
|
<Border Width="1" HorizontalAlignment="Left" Margin="14,0,0,4"
|
||||||
Background="{DynamicResource LineBrush}" IsHitTestVisible="False"/>
|
Background="{DynamicResource LineBrush}" IsHitTestVisible="False"/>
|
||||||
<ItemsControl ItemsSource="{Binding Children}" Margin="20,0,0,0"/>
|
<ItemsControl ItemsSource="{Binding Children}" Margin="20,0,0,0"/>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|||||||
Reference in New Issue
Block a user