diff --git a/src/ScriptRunner/ScriptRunner.GUI/ViewModels/MainWindowViewModel.cs b/src/ScriptRunner/ScriptRunner.GUI/ViewModels/MainWindowViewModel.cs index e27a4e4..26e4e8b 100644 --- a/src/ScriptRunner/ScriptRunner.GUI/ViewModels/MainWindowViewModel.cs +++ b/src/ScriptRunner/ScriptRunner.GUI/ViewModels/MainWindowViewModel.cs @@ -176,6 +176,10 @@ public bool IsTreeViewMode private readonly ObservableAsPropertyHelper> _filteredActionList; public IEnumerable FilteredActionList => _filteredActionList.Value; + private readonly ObservableAsPropertyHelper _actionCount; + public int ActionCount => _actionCount.Value; + + public ObservableCollection RunningJobs { get; set; } = new(); @@ -215,9 +219,13 @@ public object? SelectedActionOrGroup set { this.RaiseAndSetIfChanged(ref _selectedActionOrGroup, value); - if (value is TaggedScriptConfig {Config: var scriptConfig}) + if (value is TaggedScriptConfig {Config: var scriptConfig, ArgumentSet: var argumentSet}) { SelectedAction = scriptConfig; + if (argumentSet != null) + { + SelectedArgumentSet = argumentSet; + } } } } @@ -393,12 +401,8 @@ public MainWindowViewModel(ParamsPanelFactory paramsPanelFactory, VaultProvider { var (textFilter, categoryFilter, actions) = tuple; - // Apply text filter - var configs = string.IsNullOrWhiteSpace(textFilter) - ? actions - : actions.Where(x => x.Name.Contains(textFilter, StringComparison.InvariantCultureIgnoreCase)); - - // Apply category filter (AND operator with text filter) + // Apply category filter first + IEnumerable configs = actions; if (!string.IsNullOrWhiteSpace(categoryFilter) && categoryFilter != "All") { if (categoryFilter == "(No Category)") @@ -416,19 +420,39 @@ public MainWindowViewModel(ParamsPanelFactory paramsPanelFactory, VaultProvider // When a specific category is filtered, show each action only once under that category if (!string.IsNullOrWhiteSpace(categoryFilter) && categoryFilter != "All") { - scriptConfigGroupWrappers = new[] + var expandedEntries = configs.SelectMany(c => + c.PredefinedArgumentSets.Select(p => new TaggedScriptConfig( + categoryFilter, + p.Description == "" ? c.Name : $"{c.Name} - {p.Description}", + c, + p + )) + ); + + // Apply text filter to expanded entries + if (!string.IsNullOrWhiteSpace(textFilter)) { - new ScriptConfigGroupWrapper + expandedEntries = expandedEntries.Where(x => x.Name.Contains(textFilter, StringComparison.InvariantCultureIgnoreCase)); + } + + var children = expandedEntries.OrderBy(x => x.Name).ToList(); + + // Only create group if it has children + scriptConfigGroupWrappers = children.Any() + ? new[] { - Name = categoryFilter, - Children = configs.Select(c => new TaggedScriptConfig(categoryFilter, c.Name, c)).OrderBy(x => x.Name) + new ScriptConfigGroupWrapper + { + Name = categoryFilter, + Children = children + } } - }; + : Enumerable.Empty(); } else { // When no filter or "All" is selected, show actions grouped by all their categories - scriptConfigGroupWrappers = configs.SelectMany(c => + var groupedConfigs = configs.SelectMany(c => { if (c.Categories is {Count: > 0}) { @@ -436,18 +460,42 @@ public MainWindowViewModel(ParamsPanelFactory paramsPanelFactory, VaultProvider } return new[] {(category: "(No Category)", script: c)}; - }).GroupBy(x => x.category).OrderBy(x=>x.Key) - .Select(x=> new ScriptConfigGroupWrapper + }).GroupBy(x => x.category).OrderBy(x=>x.Key); + + scriptConfigGroupWrappers = groupedConfigs.Select(x => + { + var expandedEntries = x.SelectMany(p => + p.script.PredefinedArgumentSets.Select(argSet => new TaggedScriptConfig( + x.Key, + argSet.Description == "" ? p.script.Name : $"{p.script.Name} - {argSet.Description}", + p.script, + argSet + )) + ); + + // Apply text filter to expanded entries + if (!string.IsNullOrWhiteSpace(textFilter)) + { + expandedEntries = expandedEntries.Where(e => e.Name.Contains(textFilter, StringComparison.InvariantCultureIgnoreCase)); + } + + return new ScriptConfigGroupWrapper { Name = x.Key, - Children = x.Select(p=> new TaggedScriptConfig(x.Key, p.script.Name, p.script)).OrderBy(x=>x.Name) - }); + Children = expandedEntries.OrderBy(e => e.Name) + }; + }).Where(group => group.Children.Any()); // Filter out empty groups } return scriptConfigGroupWrappers; }) .ObserveOn(RxApp.MainThreadScheduler) .ToProperty(this, x => x.FilteredActionList, out _filteredActionList); + + this.WhenAnyValue(x => x.Actions) + .Select(list => list?.Count ?? 0) + .ObserveOn(RxApp.MainThreadScheduler) + .ToProperty(this, x => x.ActionCount, out _actionCount); Observable .FromEventPattern( @@ -1448,4 +1496,9 @@ public class ScriptConfigGroupWrapper public IEnumerable Children { get; set; } } -public record TaggedScriptConfig(string Tag, string Name, ScriptConfig Config); +public record TaggedScriptConfig(string Tag, string Name, ScriptConfig Config, ArgumentSet ArgumentSet = null) +{ + public string IconName => "fa-scroll"; + public string IconColor => ArgumentSet?.Description == "" ? "#3baced" : "#ff8c00"; +} + diff --git a/src/ScriptRunner/ScriptRunner.GUI/ViewModels/SearchBoxViewModel.cs b/src/ScriptRunner/ScriptRunner.GUI/ViewModels/SearchBoxViewModel.cs index d8ffc59..8c9512d 100644 --- a/src/ScriptRunner/ScriptRunner.GUI/ViewModels/SearchBoxViewModel.cs +++ b/src/ScriptRunner/ScriptRunner.GUI/ViewModels/SearchBoxViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Linq; @@ -33,7 +33,8 @@ public SearchBoxViewModel(IReadOnlyList allActions, IReadOnlyList< ArgumentSet = p, FullName = p.Description == ""? x.FullName : $"{x.FullName} - {p.Description}", ActionName = p.Description == ""? x.Name : $"{x.Name} - {p.Description}", - SourceName = x.SourceName + SourceName = x.SourceName, + IconColor = p.Description == "" ? "#3baced" : "#ff8c00" })).ToList(); var intial = true; @@ -105,6 +106,7 @@ public class ScriptConfigWithArgumentSet public string FullName { get; set; } public string ActionName { get; set; } public string SourceName { get; set; } + public string IconColor { get; set; } } } diff --git a/src/ScriptRunner/ScriptRunner.GUI/Views/ActionDetailsSection.axaml b/src/ScriptRunner/ScriptRunner.GUI/Views/ActionDetailsSection.axaml index ade5412..c128295 100644 --- a/src/ScriptRunner/ScriptRunner.GUI/Views/ActionDetailsSection.axaml +++ b/src/ScriptRunner/ScriptRunner.GUI/Views/ActionDetailsSection.axaml @@ -38,10 +38,26 @@ diff --git a/src/ScriptRunner/ScriptRunner.GUI/Views/ActionsList.axaml b/src/ScriptRunner/ScriptRunner.GUI/Views/ActionsList.axaml index 70bb807..b769514 100644 --- a/src/ScriptRunner/ScriptRunner.GUI/Views/ActionsList.axaml +++ b/src/ScriptRunner/ScriptRunner.GUI/Views/ActionsList.axaml @@ -1,4 +1,4 @@ - - + + @@ -162,7 +163,7 @@ - + @@ -196,21 +197,21 @@ - - - + + - + @@ -226,7 +227,7 @@ @@ -258,5 +259,15 @@ + + + + + + diff --git a/src/ScriptRunner/ScriptRunner.GUI/Views/SearchBox.axaml b/src/ScriptRunner/ScriptRunner.GUI/Views/SearchBox.axaml index 00f7752..93c8981 100644 --- a/src/ScriptRunner/ScriptRunner.GUI/Views/SearchBox.axaml +++ b/src/ScriptRunner/ScriptRunner.GUI/Views/SearchBox.axaml @@ -10,62 +10,73 @@ x:Class="ScriptRunner.GUI.Views.SearchBox" x:DataType="viewModels:SearchBoxViewModel"> - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + - - - - - + + + + + - + - - - + + + - + - - - + + + - + - - - + + + - + - + diff --git a/src/ScriptRunner/ScriptRunner.GUI/Views/SideMenu.axaml.cs b/src/ScriptRunner/ScriptRunner.GUI/Views/SideMenu.axaml.cs index 3822857..84a3119 100644 --- a/src/ScriptRunner/ScriptRunner.GUI/Views/SideMenu.axaml.cs +++ b/src/ScriptRunner/ScriptRunner.GUI/Views/SideMenu.axaml.cs @@ -83,17 +83,13 @@ private async void OpenSearchBox(object? sender, RoutedEventArgs e) if (pattern.EventArgs is SearchBox.ResultSelectedEventArgs {Result: { } selectedCommand, AutoLaunch: var autoLaunch}) { - var selectedTagged = viewModel.FilteredActionList.SelectMany(x => x.Children) - .FirstOrDefault(x => x.Config == selectedCommand.Config); - - if (selectedTagged != null) + // Set SelectedAction directly (same pattern as ExecutionLog selection) + // This works regardless of any active category filter + viewModel.SelectedAction = selectedCommand.Config; + viewModel.SelectedArgumentSet = selectedCommand.ArgumentSet; + if (autoLaunch) { - viewModel.SelectedActionOrGroup = selectedTagged; - viewModel.SelectedArgumentSet = selectedCommand.ArgumentSet; - if (autoLaunch) - { - viewModel.RunScript(); - } + viewModel.RunScript(); } } });