diff --git a/src/FlowForge.UI/App.axaml b/src/FlowForge.UI/App.axaml index d6dbbc5..690bf90 100644 --- a/src/FlowForge.UI/App.axaml +++ b/src/FlowForge.UI/App.axaml @@ -6,7 +6,7 @@ - + diff --git a/src/FlowForge.UI/MainWindow.axaml b/src/FlowForge.UI/MainWindow.axaml index 0ca7d19..fd57708 100644 --- a/src/FlowForge.UI/MainWindow.axaml +++ b/src/FlowForge.UI/MainWindow.axaml @@ -12,11 +12,11 @@ Width="1200" Height="700" MinWidth="800" MinHeight="500" Icon="avares://FlowForge.UI/Assets/icon.png" - FontSize="14" - Background="{DynamicResource MidnightBg}" + FontSize="13" + Background="{DynamicResource ForgeBg}" KeyDown="OnKeyDown"> - + @@ -25,18 +25,18 @@ @@ -49,7 +49,7 @@ diff --git a/src/FlowForge.UI/Themes/MidnightTheme.axaml b/src/FlowForge.UI/Themes/MidnightTheme.axaml deleted file mode 100644 index 34bab9c..0000000 --- a/src/FlowForge.UI/Themes/MidnightTheme.axaml +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - - - #0D1117 - #161B22 - #1C2128 - #21262D - - - #30363D - #484F58 - - - #E6EDF3 - #8B949E - #484F58 - - - #58A6FF - #58A6FF - #3FB950 - #D29922 - - - #3FB950 - #F85149 - #D29922 - #58A6FF - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/FlowForge.UI/Themes/MoltenForgeTheme.axaml b/src/FlowForge.UI/Themes/MoltenForgeTheme.axaml new file mode 100644 index 0000000..57534a0 --- /dev/null +++ b/src/FlowForge.UI/Themes/MoltenForgeTheme.axaml @@ -0,0 +1,325 @@ + + + + + + + + + + + + + #08070a + #0e0c11 + #141218 + #1c1820 + #252029 + #2e2735 + + + #2a2230 + #3d2f48 + + + #ede6f0 + #9a8da8 + #564a62 + + + #e8932f + #f5a623 + + + #5bb8f5 + #5ce0a0 + #e8932f + + + #5ce0a0 + #f2716a + #f5c842 + + + #d4612c + #f5c842 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #f5f2ee + #eee9e3 + #faf8f5 + #ffffff + #f0ece6 + #e8e3db + + + #ddd5ca + #c9bfb2 + + + #1c1714 + #6b5e52 + #a89c8e + + + #d07a18 + #e8932f + + + #2680c2 + #1a9a6c + #d07a18 + + + #1a9a6c + #d44040 + #c88a15 + + + #c05820 + #e8b530 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/FlowForge.UI/ViewModels/MainWindowViewModel.cs b/src/FlowForge.UI/ViewModels/MainWindowViewModel.cs index 3a44d60..5b4269f 100644 --- a/src/FlowForge.UI/ViewModels/MainWindowViewModel.cs +++ b/src/FlowForge.UI/ViewModels/MainWindowViewModel.cs @@ -370,6 +370,18 @@ private void Cancel() _cts?.Cancel(); } + [RelayCommand] + private void Undo() + { + Editor.Undo(); + } + + [RelayCommand] + private void Redo() + { + Editor.Redo(); + } + [RelayCommand] private void LoadTemplate(string templateId) { diff --git a/src/FlowForge.UI/ViewModels/NodeLibraryGroupViewModel.cs b/src/FlowForge.UI/ViewModels/NodeLibraryGroupViewModel.cs index e036265..1cbe1ae 100644 --- a/src/FlowForge.UI/ViewModels/NodeLibraryGroupViewModel.cs +++ b/src/FlowForge.UI/ViewModels/NodeLibraryGroupViewModel.cs @@ -1,15 +1,18 @@ using System.Collections.ObjectModel; +using Avalonia.Media; namespace FlowForge.UI.ViewModels; public class NodeLibraryGroupViewModel : ViewModelBase { public string Category { get; } + public IBrush CategoryBrush { get; } public ObservableCollection Items { get; } - public NodeLibraryGroupViewModel(string category, ObservableCollection items) + public NodeLibraryGroupViewModel(string category, ObservableCollection items, IBrush categoryBrush) { Category = category; Items = items; + CategoryBrush = categoryBrush; } } diff --git a/src/FlowForge.UI/ViewModels/NodeLibraryItemViewModel.cs b/src/FlowForge.UI/ViewModels/NodeLibraryItemViewModel.cs index a4884eb..e0797ec 100644 --- a/src/FlowForge.UI/ViewModels/NodeLibraryItemViewModel.cs +++ b/src/FlowForge.UI/ViewModels/NodeLibraryItemViewModel.cs @@ -1,13 +1,21 @@ +using Avalonia.Media; + namespace FlowForge.UI.ViewModels; public class NodeLibraryItemViewModel : ViewModelBase { public string TypeKey { get; } public string DisplayName { get; } + public string Icon { get; } + public IBrush IconBackground { get; } + public IBrush IconForeground { get; } - public NodeLibraryItemViewModel(string typeKey, string displayName) + public NodeLibraryItemViewModel(string typeKey, string displayName, string icon, IBrush iconBackground, IBrush iconForeground) { TypeKey = typeKey; DisplayName = displayName; + Icon = icon; + IconBackground = iconBackground; + IconForeground = iconForeground; } } diff --git a/src/FlowForge.UI/ViewModels/NodeLibraryViewModel.cs b/src/FlowForge.UI/ViewModels/NodeLibraryViewModel.cs index 418e970..1ca5ea3 100644 --- a/src/FlowForge.UI/ViewModels/NodeLibraryViewModel.cs +++ b/src/FlowForge.UI/ViewModels/NodeLibraryViewModel.cs @@ -2,6 +2,9 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; using CommunityToolkit.Mvvm.ComponentModel; using FlowForge.Core.Execution; @@ -16,6 +19,21 @@ public partial class NodeLibraryViewModel : ViewModelBase ["Output"] = "Save To" }; + private static readonly Dictionary NodeIcons = new() + { + ["FolderInput"] = "\U0001F4C1", + ["RenamePattern"] = "\u270E", + ["RenameRegex"] = ".*", + ["RenameAddAffix"] = "+a", + ["Filter"] = "\U0001F50D", + ["Sort"] = "\u21C5", + ["ImageResize"] = "\U0001F4F8", + ["ImageConvert"] = "\U0001F3A8", + ["ImageCompress"] = "\U0001F4E6", + ["MetadataExtract"] = "\U0001F4C4", + ["FolderOutput"] = "\U0001F4E5", + }; + private List _allGroups = new(); [ObservableProperty] @@ -34,6 +52,7 @@ public void Initialize(NodeRegistry registry) Groups.Clear(); Dictionary> categoryItems = new(); + Dictionary categoryKeys = new(); foreach (string typeKey in registry.GetRegisteredTypeKeys()) { @@ -45,24 +64,58 @@ public void Initialize(NodeRegistry registry) { existingItems = new List(); categoryItems[categoryName] = existingItems; + categoryKeys[categoryName] = category; } - existingItems.Add(new NodeLibraryItemViewModel(typeKey, displayName)); + string icon = NodeIcons.GetValueOrDefault(typeKey, "\u2699"); + var brushes = GetCategoryBrushes(category); + existingItems.Add(new NodeLibraryItemViewModel(typeKey, displayName, icon, brushes.IconBg, brushes.IconFg)); } - // Add groups in canonical order: Input, Process, Save To string[] orderedCategories = ["Input", "Process", "Save To"]; foreach (string cat in orderedCategories) { if (categoryItems.TryGetValue(cat, out List? items)) { - NodeLibraryGroupViewModel group = new(cat, new ObservableCollection(items)); + IBrush categoryBrush = GetCategoryHeaderBrush(categoryKeys[cat]); + NodeLibraryGroupViewModel group = new(cat, new ObservableCollection(items), categoryBrush); _allGroups.Add(group); Groups.Add(group); } } } + private static (IBrush IconBg, IBrush IconFg) GetCategoryBrushes(NodeCategory category) + { + return category switch + { + NodeCategory.Source => (GetBrush("ForgeSourceDim", "#145bb8f5"), GetBrush("ForgeSource", "#5bb8f5")), + NodeCategory.Transform => (GetBrush("ForgeTransformDim", "#145ce0a0"), GetBrush("ForgeTransform", "#5ce0a0")), + NodeCategory.Output => (GetBrush("ForgeOutputDim", "#14e8932f"), GetBrush("ForgeOutput", "#e8932f")), + _ => (GetBrush("ForgeElevated", "#252029"), GetBrush("ForgeTextMuted", "#564a62")) + }; + } + + private static IBrush GetCategoryHeaderBrush(NodeCategory category) + { + return category switch + { + NodeCategory.Source => GetBrush("ForgeSource", "#5bb8f5"), + NodeCategory.Transform => GetBrush("ForgeTransform", "#5ce0a0"), + NodeCategory.Output => GetBrush("ForgeOutput", "#e8932f"), + _ => GetBrush("ForgeTextMuted", "#564a62") + }; + } + + private static IBrush GetBrush(string key, string fallback) + { + if (Application.Current?.TryFindResource(key, Application.Current.ActualThemeVariant, out object? resource) == true && resource is IBrush brush) + { + return brush; + } + return new SolidColorBrush(Color.Parse(fallback)); + } + private void FilterItems() { Groups.Clear(); @@ -85,7 +138,8 @@ private void FilterItems() { Groups.Add(new NodeLibraryGroupViewModel( group.Category, - new ObservableCollection(filtered))); + new ObservableCollection(filtered), + group.CategoryBrush)); } } } diff --git a/src/FlowForge.UI/ViewModels/PipelineConnectorViewModel.cs b/src/FlowForge.UI/ViewModels/PipelineConnectorViewModel.cs index cac2f0b..fc1107b 100644 --- a/src/FlowForge.UI/ViewModels/PipelineConnectorViewModel.cs +++ b/src/FlowForge.UI/ViewModels/PipelineConnectorViewModel.cs @@ -1,4 +1,5 @@ using Avalonia; +using Avalonia.Media; using CommunityToolkit.Mvvm.ComponentModel; namespace FlowForge.UI.ViewModels; @@ -18,6 +19,9 @@ public partial class PipelineConnectorViewModel : ViewModelBase public PipelineNodeViewModel Node { get; } + /// Category color for this connector (from parent node). + public IBrush ConnectorBrush => Node.CategoryBrush; + public PipelineConnectorViewModel(string title, bool isInput, PipelineNodeViewModel node) { _title = title; diff --git a/src/FlowForge.UI/ViewModels/PipelineNodeViewModel.cs b/src/FlowForge.UI/ViewModels/PipelineNodeViewModel.cs index ce4a7c2..383aba2 100644 --- a/src/FlowForge.UI/ViewModels/PipelineNodeViewModel.cs +++ b/src/FlowForge.UI/ViewModels/PipelineNodeViewModel.cs @@ -22,9 +22,75 @@ private static IBrush GetBrush(string key, string fallback) return new SolidColorBrush(Color.Parse(fallback)); } - private static readonly IBrush SourceBrush = GetBrush("MidnightSource", "#58A6FF"); - private static readonly IBrush TransformBrush = GetBrush("MidnightTransform", "#3FB950"); - private static readonly IBrush OutputBrush = GetBrush("MidnightOutput", "#D29922"); + // Category solid colors (for text, connectors) + private static readonly IBrush SourceBrush = GetBrush("ForgeSource", "#5bb8f5"); + private static readonly IBrush TransformBrush = GetBrush("ForgeTransform", "#5ce0a0"); + private static readonly IBrush OutputBrush = GetBrush("ForgeOutput", "#e8932f"); + + // Category border colors (~20% opacity) + private static readonly IBrush SourceBorderBrush = new SolidColorBrush(Color.Parse("#335bb8f5")); + private static readonly IBrush TransformBorderBrush = new SolidColorBrush(Color.Parse("#335ce0a0")); + private static readonly IBrush OutputBorderBrush = new SolidColorBrush(Color.Parse("#33e8932f")); + + // Category header gradients (subtle, not solid blocks) + private static readonly IBrush SourceHeaderGradient = new LinearGradientBrush + { + StartPoint = new RelativePoint(0, 0.5, RelativeUnit.Relative), + EndPoint = new RelativePoint(1, 0.5, RelativeUnit.Relative), + GradientStops = { new GradientStop(Color.Parse("#0D5bb8f5"), 0), new GradientStop(Colors.Transparent, 1) } + }; + + private static readonly IBrush TransformHeaderGradient = new LinearGradientBrush + { + StartPoint = new RelativePoint(0, 0.5, RelativeUnit.Relative), + EndPoint = new RelativePoint(1, 0.5, RelativeUnit.Relative), + GradientStops = { new GradientStop(Color.Parse("#0D5ce0a0"), 0), new GradientStop(Colors.Transparent, 1) } + }; + + private static readonly IBrush OutputHeaderGradient = new LinearGradientBrush + { + StartPoint = new RelativePoint(0, 0.5, RelativeUnit.Relative), + EndPoint = new RelativePoint(1, 0.5, RelativeUnit.Relative), + GradientStops = { new GradientStop(Color.Parse("#0Ee8932f"), 0), new GradientStop(Colors.Transparent, 1) } + }; + + // Category-tinted gradient backgrounds + private static readonly IBrush SourceNodeBg = new LinearGradientBrush + { + StartPoint = new RelativePoint(0.5, 0, RelativeUnit.Relative), + EndPoint = new RelativePoint(0.5, 1, RelativeUnit.Relative), + GradientStops = { new GradientStop(Color.Parse("#0F5bb8f5"), 0), new GradientStop(Color.Parse("#1c1820"), 0.5) } + }; + + private static readonly IBrush TransformNodeBg = new LinearGradientBrush + { + StartPoint = new RelativePoint(0.5, 0, RelativeUnit.Relative), + EndPoint = new RelativePoint(0.5, 1, RelativeUnit.Relative), + GradientStops = { new GradientStop(Color.Parse("#0F5ce0a0"), 0), new GradientStop(Color.Parse("#1c1820"), 0.5) } + }; + + private static readonly IBrush OutputNodeBg = new LinearGradientBrush + { + StartPoint = new RelativePoint(0.5, 0, RelativeUnit.Relative), + EndPoint = new RelativePoint(0.5, 1, RelativeUnit.Relative), + GradientStops = { new GradientStop(Color.Parse("#14e8932f"), 0), new GradientStop(Color.Parse("#1c1820"), 0.5) } + }; + + // Icon emoji per node type + private static readonly Dictionary NodeIcons = new() + { + ["FolderInput"] = "\U0001F4C1", + ["RenamePattern"] = "\u270E", + ["RenameRegex"] = ".*", + ["RenameAddAffix"] = "+a", + ["Filter"] = "\U0001F50D", + ["Sort"] = "\u21C5", + ["ImageResize"] = "\U0001F4F8", + ["ImageConvert"] = "\U0001F3A8", + ["ImageCompress"] = "\U0001F4E6", + ["MetadataExtract"] = "\U0001F4C4", + ["FolderOutput"] = "\U0001F4E5", + }; [ObservableProperty] private Point _location; @@ -35,12 +101,25 @@ private static IBrush GetBrush(string key, string fallback) public Guid Id { get; } public string TypeKey { get; } public string Title { get; } + public string IconEmoji { get; } + public string ConfigPreview { get; } public NodeCategory Category { get; } public ObservableCollection Input { get; } = new(); public ObservableCollection Output { get; } = new(); public Dictionary Config { get; } + + /// Solid category color for text and connector fills. + public IBrush CategoryBrush { get; } + + /// Subtle gradient for the node header area. public IBrush HeaderBrush { get; } + /// 20% opacity category color for the node border. + public IBrush NodeBorderBrush { get; } + + /// Top-down gradient with subtle category tint. + public IBrush NodeBackground { get; } + public PipelineNodeViewModel(NodeDefinition definition, NodeRegistry registry) { Id = definition.Id; @@ -48,9 +127,11 @@ public PipelineNodeViewModel(NodeDefinition definition, NodeRegistry registry) Title = registry.GetDisplayName(definition.TypeKey); Category = registry.GetCategoryForTypeKey(definition.TypeKey); Config = definition.Config; + IconEmoji = NodeIcons.GetValueOrDefault(definition.TypeKey, "\u2699"); + ConfigPreview = BuildConfigPreview(definition.Config); _location = new Point(definition.Position.X, definition.Position.Y); - HeaderBrush = Category switch + CategoryBrush = Category switch { NodeCategory.Source => SourceBrush, NodeCategory.Transform => TransformBrush, @@ -58,6 +139,30 @@ public PipelineNodeViewModel(NodeDefinition definition, NodeRegistry registry) _ => TransformBrush }; + HeaderBrush = Category switch + { + NodeCategory.Source => SourceHeaderGradient, + NodeCategory.Transform => TransformHeaderGradient, + NodeCategory.Output => OutputHeaderGradient, + _ => TransformHeaderGradient + }; + + NodeBorderBrush = Category switch + { + NodeCategory.Source => SourceBorderBrush, + NodeCategory.Transform => TransformBorderBrush, + NodeCategory.Output => OutputBorderBrush, + _ => TransformBorderBrush + }; + + NodeBackground = Category switch + { + NodeCategory.Source => SourceNodeBg, + NodeCategory.Transform => TransformNodeBg, + NodeCategory.Output => OutputNodeBg, + _ => TransformNodeBg + }; + // Source nodes have no input; output nodes have no output if (Category != NodeCategory.Source) { @@ -69,4 +174,22 @@ public PipelineNodeViewModel(NodeDefinition definition, NodeRegistry registry) Output.Add(new PipelineConnectorViewModel("Out", isInput: false, this)); } } + + private static string BuildConfigPreview(Dictionary config) + { + // Show the first string config value as a preview (path, pattern, etc.) + foreach (KeyValuePair kvp in config) + { + if (kvp.Value.ValueKind == JsonValueKind.String) + { + string val = kvp.Value.GetString() ?? string.Empty; + if (!string.IsNullOrWhiteSpace(val)) + { + return val; + } + } + } + + return string.Empty; + } } diff --git a/src/FlowForge.UI/ViewModels/PropertiesViewModel.cs b/src/FlowForge.UI/ViewModels/PropertiesViewModel.cs index 861be95..8d79255 100644 --- a/src/FlowForge.UI/ViewModels/PropertiesViewModel.cs +++ b/src/FlowForge.UI/ViewModels/PropertiesViewModel.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; using CommunityToolkit.Mvvm.ComponentModel; using FlowForge.Core.Execution; using FlowForge.Core.Nodes.Base; @@ -16,6 +19,12 @@ public partial class PropertiesViewModel : ViewModelBase [ObservableProperty] private bool _hasSelection; + [ObservableProperty] + private IBrush? _badgeForeground; + + [ObservableProperty] + private IBrush? _badgeBackground; + public ObservableCollection Fields { get; } = new(); public void LoadNode(PipelineNodeViewModel? node, NodeRegistry registry, Action? onConfigChanged = null) @@ -26,11 +35,21 @@ public void LoadNode(PipelineNodeViewModel? node, NodeRegistry registry, Action< { SelectedNodeTitle = null; HasSelection = false; + BadgeForeground = null; + BadgeBackground = null; return; } SelectedNodeTitle = node.Title; HasSelection = true; + BadgeForeground = node.HeaderBrush; + BadgeBackground = node.Category switch + { + NodeCategory.Source => GetBrush("ForgeSourceDim", "#265bb8f5"), + NodeCategory.Transform => GetBrush("ForgeTransformDim", "#265ce0a0"), + NodeCategory.Output => GetBrush("ForgeOutputDim", "#26e8932f"), + _ => GetBrush("ForgeElevated", "#252029") + }; IReadOnlyList schema = registry.GetConfigSchema(node.TypeKey); foreach (ConfigField field in schema) @@ -38,4 +57,13 @@ public void LoadNode(PipelineNodeViewModel? node, NodeRegistry registry, Action< Fields.Add(new ConfigFieldViewModel(field, node.Config, onConfigChanged)); } } + + private static IBrush GetBrush(string key, string fallback) + { + if (Application.Current?.TryFindResource(key, Application.Current.ActualThemeVariant, out object? resource) == true && resource is IBrush brush) + { + return brush; + } + return new SolidColorBrush(Color.Parse(fallback)); + } } diff --git a/src/FlowForge.UI/Views/CanvasView.axaml b/src/FlowForge.UI/Views/CanvasView.axaml index 71b91dc..cd91d4c 100644 --- a/src/FlowForge.UI/Views/CanvasView.axaml +++ b/src/FlowForge.UI/Views/CanvasView.axaml @@ -4,7 +4,6 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="using:FlowForge.UI.ViewModels" xmlns:nodify="using:Nodify.Avalonia" - xmlns:nodifyNodes="using:Nodify.Avalonia.Nodes" xmlns:nodifyConn="using:Nodify.Avalonia.Connections" mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="400" @@ -18,43 +17,133 @@ PendingConnection="{Binding PendingConnection}" ItemsDragStartedCommand="{Binding ItemsDragStartedCommand}" ItemsDragCompletedCommand="{Binding ItemsDragCompletedCommand}" - Background="{DynamicResource MidnightBg}" + Background="{DynamicResource ForgeBg}" + GridCellSize="20" DragDrop.AllowDrop="True"> + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Target="{Binding Target.Anchor}" + Stroke="{DynamicResource ForgeAccent}" + StrokeThickness="2" /> @@ -73,54 +162,82 @@ + + + - + Spacing="20" + MaxWidth="400"> - - - - + + + + + + + + + + - - + + + + diff --git a/src/FlowForge.UI/Views/ConfigFieldTemplateSelector.cs b/src/FlowForge.UI/Views/ConfigFieldTemplateSelector.cs index cf5c86f..4f882b0 100644 --- a/src/FlowForge.UI/Views/ConfigFieldTemplateSelector.cs +++ b/src/FlowForge.UI/Views/ConfigFieldTemplateSelector.cs @@ -26,10 +26,10 @@ public Control Build(object? param) Border cardBorder = new() { - Background = new Avalonia.Media.SolidColorBrush(Avalonia.Media.Color.Parse("#1C2128")), - BorderBrush = new Avalonia.Media.SolidColorBrush(Avalonia.Media.Color.Parse("#30363D")), + Background = GetThemeBrush("ForgeDeep", "#0e0c11"), + BorderBrush = GetThemeBrush("ForgeBorder", "#2a2230"), BorderThickness = new Avalonia.Thickness(1), - CornerRadius = new Avalonia.CornerRadius(6), + CornerRadius = new Avalonia.CornerRadius(12), Padding = new Avalonia.Thickness(12, 8), Margin = new Avalonia.Thickness(0, 0, 0, 8) }; @@ -44,7 +44,7 @@ public Control Build(object? param) Text = field.IsRequired ? $"{field.Label} *" : field.Label, FontWeight = Avalonia.Media.FontWeight.SemiBold, FontSize = 12, - Foreground = new Avalonia.Media.SolidColorBrush(Avalonia.Media.Color.Parse("#E6EDF3")), + Foreground = GetThemeBrush("ForgeText", "#ede6f0"), Margin = new Avalonia.Thickness(0, 0, 0, 2) }; panel.Children.Add(label); @@ -255,4 +255,14 @@ private static TextBox BuildMultiLineEditor(ConfigFieldViewModel field) new Avalonia.Data.Binding("Value")); return textBox; } + + private static Avalonia.Media.IBrush GetThemeBrush(string key, string fallback) + { + if (Avalonia.Application.Current?.TryFindResource(key, Avalonia.Application.Current.ActualThemeVariant, out object? resource) == true && resource is Avalonia.Media.IBrush brush) + { + return brush; + } + + return new Avalonia.Media.SolidColorBrush(Avalonia.Media.Color.Parse(fallback)); + } } diff --git a/src/FlowForge.UI/Views/ExecutionLogView.axaml b/src/FlowForge.UI/Views/ExecutionLogView.axaml index 8c782b5..e5e1936 100644 --- a/src/FlowForge.UI/Views/ExecutionLogView.axaml +++ b/src/FlowForge.UI/Views/ExecutionLogView.axaml @@ -8,10 +8,23 @@ x:Class="FlowForge.UI.Views.ExecutionLogView" x:DataType="vm:ExecutionLogViewModel"> - + + + + + + + + + + + + + + @@ -21,12 +34,12 @@ Command="{Binding SelectTabCommand}" CommandParameter="0"> - - + + Foreground="{DynamicResource ForgeSuccess}" + FontSize="10" FontWeight="Bold" /> @@ -38,12 +51,12 @@ Command="{Binding SelectTabCommand}" CommandParameter="1"> - - + + Foreground="{DynamicResource ForgeError}" + FontSize="10" FontWeight="Bold" /> @@ -55,12 +68,12 @@ Command="{Binding SelectTabCommand}" CommandParameter="2"> - - + + Foreground="{DynamicResource ForgeWarning}" + FontSize="10" FontWeight="Bold" /> @@ -71,8 +84,8 @@ TextTrimming="CharacterEllipsis" VerticalAlignment="Center" HorizontalAlignment="Right" - Foreground="{DynamicResource MidnightTextSecondary}" - FontSize="15" + Foreground="{DynamicResource ForgeTextSecondary}" + FontSize="12" Margin="8,0,12,0" IsVisible="{Binding !IsRunning}" /> @@ -81,29 +94,30 @@ + Foreground="{DynamicResource ForgeAccent}" + FontWeight="SemiBold" FontSize="12" /> + Foreground="{DynamicResource ForgeTextSecondary}" + FontSize="12" /> + Height="4" Margin="0,6,0,0" + CornerRadius="2" /> @@ -116,41 +130,53 @@ - - + + + + + - - @@ -167,8 +193,8 @@ @@ -176,27 +202,30 @@ - - + + - - @@ -212,8 +241,8 @@ @@ -221,27 +250,30 @@ - - + + - - diff --git a/src/FlowForge.UI/Views/NodeLibraryView.axaml b/src/FlowForge.UI/Views/NodeLibraryView.axaml index 62c93d6..9dc300b 100644 --- a/src/FlowForge.UI/Views/NodeLibraryView.axaml +++ b/src/FlowForge.UI/Views/NodeLibraryView.axaml @@ -4,60 +4,84 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="using:FlowForge.UI.ViewModels" mc:Ignorable="d" - d:DesignWidth="200" d:DesignHeight="600" + d:DesignWidth="250" d:DesignHeight="600" x:Class="FlowForge.UI.Views.NodeLibraryView" x:DataType="vm:NodeLibraryViewModel"> - - + + + + + + - - - + + FontWeight="Bold" + LetterSpacing="1.2" + Foreground="{DynamicResource ForgeTextMuted}" + Margin="0,0,0,2" /> + Watermark="Search nodes..." + Background="{DynamicResource ForgeDeep}" + CornerRadius="12" + Margin="10,10,10,4" /> - + + + + diff --git a/src/FlowForge.UI/Views/PropertiesView.axaml b/src/FlowForge.UI/Views/PropertiesView.axaml index 2173eb7..b586616 100644 --- a/src/FlowForge.UI/Views/PropertiesView.axaml +++ b/src/FlowForge.UI/Views/PropertiesView.axaml @@ -5,49 +5,47 @@ xmlns:vm="using:FlowForge.UI.ViewModels" xmlns:views="using:FlowForge.UI.Views" mc:Ignorable="d" - d:DesignWidth="250" d:DesignHeight="600" + d:DesignWidth="290" d:DesignHeight="600" x:Class="FlowForge.UI.Views.PropertiesView" x:DataType="vm:PropertiesViewModel"> - - - + + + + + - - + Padding="10"> diff --git a/src/FlowForge.UI/Views/ShortcutsWindow.axaml b/src/FlowForge.UI/Views/ShortcutsWindow.axaml index 416bc86..16a0c92 100644 --- a/src/FlowForge.UI/Views/ShortcutsWindow.axaml +++ b/src/FlowForge.UI/Views/ShortcutsWindow.axaml @@ -2,69 +2,77 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="FlowForge.UI.Views.ShortcutsWindow" Title="Keyboard Shortcuts" - Width="400" Height="400" + Width="420" Height="440" CanResize="False" WindowStartupLocation="CenterOwner" - Background="{DynamicResource MidnightPanel}"> + Background="{DynamicResource ForgePanel}"> + Foreground="{DynamicResource ForgeAccent}" /> - - - - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + + + + + + + + + + + + + + + - + + + + + + + + + + + - - - - - - + + - + + + + - + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +