Conversation
Create Flow.Launcher.Avalonia project as foundation for migrating from WPF to Avalonia UI framework. Key components: - MainWindow with query box and results list (matching WPF layout) - ViewModels: MainViewModel, ResultsViewModel, ResultViewModel - Themes/Base.axaml with converted styles from WPF - FluentAvaloniaUI for Windows 11 styling - References existing Core/Infrastructure/Plugin projects The project builds and runs alongside the existing WPF application. This is Phase 1 of the incremental migration approach.
- Load settings from disk via FlowLauncherJsonStorage - Initialize PluginManager and query plugins on text change - Add minimal AvaloniaPublicAPI implementing IPublicAPI - Execute plugin results on Enter key - Add WPF framework reference for IPublicAPI compatibility
…oading - Use DynamicData SourceList with automatic sorting by score descending - Add ReplaceResults() with EditDiff for minimal UI updates (reduces flickering) - Keep previous results visible while typing until new results arrive - Add ImageLoader with Windows Shell API (IShellItemImageFactory) for exe/ico icons - Use AlphaFormat.Unpremul to correctly render transparent icons without white borders - Query all plugins in parallel and merge/sort results globally
- Wrap each plugin query in Task.Run() to ensure synchronous plugin code doesn't block the UI thread - Show results progressively as each plugin completes using ConcurrentBag - Update UI after each plugin returns instead of waiting for all plugins
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
This PR introduces a new Avalonia UI frontend for Flow Launcher as an alternative to the existing WPF implementation. The migration adds a complete new project with UI components, ViewModels, and platform integration while maintaining compatibility with the existing plugin system.
Changes:
- Adds new
Flow.Launcher.Avaloniaproject with complete UI implementation including MainWindow, result list views, and styling - Implements Windows global hotkey support and image loading utilities with native Win32 interop
- Adds console logging output to infrastructure logger for easier debugging
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 50 comments.
Show a summary per file
| File | Description |
|---|---|
| Flow.Launcher.sln | Adds Flow.Launcher.Avalonia project to solution with build configurations |
| Flow.Launcher.Infrastructure/Logger/Log.cs | Adds console output for log messages |
| Flow.Launcher.Avalonia/app.manifest | Defines Windows compatibility and DPI awareness settings |
| Flow.Launcher.Avalonia/Views/ResultListBox.axaml | XAML markup for search results list display |
| Flow.Launcher.Avalonia/Views/ResultListBox.axaml.cs | Code-behind for result list interactions |
| Flow.Launcher.Avalonia/ViewModel/ResultsViewModel.cs | ViewModel managing result collection with sorting and selection |
| Flow.Launcher.Avalonia/ViewModel/ResultViewModel.cs | ViewModel for individual result items |
| Flow.Launcher.Avalonia/ViewModel/MainViewModel.cs | Main application ViewModel coordinating queries and UI state |
| Flow.Launcher.Avalonia/Themes/Resources.axaml | Color and resource definitions for UI theme |
| Flow.Launcher.Avalonia/Themes/Base.axaml | Style definitions for UI components |
| Flow.Launcher.Avalonia/Program.cs | Application entry point |
| Flow.Launcher.Avalonia/MainWindow.axaml | Main window XAML markup |
| Flow.Launcher.Avalonia/MainWindow.axaml.cs | Main window code-behind with event handling |
| Flow.Launcher.Avalonia/Helper/ImageLoader.cs | Image loading utility with caching and Win32 shell integration |
| Flow.Launcher.Avalonia/Helper/HotKeyMapper.cs | Global hotkey registration management |
| Flow.Launcher.Avalonia/Helper/GlobalHotkey.cs | Win32-based global hotkey implementation |
| Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj | Project configuration and dependencies |
| Flow.Launcher.Avalonia/Converters/CommonConverters.cs | Value converters for data binding |
| Flow.Launcher.Avalonia/Converters/BoolToIsVisibleConverter.cs | Boolean to visibility converter |
| Flow.Launcher.Avalonia/AvaloniaPublicAPI.cs | IPublicAPI implementation for plugin compatibility |
| Flow.Launcher.Avalonia/App.axaml | Application XAML with theme configuration |
| Flow.Launcher.Avalonia/App.axaml.cs | Application code-behind with initialization logic |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| <application xmlns="urn:schemas-microsoft-com:asm.v3"> | ||
| <windowsSettings> | ||
| <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> |
There was a problem hiding this comment.
Corrected spelling of 'true/pm' to 'true/PM' - the value appears to be malformed. It should be just 'true' for the dpiAware element, or 'PerMonitor' for per-monitor DPI awareness.
| <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> | |
| <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> |
| // Also output to console for easy debugging | ||
| System.Console.WriteLine($"[{level}] {classNameWithMethod}: {message}"); |
There was a problem hiding this comment.
Console logging added to production code affects performance and should be conditional. Consider wrapping this in a DEBUG preprocessor directive or using a configuration flag to enable/disable console output, as this will log every message in Release builds.
| // Also output to console for easy debugging | |
| System.Console.WriteLine($"[{level}] {classNameWithMethod}: {message}"); | |
| // Also output to console for easy debugging | |
| #if DEBUG | |
| System.Console.WriteLine($"[{level}] {classNameWithMethod}: {message}"); | |
| #endif |
| public void Dispose() | ||
| { | ||
| _subscription.Dispose(); | ||
| _sourceList.Dispose(); | ||
| } |
There was a problem hiding this comment.
The Dispose method doesn't follow the standard IDisposable pattern. Consider implementing the full pattern with a protected virtual Dispose(bool disposing) method and a finalizer to handle unmanaged resources properly, especially since this class manages disposable resources.
|
|
||
| if (RegisterClass(ref wc) == 0) | ||
| { | ||
| Console.WriteLine("[GlobalHotkey] Failed to register window class"); |
There was a problem hiding this comment.
Direct Console.WriteLine calls for logging should use the application's logging infrastructure (Flow.Launcher.Infrastructure.Logger.Log) for consistency and proper log management. This applies to all Console.WriteLine statements in this file.
| var wc = new WNDCLASS | ||
| { | ||
| lpfnWndProc = _wndProc, | ||
| hInstance = GetModuleHandle(null), |
There was a problem hiding this comment.
Replace this call with a call to managed code if possible.
| hInstance = GetModuleHandle(null), | |
| hInstance = Marshal.GetHINSTANCE(typeof(GlobalHotkey).Module), |
| private string _subTitle = string.Empty; | ||
|
|
||
| [ObservableProperty] | ||
| private string _iconPath = string.Empty; |
There was a problem hiding this comment.
Field '_iconPath' can be 'readonly'.
| public partial class ResultViewModel : ObservableObject | ||
| { | ||
| [ObservableProperty] | ||
| private string _title = string.Empty; |
There was a problem hiding this comment.
Field '_title' can be 'readonly'.
|
|
||
| public partial class ResultListBox : UserControl | ||
| { | ||
| private ListBox? _listBox; |
There was a problem hiding this comment.
Field '_listBox' can be 'readonly'.
| private int _selectedIndex; | ||
|
|
||
| [ObservableProperty] | ||
| private bool _isVisible = true; |
There was a problem hiding this comment.
Field '_isVisible' can be 'readonly'.
| private bool _hasResults; | ||
|
|
||
| [ObservableProperty] | ||
| private ResultsViewModel _results; |
There was a problem hiding this comment.
Field '_results' can be 'readonly'.
- Add ActiveView enum to track Results vs ContextMenu view state - Add ContextMenu ResultsViewModel and view switching logic - Implement LoadContextMenuCommand using PluginManager.GetContextMenusForPlugin - Add keyboard navigation: Shift+Enter/Right to open, Left/Escape to close - Update SelectNextItem/SelectPrevItem to navigate appropriate list - Update EscCommand to return from context menu before hiding
- Create Internationalization service that parses WPF XAML language files - Load translations from main Languages/ folder and all plugin Languages/ folders - Add LocalizeExtension markup extension and Translator helper for XAML/code - Fix IPublicAPI.GetTranslation to use the i18n service for plugin context menus - Update MainWindow to use localized placeholder text
This comment has been minimized.
This comment has been minimized.
- Internationalization.Initialize() called in constructor when DI creates it - Remove InitializeInternationalization() method from App.axaml.cs - Update AvaloniaPublicAPI and LocalizeExtension to use Ioc.Default.GetService - Reorder ConfigureDI before i18n initialization
This comment has been minimized.
This comment has been minimized.
|
Is there any actual benefit to switching to Avalonia or it's just for testing purposes? |
There should be some performance improvement for animation. Besides, I also want to have the chance to make flow cross platform as I am gradually migrating my workflow to mac. |
- MainWindowVisibility starts as false (window hidden) - Window IsVisible bound to MainWindowVisibility - Set MainWindowVisibility = true in OnPluginsReady() after plugins load
This comment has been minimized.
This comment has been minimized.
- Add Glyph and GlyphAvailable properties to ResultViewModel - Set Glyph from plugin Result when creating result items - Add resultGlyph style matching icon size (32x32) - Update ResultListBox to show glyph or image icon based on ShowGlyph property - ShowGlyph = true when UseGlyphIcons is enabled AND glyph is available
…ling - Add gold foreground color (#FFD700) for highlighted search matches - Update HighlightTextConverter to use theme resource with fallback - Fix TextBlockHelper to preserve Foreground property when copying Runs - Fix highlight update bug: sync TitleHighlightData/SubTitleHighlightData/Score for reused ResultViewModel instances when query changes - Update migration checklist to mark HighlightTextConverter as done
- Remove 13 codemap.md files from git index (Plugins/Flow.Launcher.Plugin.Explorer) - Add **/codemap.md pattern to .gitignore - Add .slim/ to .gitignore
Enable automatic GitHub Actions builds for pushes to the avalonia_migration branch.
Resolved conflicts: - Calculator.csproj: Keep Mages 3.0.1 (from dev) + Avalonia packages - Explorer SettingsViewModel.cs: Use PromptUserSelectFileAsync for ShellPath - ExplorerSettings.xaml: Removed (deleted in HEAD) - packages.lock.json: Restored from dev
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Fix all issues with AI agents
In `@Flow.Launcher.Avalonia/ViewModel/ResultsViewModel.cs`:
- Around line 23-149: Add a partial method OnSelectedItemChanged to keep
SelectedIndex in sync when SelectedItem is changed by the UI: implement partial
void OnSelectedItemChanged(ResultViewModel? value) to set SelectedIndex = value
== null ? -1 : _results.IndexOf(value) (or -1 if not found), and ensure you
guard for _results being non-null and index bounds before changing
SelectedIndex; use the existing symbols SelectedItem, SelectedIndex,
OnSelectedItemChanged, and _results so the UI two-way binding stays consistent.
In `@Flow.Launcher.Avalonia/ViewModel/SettingPages/GeneralSettingsViewModel.cs`:
- Around line 40-43: SelectedLanguage can become null when _settings.Language
equals the special "system" code because LoadLanguages doesn't include a
matching Language entry; update LoadLanguages to insert a "System" Language
option (use the same system code constant that Internationalization expects)
into the Languages collection so Languages.FirstOrDefault(l => l.LanguageCode ==
_settings.Language) returns a non-null entry, and apply the same addition
wherever LoadLanguages is duplicated (see LoadLanguages and any similar
population code around the other occurrence) so the dropdown can represent and
re-select the system option.
- Around line 339-354: SelectPython and SelectNode are currently no-op commands;
implement a file picker in the SelectPython and SelectNode methods to let the
user choose the executable, assign the chosen path to
_settings.PluginSettings.PythonExecutablePath or
_settings.PluginSettings.NodeExecutablePath, persist settings if necessary, and
raise property change notifications by calling
OnPropertyChanged(nameof(PythonPath)) and OnPropertyChanged(nameof(NodePath));
alternatively, if you can't implement the picker now, disable or hide the
commands by adding a CanExecute check for the RelayCommand (or remove the
[RelayCommand] attribute and expose ICommand that returns false until supported)
so the buttons aren't clickable.
In `@Flow.Launcher.Avalonia/ViewModel/SettingPages/PluginsSettingsViewModel.cs`:
- Around line 54-61: The FilteredPlugins getter can throw when
PluginItemViewModel.Description or ActionKeywords/ActionKeywordsText are null;
update the predicate in FilteredPlugins (and the similar logic referenced by
PluginItemViewModel usage at the other spots) to null-safe checks—e.g., treat
null Description or ActionKeywordsText as empty strings and guard ActionKeywords
(or any collection) with ?., Any(), or null-coalescing before evaluating
Contains or joining into ActionKeywordsText—so the filter uses (Description ??
"") and (ActionKeywordsText ?? "") or checks ActionKeywords?.Any() appropriately
to avoid null reference exceptions.
In `@Flow.Launcher.Avalonia/ViewModel/SettingPages/ThemeSettingsViewModel.cs`:
- Around line 25-31: The ThemeSettingsViewModel currently subscribes to
_settings.PropertyChanged with a lambda that checks nameof(Settings.Language)
and calls UpdateLabels(), but never unsubscribes, causing a potential memory
leak; modify ThemeSettingsViewModel to either implement IDisposable and
unsubscribe the handler from _settings.PropertyChanged in Dispose, or use a weak
event pattern (WeakEventManager or equivalent) for the subscription so the
ViewModel can be garbage collected; ensure the subscription reference is stored
(e.g., a handler delegate) so it can be removed in Dispose if you choose
explicit unsubscription.
In `@Flow.Launcher.Avalonia/Views/SettingPages/PluginStoreSettingsPage.axaml`:
- Around line 18-29: Add accessible names to all icon-only Buttons by setting
AutomationProperties.Name to the localized tooltip string (use the same
i18n:Localize keys used for ToolTip.Tip) so screen readers announce them;
specifically update the Buttons bound to RefreshExternalPluginsCommand,
CheckPluginUpdatesCommand, InstallPluginCommand and likewise add
AutomationProperties.Name to the clear-filter Button (ClearFilterCommand) and
the website/source-code Buttons (OpenPluginWebsiteCommand /
OpenPluginSourceCommand or equivalent) using their existing i18n:Localize
resource keys.
In `@Flow.Launcher.Avalonia/Views/SettingPages/ProxySettingsPage.axaml`:
- Around line 28-32: The NumericUpDown inside SettingsExpander currently allows
a Minimum="0" which permits port 0; change the control's Minimum to "1" so
ProxyPort can only be set in the valid range 1–65535. Locate the NumericUpDown
in ProxySettingsPage.axaml (the element with Value="{Binding ProxyPort}" and
IsEnabled="{Binding ProxyEnabled}") and update its Minimum attribute from 0 to 1
while leaving Maximum="65535" and bindings unchanged.
In `@Flow.Launcher/Languages/en.xaml`:
- Line 229: Update the C# plugin label string value identified by the resource
key "pluginType_CSharp": change the text from "C# / .Net" to use the standard
Microsoft capitalization "C# / .NET" so the resource value for pluginType_CSharp
reads "C# / .NET".
- Around line 229-232: The non-English XAML resource files are missing nine new
keys (pluginType_CSharp, pluginType_Python, pluginType_Nodejs,
pluginType_Executable, hotkeyFormatTip, sourceCode, loading,
plugin_settings_open_in_wpf_window, openSettings), causing fallbacks or blank
UI; add these exact x:Key entries to each of the 25 non-English language files
(matching the keys used in Flow.Launcher/Languages/en.xaml) and provide
appropriate localized string values (or temporary English text as a fallback) so
the UI displays text for all cultures, ensuring keys and casing match exactly
across files.
🧹 Nitpick comments (10)
Flow.Launcher.Avalonia/Helper/TextBlockHelper.cs (1)
50-54: Non-Run inlines added directly may cause parent ownership issues.Adding non-Run inline types directly without copying can cause problems since Avalonia inlines can only belong to one parent. If the source InlineCollection is reused (e.g., from a cached converter result), subsequent bindings will fail because the inline already has a parent.
Given the comment indicates this "may need enhancement," consider either:
- Implementing copy logic for other common inline types (e.g.,
Span,Bold,Italic)- Logging a warning when encountering non-Run types to aid debugging
♻️ Suggested enhancement for robustness
else { - // For other inline types, add directly (may need enhancement) - textBlock.Inlines?.Add(inline); + // For other inline types, log and skip to avoid parent ownership issues + // TODO: Implement copy logic for Span, Bold, Italic if needed + System.Diagnostics.Debug.WriteLine($"TextBlockHelper: Unsupported inline type {inline.GetType().Name} encountered"); }Flow.Launcher.Avalonia/ViewModel/SettingPages/ThemeSettingsViewModel.cs (3)
82-82: Repeated allocation on every property access.
MaxResultsRangecreates a newList<int>each time it's accessed. Cache it as a static or instance field.Proposed fix
+ private static readonly List<int> _maxResultsRange = Enumerable.Range(1, 20).ToList(); + - public List<int> MaxResultsRange => Enumerable.Range(1, 20).ToList(); + public List<int> MaxResultsRange => _maxResultsRange;
97-115: Inconsistent setter pattern: missing equality checks.
QueryBoxFontSizeandResultItemFontSizesetters don't check for equality before assigning, unlike the other properties in this ViewModel (MaxResults,UseGlyphIcons,SelectedColorScheme). This can cause unnecessaryPropertyChangednotifications.Proposed fix for consistency
public double QueryBoxFontSize { get => _settings.QueryBoxFontSize; set { + if (_settings.QueryBoxFontSize != value) + { _settings.QueryBoxFontSize = value; OnPropertyChanged(); + } } } public double ResultItemFontSize { get => _settings.ResultItemFontSize; set { + if (_settings.ResultItemFontSize != value) + { _settings.ResultItemFontSize = value; OnPropertyChanged(); + } } }
56-67: Minor: Extra blank line before method body.There's an unnecessary blank line between the method signature (line 56) and the opening brace (line 58).
AVALONIA_MIGRATION_CHECKLIST.md (4)
3-4: Update the "Last Updated" timestamp.The timestamp shows "January 2026" but the current date is February 2026. Consider updating this to reflect the current migration status.
50-97: Consider fixing table formatting to satisfy markdown linting.The tables throughout this document have several formatting issues flagged by markdownlint:
- Section headers embedded in tables (e.g.,
| **Startup Section** |on line 55) create column count mismatches because they span only 1 column instead of the expected 3 columns.- Missing spaces around pipe separators in many table rows (MD060 violations).
- Bold text used as section headers within tables triggers MD036 warnings.
While the tables render correctly in most viewers, these issues may affect automated parsing and tooling.
Options to fix:
- Convert embedded section headers to proper markdown headings (e.g.,
#### Startup Section) and restructure tables- Ensure section header rows span all columns:
| **Startup Section** | | |- Add spaces around all pipe separators:
| Setting | Status | Notes |instead of|Setting|Status|Notes|Since this is a tracking document where readability is paramount, you may choose to suppress these linting rules if the current format is preferred.
412-467: Add language specifiers to code blocks.The two file structure code blocks (lines 413-433 and 436-466) are missing language specifiers. Add
textorplaintextto satisfy markdown linting and improve rendering:📝 Suggested fix
### WPF Source Files -``` +```text Flow.Launcher/ ├── MainWindow.xaml (+ .cs) ...### Avalonia Target Files -``` +```text Flow.Launcher.Avalonia/ ├── MainWindow.axaml (+ .cs) ...
50-254: Consider the maintenance burden of manual metrics.The checklist includes many manually tracked metrics (line counts, percentages, progress ratios) throughout the document. These will require regular updates as the migration progresses and may become stale between updates.
Suggestions:
- Consider adding a note encouraging contributors to update metrics when completing migration tasks
- Alternatively, explore whether any metrics could be automatically generated via scripts (e.g., line counts from
clocor similar tools)- The "Last Updated" timestamp helps indicate freshness but should be consistently maintained
This is a common tradeoff for migration tracking documents where the value of having a roadmap outweighs the staleness risk.
Flow.Launcher.Avalonia/Views/SettingPages/GeneralSettingsPage.axaml (1)
154-197: Consider disabling dependent inputs when their toggles are off.This avoids editing delay/home-page counts when the corresponding feature is disabled.
♻️ Suggested tweak
<ui:NumberBox Value="{Binding SearchDelayTime}" Minimum="0" Maximum="1000" SmallChange="10" SpinButtonPlacementMode="Compact" - Width="120" /> + Width="120" + IsEnabled="{Binding SearchQueryResultsWithDelay}" /> ... <ui:NumberBox Value="{Binding MaxHistoryResultsToShow}" Minimum="0" Maximum="100" SmallChange="5" SpinButtonPlacementMode="Compact" - Width="120" /> + Width="120" + IsEnabled="{Binding ShowHistoryResultsForHomePage}" />Flow.Launcher.Avalonia/Views/SettingPages/PluginStoreSettingsPage.axaml (1)
77-79: Consider MinWidth/MinHeight for better DPI responsiveness.Fixed
Width/Heightcan clip text at higher scaling. UsingMinWidth/MinHeightpreserves the grid while allowing expansion.♻️ Suggested tweak
-<Button Padding="0" - Width="216" Height="184" +<Button Padding="0" + MinWidth="216" MinHeight="184" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Background="{DynamicResource ControlFillColorDefaultBrush}" BorderBrush="{DynamicResource CardStrokeColorDefaultBrush}" BorderThickness="1" CornerRadius="{DynamicResource ControlCornerRadius}">
| [ObservableProperty] | ||
| private ResultViewModel? _selectedItem; | ||
|
|
||
| [ObservableProperty] | ||
| private int _selectedIndex; | ||
|
|
||
| [ObservableProperty] | ||
| private bool _isVisible = true; | ||
|
|
||
| /// <summary> | ||
| /// Sorted results collection bound to the UI. | ||
| /// Automatically sorted by Score descending. | ||
| /// </summary> | ||
| public ReadOnlyObservableCollection<ResultViewModel> Results => _results; | ||
|
|
||
| public Settings Settings => _settings; | ||
|
|
||
| public int MaxHeight => (int)(_settings.MaxResultsToShow * _settings.ItemHeightSize); | ||
|
|
||
| public ResultsViewModel(Settings settings) | ||
| { | ||
| _settings = settings; | ||
|
|
||
| // Connect SourceList to sorted ReadOnlyObservableCollection | ||
| _subscription = _sourceList.Connect() | ||
| .Sort(SortExpressionComparer<ResultViewModel>.Descending(r => r.Score)) | ||
| .Bind(out _results) | ||
| .Subscribe(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Replace all results with new ones using atomic Edit to prevent flickering. | ||
| /// Edit batches changes and fires only one notification at the end. | ||
| /// </summary> | ||
| public void ReplaceResults(IEnumerable<ResultViewModel> newResults) | ||
| { | ||
| var resultsList = newResults.ToList(); | ||
| foreach (var r in resultsList) | ||
| { | ||
| r.Settings = _settings; | ||
| } | ||
|
|
||
| // Update highlight data and score for items that will be kept by EditDiff. | ||
| // This is necessary because EditDiff reuses existing ResultViewModel instances | ||
| // based on Title+SubTitle equality, but highlight indices are query-specific. | ||
| // For example, "Chrome" highlighted for "chr" [0,1,2] vs "chrome" [0,1,2,3,4,5]. | ||
| var existingItems = _sourceList.Items.ToDictionary(r => (r.Title, r.SubTitle)); | ||
| foreach (var newItem in resultsList) | ||
| { | ||
| if (existingItems.TryGetValue((newItem.Title, newItem.SubTitle), out var existing)) | ||
| { | ||
| existing.TitleHighlightData = newItem.TitleHighlightData; | ||
| existing.SubTitleHighlightData = newItem.SubTitleHighlightData; | ||
| existing.Score = newItem.Score; | ||
| } | ||
| } | ||
|
|
||
| // EditDiff calculates minimal changes needed - items with same Title+SubTitle are kept | ||
| _sourceList.EditDiff(resultsList, ResultViewModelComparer.Instance); | ||
|
|
||
| // Select first item after replacement | ||
| if (_results.Count > 0) | ||
| { | ||
| SelectedIndex = 0; | ||
| SelectedItem = _results[0]; | ||
| } | ||
| else | ||
| { | ||
| SelectedItem = null; | ||
| SelectedIndex = -1; | ||
| } | ||
| } | ||
|
|
||
| public void AddResult(ResultViewModel result) | ||
| { | ||
| result.Settings = _settings; | ||
| _sourceList.Add(result); | ||
|
|
||
| // Select first item if nothing selected | ||
| if (SelectedItem == null && _results.Count > 0) | ||
| { | ||
| SelectedIndex = 0; | ||
| SelectedItem = _results[0]; | ||
| } | ||
| } | ||
|
|
||
| public void Clear() | ||
| { | ||
| _sourceList.Clear(); | ||
| SelectedItem = null; | ||
| SelectedIndex = -1; | ||
| } | ||
|
|
||
| public void SelectNextItem() | ||
| { | ||
| if (_results.Count == 0) return; | ||
|
|
||
| var newIndex = SelectedIndex + 1; | ||
| if (newIndex >= _results.Count) | ||
| { | ||
| newIndex = 0; // Wrap to beginning | ||
| } | ||
|
|
||
| SelectedIndex = newIndex; | ||
| SelectedItem = _results[newIndex]; | ||
| } | ||
|
|
||
| public void SelectPrevItem() | ||
| { | ||
| if (_results.Count == 0) return; | ||
|
|
||
| var newIndex = SelectedIndex - 1; | ||
| if (newIndex < 0) | ||
| { | ||
| newIndex = _results.Count - 1; // Wrap to end | ||
| } | ||
|
|
||
| SelectedIndex = newIndex; | ||
| SelectedItem = _results[newIndex]; | ||
| } | ||
|
|
||
| partial void OnSelectedIndexChanged(int value) | ||
| { | ||
| if (value >= 0 && value < _results.Count) | ||
| { | ||
| SelectedItem = _results[value]; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
# Search for SelectedItem and SelectedIndex usage in AXAML and C# files
rg -n "SelectedItem|SelectedIndex" -g "*.axaml" -g "*.cs" -C 3 | head -100Repository: Flow-Launcher/Flow.Launcher
Length of output: 12618
🏁 Script executed:
# Find AXAML files and search for ResultsViewModel binding
rg -l "ResultsViewModel" -g "*.axaml"Repository: Flow-Launcher/Flow.Launcher
Length of output: 119
🏁 Script executed:
# Read the ResultListBox.axaml file
cat -n Flow.Launcher.Avalonia/Views/ResultListBox.axamlRepository: Flow-Launcher/Flow.Launcher
Length of output: 7651
Add OnSelectedItemChanged to sync SelectedIndex when SelectedItem updates.
Both SelectedItem and SelectedIndex are bound with Mode=TwoWay in the UI (ResultListBox.axaml lines 19-20). The current code only syncs index → item via OnSelectedIndexChanged, but when the UI updates SelectedItem directly, SelectedIndex has no handler to update and can become stale.
♻️ Add bidirectional sync
partial void OnSelectedIndexChanged(int value)
{
if (value >= 0 && value < _results.Count)
{
SelectedItem = _results[value];
}
}
+
+ partial void OnSelectedItemChanged(ResultViewModel? value)
+ {
+ var index = value == null ? -1 : _results.IndexOf(value);
+ if (index != SelectedIndex)
+ SelectedIndex = index;
+ }🤖 Prompt for AI Agents
In `@Flow.Launcher.Avalonia/ViewModel/ResultsViewModel.cs` around lines 23 - 149,
Add a partial method OnSelectedItemChanged to keep SelectedIndex in sync when
SelectedItem is changed by the UI: implement partial void
OnSelectedItemChanged(ResultViewModel? value) to set SelectedIndex = value ==
null ? -1 : _results.IndexOf(value) (or -1 if not found), and ensure you guard
for _results being non-null and index bounds before changing SelectedIndex; use
the existing symbols SelectedItem, SelectedIndex, OnSelectedItemChanged, and
_results so the UI two-way binding stays consistent.
| public Language? SelectedLanguage | ||
| { | ||
| get => Languages.FirstOrDefault(l => l.LanguageCode == _settings.Language); | ||
| set |
There was a problem hiding this comment.
Add a "System" language option so the dropdown can represent existing settings.
If _settings.Language is the system code, SelectedLanguage resolves to null and users can’t re-select it. Include the system option (using the same code as Internationalization expects) in LoadLanguages.
🛠️ Suggested fix
private void LoadLanguages()
{
// Minimal set of languages for now, can be expanded by loading from directory later
Languages = new List<Language>
{
+ new Language(Constant.SystemLanguageCode, "System"),
new Language("en", "English"),
new Language("zh-cn", "中文 (简体)"),
new Language("zh-tw", "中文 (繁體)"),
new Language("ko", "한국어"),
new Language("ja", "日本語")
};
}Also applies to: 360-370
🤖 Prompt for AI Agents
In `@Flow.Launcher.Avalonia/ViewModel/SettingPages/GeneralSettingsViewModel.cs`
around lines 40 - 43, SelectedLanguage can become null when _settings.Language
equals the special "system" code because LoadLanguages doesn't include a
matching Language entry; update LoadLanguages to insert a "System" Language
option (use the same system code constant that Internationalization expects)
into the Languages collection so Languages.FirstOrDefault(l => l.LanguageCode ==
_settings.Language) returns a non-null entry, and apply the same addition
wherever LoadLanguages is duplicated (see LoadLanguages and any similar
population code around the other occurrence) so the dropdown can represent and
re-select the system option.
| public string PythonPath => _settings.PluginSettings.PythonExecutablePath ?? "Not set"; | ||
| public string NodePath => _settings.PluginSettings.NodeExecutablePath ?? "Not set"; | ||
|
|
||
| [RelayCommand] | ||
| private async Task SelectPython() | ||
| { | ||
| // TODO: Implement file picker | ||
| await Task.CompletedTask; | ||
| } | ||
|
|
||
| [RelayCommand] | ||
| private async Task SelectNode() | ||
| { | ||
| // TODO: Implement file picker | ||
| await Task.CompletedTask; | ||
| } |
There was a problem hiding this comment.
SelectPython/SelectNode are no-op commands right now.
The buttons will appear clickable but do nothing. Either implement the file picker (and update _settings.PluginSettings.* with OnPropertyChanged(nameof(PythonPath/NodePath))) or temporarily disable/hide the buttons until supported.
🤖 Prompt for AI Agents
In `@Flow.Launcher.Avalonia/ViewModel/SettingPages/GeneralSettingsViewModel.cs`
around lines 339 - 354, SelectPython and SelectNode are currently no-op
commands; implement a file picker in the SelectPython and SelectNode methods to
let the user choose the executable, assign the chosen path to
_settings.PluginSettings.PythonExecutablePath or
_settings.PluginSettings.NodeExecutablePath, persist settings if necessary, and
raise property change notifications by calling
OnPropertyChanged(nameof(PythonPath)) and OnPropertyChanged(nameof(NodePath));
alternatively, if you can't implement the picker now, disable or hide the
commands by adding a CanExecute check for the RelayCommand (or remove the
[RelayCommand] attribute and expose ICommand that returns false until supported)
so the buttons aren't clickable.
| public IEnumerable<PluginItemViewModel> FilteredPlugins => | ||
| string.IsNullOrWhiteSpace(SearchText) | ||
| ? Plugins | ||
| : Plugins.Where(p => | ||
| p.Name.Contains(SearchText, StringComparison.OrdinalIgnoreCase) || | ||
| p.Description.Contains(SearchText, StringComparison.OrdinalIgnoreCase) || | ||
| p.ActionKeywordsText.Contains(SearchText, StringComparison.OrdinalIgnoreCase) | ||
| ); |
There was a problem hiding this comment.
Guard against null metadata in search and action keywords.
Third‑party plugins can omit fields, so Description or ActionKeywords being null would throw during filtering or ActionKeywordsText evaluation.
🛠️ Proposed fix
public IEnumerable<PluginItemViewModel> FilteredPlugins =>
string.IsNullOrWhiteSpace(SearchText)
? Plugins
: Plugins.Where(p =>
- p.Name.Contains(SearchText, StringComparison.OrdinalIgnoreCase) ||
- p.Description.Contains(SearchText, StringComparison.OrdinalIgnoreCase) ||
- p.ActionKeywordsText.Contains(SearchText, StringComparison.OrdinalIgnoreCase)
+ (p.Name?.Contains(SearchText, StringComparison.OrdinalIgnoreCase) ?? false) ||
+ (p.Description?.Contains(SearchText, StringComparison.OrdinalIgnoreCase) ?? false) ||
+ p.ActionKeywordsText.Contains(SearchText, StringComparison.OrdinalIgnoreCase)
);
-public string ActionKeywordsText => string.Join(Query.ActionKeywordSeparator, _plugin.Metadata.ActionKeywords);
+public string ActionKeywordsText =>
+ string.Join(Query.ActionKeywordSeparator, _plugin.Metadata.ActionKeywords ?? Array.Empty<string>());Also applies to: 314-315
🤖 Prompt for AI Agents
In `@Flow.Launcher.Avalonia/ViewModel/SettingPages/PluginsSettingsViewModel.cs`
around lines 54 - 61, The FilteredPlugins getter can throw when
PluginItemViewModel.Description or ActionKeywords/ActionKeywordsText are null;
update the predicate in FilteredPlugins (and the similar logic referenced by
PluginItemViewModel usage at the other spots) to null-safe checks—e.g., treat
null Description or ActionKeywordsText as empty strings and guard ActionKeywords
(or any collection) with ?., Any(), or null-coalescing before evaluating
Contains or joining into ActionKeywordsText—so the filter uses (Description ??
"") and (ActionKeywordsText ?? "") or checks ActionKeywords?.Any() appropriately
to avoid null reference exceptions.
| _settings.PropertyChanged += (s, e) => | ||
| { | ||
| if (e.PropertyName == nameof(Settings.Language)) | ||
| { | ||
| UpdateLabels(); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Potential memory leak from event subscription.
The PropertyChanged handler is subscribed but never unsubscribed. If the Settings instance outlives this ViewModel, the ViewModel won't be garbage collected. Consider implementing IDisposable or using weak event patterns.
Proposed fix using weak event or explicit disposal
-public partial class ThemeSettingsViewModel : ObservableObject
+public partial class ThemeSettingsViewModel : ObservableObject, IDisposable
{
private readonly Settings _settings;
private readonly AvaloniaI18n _i18n;
+ private readonly PropertyChangedEventHandler _settingsChangedHandler;
public ThemeSettingsViewModel()
{
_settings = Ioc.Default.GetRequiredService<Settings>();
_i18n = Ioc.Default.GetRequiredService<AvaloniaI18n>();
ColorSchemeOptions = DropdownDataGeneric<ColorSchemes>.GetEnumData("ColorScheme");
- _settings.PropertyChanged += (s, e) =>
+ _settingsChangedHandler = (s, e) =>
{
if (e.PropertyName == nameof(Settings.Language))
{
UpdateLabels();
}
};
+ _settings.PropertyChanged += _settingsChangedHandler;
}
+
+ public void Dispose()
+ {
+ _settings.PropertyChanged -= _settingsChangedHandler;
+ }🤖 Prompt for AI Agents
In `@Flow.Launcher.Avalonia/ViewModel/SettingPages/ThemeSettingsViewModel.cs`
around lines 25 - 31, The ThemeSettingsViewModel currently subscribes to
_settings.PropertyChanged with a lambda that checks nameof(Settings.Language)
and calls UpdateLabels(), but never unsubscribes, causing a potential memory
leak; modify ThemeSettingsViewModel to either implement IDisposable and
unsubscribe the handler from _settings.PropertyChanged in Dispose, or use a weak
event pattern (WeakEventManager or equivalent) for the subscription so the
ViewModel can be garbage collected; ensure the subscription reference is stored
(e.g., a handler delegate) so it can be removed in Dispose if you choose
explicit unsubscription.
| <StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="10"> | ||
| <Button Command="{Binding RefreshExternalPluginsCommand}" ToolTip.Tip="{i18n:Localize refresh}"> | ||
| <ui:SymbolIcon Symbol="Refresh" FontSize="16"/> | ||
| </Button> | ||
|
|
||
| <Button Command="{Binding CheckPluginUpdatesCommand}" ToolTip.Tip="{i18n:Localize checkUpdates}"> | ||
| <ui:SymbolIcon Symbol="Sync" FontSize="16"/> | ||
| </Button> | ||
|
|
||
| <Button Command="{Binding InstallPluginCommand}" ToolTip.Tip="{i18n:Localize installLocalPluginTooltip}"> | ||
| <ui:SymbolIcon Symbol="Add" FontSize="16"/> | ||
| </Button> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "PluginStoreSettingsPage.axaml" -type fRepository: Flow-Launcher/Flow.Launcher
Length of output: 144
🏁 Script executed:
cat -n Flow.Launcher.Avalonia/Views/SettingPages/PluginStoreSettingsPage.axaml | head -150Repository: Flow-Launcher/Flow.Launcher
Length of output: 11657
🏁 Script executed:
rg "AutomationProperties" --type xml --type xaml -iRepository: Flow-Launcher/Flow.Launcher
Length of output: 99
🏁 Script executed:
rg "AutomationProperties" -iRepository: Flow-Launcher/Flow.Launcher
Length of output: 1356
🌐 Web query:
Avalonia AutomationProperties Name accessible buttons screen readers
💡 Result:
In Avalonia, the “accessible name” that screen readers announce comes from the UI Automation (UIA) Name property. Avalonia exposes this via the attached property AutomationProperties.Name (it “affects the default value for GetName()”). [2]
What to do for accessible buttons
-
Text button: if the
Buttoncontent is already meaningful text, you often don’t need anything extra (UIA commonly uses the visible text/content as the default name). If your button is icon-only or the content isn’t meaningful to a screen reader, set an explicit name. [5] -
Icon-only button (recommended):
<Button Content="{StaticResource IconSearch}" AutomationProperties.Name="Search" />
(Keep it concise; don’t include words like “button” in the name—screen readers already know the role.) [5]
-
If there’s a visible label elsewhere, point to it with
AutomationProperties.LabeledBy:<TextBlock x:Name="SaveLabel" Text="Save" /> <Button Content="{StaticResource IconSave}" AutomationProperties.LabeledBy="{Binding `#SaveLabel`}" />
(
AutomationProperties.LabeledByis an attached property in Avalonia.) [3] -
Optional extra description: if a name alone isn’t enough, add
AutomationProperties.HelpText. [4]
Where these properties come from in Avalonia
Avalonia defines these accessibility attached properties in Avalonia.Automation.AutomationProperties (including NameProperty, LabeledByProperty, and HelpTextProperty). [1]
Sources: Avalonia API docs for AutomationProperties and related attached properties [1][2][3][4], plus UIA guidance on using AutomationProperties.Name for icon-only buttons and naming rules [5].
Add accessible labels to icon-only buttons.
Icon-only buttons must set AutomationProperties.Name for screen reader support. Tooltips alone are not reliably announced by assistive technologies. Apply the fix to all icon-only buttons across the file: the refresh/sync/add buttons (lines 18-29), the clear filter button (lines 47-52), and the website/source code buttons (lines 116-125).
✅ Example fix (apply similarly to other icon-only buttons)
-<Button Command="{Binding RefreshExternalPluginsCommand}" ToolTip.Tip="{i18n:Localize refresh}">
+<Button Command="{Binding RefreshExternalPluginsCommand}"
+ ToolTip.Tip="{i18n:Localize refresh}"
+ AutomationProperties.Name="{i18n:Localize refresh}">
<ui:SymbolIcon Symbol="Refresh" FontSize="16"/>
</Button>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="10"> | |
| <Button Command="{Binding RefreshExternalPluginsCommand}" ToolTip.Tip="{i18n:Localize refresh}"> | |
| <ui:SymbolIcon Symbol="Refresh" FontSize="16"/> | |
| </Button> | |
| <Button Command="{Binding CheckPluginUpdatesCommand}" ToolTip.Tip="{i18n:Localize checkUpdates}"> | |
| <ui:SymbolIcon Symbol="Sync" FontSize="16"/> | |
| </Button> | |
| <Button Command="{Binding InstallPluginCommand}" ToolTip.Tip="{i18n:Localize installLocalPluginTooltip}"> | |
| <ui:SymbolIcon Symbol="Add" FontSize="16"/> | |
| </Button> | |
| <StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="10"> | |
| <Button Command="{Binding RefreshExternalPluginsCommand}" | |
| ToolTip.Tip="{i18n:Localize refresh}" | |
| AutomationProperties.Name="{i18n:Localize refresh}"> | |
| <ui:SymbolIcon Symbol="Refresh" FontSize="16"/> | |
| </Button> | |
| <Button Command="{Binding CheckPluginUpdatesCommand}" | |
| ToolTip.Tip="{i18n:Localize checkUpdates}" | |
| AutomationProperties.Name="{i18n:Localize checkUpdates}"> | |
| <ui:SymbolIcon Symbol="Sync" FontSize="16"/> | |
| </Button> | |
| <Button Command="{Binding InstallPluginCommand}" | |
| ToolTip.Tip="{i18n:Localize installLocalPluginTooltip}" | |
| AutomationProperties.Name="{i18n:Localize installLocalPluginTooltip}"> | |
| <ui:SymbolIcon Symbol="Add" FontSize="16"/> | |
| </Button> |
🤖 Prompt for AI Agents
In `@Flow.Launcher.Avalonia/Views/SettingPages/PluginStoreSettingsPage.axaml`
around lines 18 - 29, Add accessible names to all icon-only Buttons by setting
AutomationProperties.Name to the localized tooltip string (use the same
i18n:Localize keys used for ToolTip.Tip) so screen readers announce them;
specifically update the Buttons bound to RefreshExternalPluginsCommand,
CheckPluginUpdatesCommand, InstallPluginCommand and likewise add
AutomationProperties.Name to the clear-filter Button (ClearFilterCommand) and
the website/source-code Buttons (OpenPluginWebsiteCommand /
OpenPluginSourceCommand or equivalent) using their existing i18n:Localize
resource keys.
| <ui:SettingsExpander Header="{i18n:Localize port}"> | ||
| <ui:SettingsExpander.Footer> | ||
| <NumericUpDown Value="{Binding ProxyPort}" Minimum="0" Maximum="65535" IsEnabled="{Binding ProxyEnabled}" /> | ||
| </ui:SettingsExpander.Footer> | ||
| </ui:SettingsExpander> |
There was a problem hiding this comment.
Port minimum should be 1, not 0.
Port 0 is a reserved port that cannot be used as an actual proxy server port. Valid ports for proxy configuration are 1-65535.
Proposed fix
<ui:SettingsExpander Header="{i18n:Localize port}">
<ui:SettingsExpander.Footer>
- <NumericUpDown Value="{Binding ProxyPort}" Minimum="0" Maximum="65535" IsEnabled="{Binding ProxyEnabled}" />
+ <NumericUpDown Value="{Binding ProxyPort}" Minimum="1" Maximum="65535" IsEnabled="{Binding ProxyEnabled}" />
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <ui:SettingsExpander Header="{i18n:Localize port}"> | |
| <ui:SettingsExpander.Footer> | |
| <NumericUpDown Value="{Binding ProxyPort}" Minimum="0" Maximum="65535" IsEnabled="{Binding ProxyEnabled}" /> | |
| </ui:SettingsExpander.Footer> | |
| </ui:SettingsExpander> | |
| <ui:SettingsExpander Header="{i18n:Localize port}"> | |
| <ui:SettingsExpander.Footer> | |
| <NumericUpDown Value="{Binding ProxyPort}" Minimum="1" Maximum="65535" IsEnabled="{Binding ProxyEnabled}" /> | |
| </ui:SettingsExpander.Footer> | |
| </ui:SettingsExpander> |
🤖 Prompt for AI Agents
In `@Flow.Launcher.Avalonia/Views/SettingPages/ProxySettingsPage.axaml` around
lines 28 - 32, The NumericUpDown inside SettingsExpander currently allows a
Minimum="0" which permits port 0; change the control's Minimum to "1" so
ProxyPort can only be set in the valid range 1–65535. Locate the NumericUpDown
in ProxySettingsPage.axaml (the element with Value="{Binding ProxyPort}" and
IsEnabled="{Binding ProxyEnabled}") and update its Minimum attribute from 0 to 1
while leaving Maximum="65535" and bindings unchanged.
| <system:String x:Key="pluginStore_RecentlyUpdated">Recently Updated</system:String> | ||
| <system:String x:Key="pluginStore_None">Plugins</system:String> | ||
| <system:String x:Key="pluginStore_Installed">Installed</system:String> | ||
| <system:String x:Key="pluginType_CSharp">C# / .Net</system:String> |
There was a problem hiding this comment.
Use standard “.NET” capitalization in the C# plugin label.
Minor text polish for consistency with Microsoft branding.
✏️ Suggested tweak
- <system:String x:Key="pluginType_CSharp">C# / .Net</system:String>
+ <system:String x:Key="pluginType_CSharp">C# / .NET</system:String>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <system:String x:Key="pluginType_CSharp">C# / .Net</system:String> | |
| <system:String x:Key="pluginType_CSharp">C# / .NET</system:String> |
🤖 Prompt for AI Agents
In `@Flow.Launcher/Languages/en.xaml` at line 229, Update the C# plugin label
string value identified by the resource key "pluginType_CSharp": change the text
from "C# / .Net" to use the standard Microsoft capitalization "C# / .NET" so the
resource value for pluginType_CSharp reads "C# / .NET".
| <system:String x:Key="pluginType_CSharp">C# / .Net</system:String> | ||
| <system:String x:Key="pluginType_Python">Python</system:String> | ||
| <system:String x:Key="pluginType_Nodejs">Node.js</system:String> | ||
| <system:String x:Key="pluginType_Executable">Executable</system:String> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check other language files for the newly added keys.
python - <<'PY'
import re, pathlib, sys
root = pathlib.Path(".")
lang_dir = next(root.glob("**/Flow.Launcher/Languages"), None)
if not lang_dir:
print("Languages directory not found.")
sys.exit(1)
new_keys = {
"pluginType_CSharp",
"pluginType_Python",
"pluginType_Nodejs",
"pluginType_Executable",
"hotkeyFormatTip",
"sourceCode",
"loading",
"plugin_settings_open_in_wpf_window",
"openSettings",
}
def keys(path):
text = path.read_text(encoding="utf-8", errors="ignore")
return set(re.findall(r'x:Key="([^"]+)"', text))
en_keys = keys(lang_dir / "en.xaml")
print(f"en.xaml has {len(en_keys)} total keys")
print(f"Checking for {len(new_keys)} specific keys in other language files...\n")
for f in sorted(lang_dir.glob("*.xaml")):
if f.name == "en.xaml":
continue
file_keys = keys(f)
missing = sorted(new_keys - file_keys)
if missing:
print(f"{f.name}: missing {', '.join(missing)}")
else:
print(f"{f.name}: all keys present")
PYRepository: Flow-Launcher/Flow.Launcher
Length of output: 4968
Add missing localization keys to all non-English language files.
All 9 new keys (pluginType_CSharp, pluginType_Python, pluginType_Nodejs, pluginType_Executable, hotkeyFormatTip, sourceCode, loading, plugin_settings_open_in_wpf_window, openSettings) are missing from all 25 non-English XAML files. Users will see fallback keys or blank text.
🤖 Prompt for AI Agents
In `@Flow.Launcher/Languages/en.xaml` around lines 229 - 232, The non-English XAML
resource files are missing nine new keys (pluginType_CSharp, pluginType_Python,
pluginType_Nodejs, pluginType_Executable, hotkeyFormatTip, sourceCode, loading,
plugin_settings_open_in_wpf_window, openSettings), causing fallbacks or blank
UI; add these exact x:Key entries to each of the 25 non-English language files
(matching the keys used in Flow.Launcher/Languages/en.xaml) and provide
appropriate localized string values (or temporary English text as a fallback) so
the UI displays text for all cultures, ensuring keys and casing match exactly
across files.
- Replace PluginManager.AllPlugins with GetAllLoadedPlugins() - Replace PluginManager.NonGlobalPlugins with GetNonGlobalPlugins() - Fix InitializePluginsAsync to pass IResultUpdateRegister parameter - Fix QueryBuilder.Build to pass required 3 parameters - Implement IResultUpdateRegister interface on MainViewModel
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In `@Flow.Launcher.Avalonia/ViewModel/MainViewModel.cs`:
- Around line 267-269: The code cancels but does not dispose the previous
CancellationTokenSource (_queryTokenSource) in MainViewModel, which can leak
registrations; before reassigning _queryTokenSource create logic should call
_queryTokenSource.Cancel(); _queryTokenSource.Dispose(); (or safely Dispose if
not null) and then assign a new CancellationTokenSource so the old CTS is
properly cleaned up—update the block that currently uses
_queryTokenSource?.Cancel(); _queryTokenSource = new CancellationTokenSource();
var token = _queryTokenSource.Token; to cancel and dispose the existing
_queryTokenSource prior to creating the new one.
- Around line 193-202: The RegisterResultsUpdatedEvent stub must hook plugins
implementing IResultUpdated into the Avalonia result pipeline: detect if the
PluginPair (parameter name pair) has an IResultUpdated implementation, subscribe
its ResultsUpdated event to push updates into the existing
_resultsUpdateChannelWriter (write the event payload or a wrapper matching the
channel's expected type), and ensure you unsubscribe on plugin unload if/where
appropriate; implement this inside RegisterResultsUpdatedEvent so the Avalonia
pipeline receives incremental updates the same way the WPF implementation does.
In
`@Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj`:
- Around line 72-80: The CopyToAvaloniaOutput Target currently copies
PluginFiles but does not remove deleted/stale files from the AvaloniaPluginDir;
modify the CopyToAvaloniaOutput target (referencing Target Name
"CopyToAvaloniaOutput", PropertyGroup "AvaloniaPluginDir", ItemGroup
"PluginFiles" and the Copy task) to clean the destination before copying by
deleting or removing the $(AvaloniaPluginDir) contents (e.g., add a Delete or
RemoveDir step for $(AvaloniaPluginDir) and recreate the directory with MakeDir)
so only current build outputs are present after the target runs.
In `@Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs`:
- Around line 544-559: PromptUserSelectFolderAsync currently ignores the
initialDirectory parameter; convert the provided string into an IStorageFolder
and set it as SuggestedStartLocation on the FolderPickerOpenOptions so the
picker opens at the intended folder. Specifically, in
PromptUserSelectFolderAsync call Avalonia's
StorageProvider.TryGetFolderFromPathAsync(new Uri(path)) (guarding null/empty
initialDirectory and catching any Uri/lookup failures) and assign the resulting
IStorageFolder to SuggestedStartLocation on the FolderPickerOpenOptions; apply
the same pattern in PromptUserSelectFileAsync (use
FilePickerOpenOptions.SuggestedStartLocation) to preserve the existing UX when
an initialDirectory is provided.
In
`@Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj`:
- Around line 61-64: The project uses outdated package versions for Avalonia and
FluentAvaloniaUI; update the PackageReference entries named "Avalonia" and
"FluentAvaloniaUI" in Flow.Launcher.Plugin.WebSearch.csproj to at least
Version="11.3.11" for Avalonia and Version="2.5.0" for FluentAvaloniaUI, save
the .csproj, then run a restore/build to ensure the new packages resolve
correctly and update any binding/namespace changes if the compiler flags them.
🧹 Nitpick comments (3)
Flow.Launcher.Infrastructure/Logger/Log.cs (2)
1-1: Unused import.
System.Diagnosticsdoes not appear to be used in this file.Demystify()comes from the Ben.Demystifier package, not from System.Diagnostics.🧹 Suggested removal
-using System.Diagnostics;
148-150: Console output should be conditional and respect log level settings.This console output runs unconditionally in all builds and bypasses the log level configured via
SetLogLevel(). The existing debug target (lines 55-61) is wrapped in#if DEBUGfor consistency. Consider applying the same pattern here to avoid unnecessary overhead in release builds and to maintain consistency.♻️ Proposed fix
logger.Log(level, message); - - // Also output to console for easy debugging - System.Console.WriteLine($"[{level}] {classNameWithMethod}: {message}"); +#if DEBUG + // Also output to console for easy debugging + Console.WriteLine($"[{level}] {classNameWithMethod}: {message}"); +#endif }You'll also need to add
using System;at the top if not already implicitly available.Plugins/Flow.Launcher.Plugin.Program/Main.cs (1)
440-445: Avoid Console.WriteLine in plugin settings creation.
Direct console output is noisy in GUI contexts; the structured logger already covers this.♻️ Suggested tweak
- System.Console.WriteLine("Program plugin: CreateSettingPanelAvalonia called!"); Context.API.LogInfo(ClassName, "Creating Avalonia setting panel");
| /// <summary> | ||
| /// Register a plugin to receive results updated event. | ||
| /// Required by IResultUpdateRegister for plugin initialization. | ||
| /// </summary> | ||
| public void RegisterResultsUpdatedEvent(PluginPair pair) | ||
| { | ||
| // Avalonia uses a simplified result update model - plugins that implement | ||
| // IResultUpdated will have their events registered here when needed. | ||
| // For now, this is a stub as the basic query flow handles result updates. | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Search for IResultUpdated and IResultUpdateRegister usage in the codebase
rg -i "IResultUpdated|IResultUpdateRegister" --type csRepository: Flow-Launcher/Flow.Launcher
Length of output: 1465
🏁 Script executed:
# Get the RegisterResultsUpdatedEvent implementation from the original Flow.Launcher
rg -A 15 "public void RegisterResultsUpdatedEvent" Flow.Launcher/ViewModel/MainViewModel.csRepository: Flow-Launcher/Flow.Launcher
Length of output: 652
🏁 Script executed:
# Check the Avalonia implementation more carefully
cat -n Flow.Launcher.Avalonia/ViewModel/MainViewModel.cs | sed -n '190,210p'Repository: Flow-Launcher/Flow.Launcher
Length of output: 945
🏁 Script executed:
# Check how PluginManager initializes plugins with the register
rg -B 5 -A 10 "InitializePluginsAsync" Flow.Launcher.Core/Plugin/PluginManager.csRepository: Flow-Launcher/Flow.Launcher
Length of output: 775
🏁 Script executed:
# Check if there are any other event subscriptions to ResultsUpdated in Avalonia
rg "ResultsUpdated" Flow.Launcher.Avalonia/Repository: Flow-Launcher/Flow.Launcher
Length of output: 181
🏁 Script executed:
# Check what the basic query flow looks like in Avalonia
rg -B 5 -A 15 "QueryAsync|ResultCollection" Flow.Launcher.Avalonia/ViewModel/MainViewModel.cs | head -80Repository: Flow-Launcher/Flow.Launcher
Length of output: 1528
🏁 Script executed:
# Check the full QueryAsync implementation in Avalonia to see if it handles incremental updates
rg -A 50 "private async Task QueryAsync" Flow.Launcher.Avalonia/ViewModel/MainViewModel.csRepository: Flow-Launcher/Flow.Launcher
Length of output: 1650
🏁 Script executed:
# Check if there's any result update handling or callbacks in Avalonia
rg "Results|result|Update" Flow.Launcher.Avalonia/ViewModel/MainViewModel.cs | grep -i "add\|insert\|update" | head -20Repository: Flow-Launcher/Flow.Launcher
Length of output: 1360
🏁 Script executed:
# Check IResultUpdated interface to understand what events plugins expect
cat Flow.Launcher.Plugin/Interfaces/IResultUpdated.csRepository: Flow-Launcher/Flow.Launcher
Length of output: 1335
🏁 Script executed:
# Double-check if ResultsUpdated event subscription exists anywhere in Avalonia codebase
rg "ResultsUpdated\s*\+=" Flow.Launcher.Avalonia/Repository: Flow-Launcher/Flow.Launcher
Length of output: 53
🏁 Script executed:
# Verify if ProcessResultUpdatesAsync or similar handles event-based updates
rg -B 5 -A 20 "ProcessResultUpdatesAsync" Flow.Launcher.Avalonia/ViewModel/MainViewModel.csRepository: Flow-Launcher/Flow.Launcher
Length of output: 2110
🏁 Script executed:
# Check if _resultsUpdateChannelWriter is written to elsewhere for plugin events
rg "_resultsUpdateChannelWriter" Flow.Launcher.Avalonia/ViewModel/MainViewModel.csRepository: Flow-Launcher/Flow.Launcher
Length of output: 432
🏁 Script executed:
# Verify the complete RegisterResultsUpdatedEvent implementation one more time
cat -n Flow.Launcher.Avalonia/ViewModel/MainViewModel.cs | sed -n '193,202p'Repository: Flow-Launcher/Flow.Launcher
Length of output: 623
🏁 Script executed:
# Check if there's any plugin.ResultsUpdated subscription in Avalonia
rg "plugin\.ResultsUpdated|\.ResultsUpdated\s*\+=" Flow.Launcher.Avalonia/ViewModel/Repository: Flow-Launcher/Flow.Launcher
Length of output: 53
IResultUpdated events are not wired into the result update pipeline.
The RegisterResultsUpdatedEvent method is a stub, so plugins implementing IResultUpdated cannot emit incremental result updates. While the Avalonia implementation has a channel-based result pipeline, it only handles initial results from parallel plugin queries—not event-based incremental updates from plugins like WebSearch.
Wire the plugin's ResultsUpdated event into the existing _resultsUpdateChannelWriter to match the WPF implementation, or remove IResultUpdateRegister until this feature is supported.
🤖 Prompt for AI Agents
In `@Flow.Launcher.Avalonia/ViewModel/MainViewModel.cs` around lines 193 - 202,
The RegisterResultsUpdatedEvent stub must hook plugins implementing
IResultUpdated into the Avalonia result pipeline: detect if the PluginPair
(parameter name pair) has an IResultUpdated implementation, subscribe its
ResultsUpdated event to push updates into the existing
_resultsUpdateChannelWriter (write the event payload or a wrapper matching the
channel's expected type), and ensure you unsubscribe on plugin unload if/where
appropriate; implement this inside RegisterResultsUpdatedEvent so the Avalonia
pipeline receives incremental updates the same way the WPF implementation does.
| _queryTokenSource?.Cancel(); | ||
| _queryTokenSource = new CancellationTokenSource(); | ||
| var token = _queryTokenSource.Token; |
There was a problem hiding this comment.
Dispose the previous CancellationTokenSource.
Cancelling without disposing can leak registrations over time during rapid query changes.
🛠️ Suggested fix
- _queryTokenSource?.Cancel();
- _queryTokenSource = new CancellationTokenSource();
+ _queryTokenSource?.Cancel();
+ _queryTokenSource?.Dispose();
+ _queryTokenSource = new CancellationTokenSource();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| _queryTokenSource?.Cancel(); | |
| _queryTokenSource = new CancellationTokenSource(); | |
| var token = _queryTokenSource.Token; | |
| _queryTokenSource?.Cancel(); | |
| _queryTokenSource?.Dispose(); | |
| _queryTokenSource = new CancellationTokenSource(); | |
| var token = _queryTokenSource.Token; |
🤖 Prompt for AI Agents
In `@Flow.Launcher.Avalonia/ViewModel/MainViewModel.cs` around lines 267 - 269,
The code cancels but does not dispose the previous CancellationTokenSource
(_queryTokenSource) in MainViewModel, which can leak registrations; before
reassigning _queryTokenSource create logic should call
_queryTokenSource.Cancel(); _queryTokenSource.Dispose(); (or safely Dispose if
not null) and then assign a new CancellationTokenSource so the old CTS is
properly cleaned up—update the block that currently uses
_queryTokenSource?.Cancel(); _queryTokenSource = new CancellationTokenSource();
var token = _queryTokenSource.Token; to cancel and dispose the existing
_queryTokenSource prior to creating the new one.
| <!-- Copy plugin output to Avalonia build directory --> | ||
| <Target Name="CopyToAvaloniaOutput" AfterTargets="Build"> | ||
| <PropertyGroup> | ||
| <AvaloniaPluginDir>..\..\Output\$(Configuration)\Avalonia\Plugins\$(AssemblyName)\</AvaloniaPluginDir> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <PluginFiles Include="$(OutputPath)**\*.*" /> | ||
| </ItemGroup> | ||
| <Copy SourceFiles="@(PluginFiles)" DestinationFolder="$(AvaloniaPluginDir)%(RecursiveDir)" SkipUnchangedFiles="true" /> |
There was a problem hiding this comment.
Stale assemblies may linger in the Avalonia output directory.
Copy won’t remove files that were deleted from the build output, so old assemblies can remain and be loaded. Consider cleaning the destination before copying.
🧹 Suggested fix
<Target Name="CopyToAvaloniaOutput" AfterTargets="Build">
<PropertyGroup>
<AvaloniaPluginDir>..\..\Output\$(Configuration)\Avalonia\Plugins\$(AssemblyName)\</AvaloniaPluginDir>
</PropertyGroup>
<ItemGroup>
<PluginFiles Include="$(OutputPath)**\*.*" />
</ItemGroup>
+ <RemoveDir Directories="$(AvaloniaPluginDir)" />
<Copy SourceFiles="@(PluginFiles)" DestinationFolder="$(AvaloniaPluginDir)%(RecursiveDir)" SkipUnchangedFiles="true" />
</Target>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <!-- Copy plugin output to Avalonia build directory --> | |
| <Target Name="CopyToAvaloniaOutput" AfterTargets="Build"> | |
| <PropertyGroup> | |
| <AvaloniaPluginDir>..\..\Output\$(Configuration)\Avalonia\Plugins\$(AssemblyName)\</AvaloniaPluginDir> | |
| </PropertyGroup> | |
| <ItemGroup> | |
| <PluginFiles Include="$(OutputPath)**\*.*" /> | |
| </ItemGroup> | |
| <Copy SourceFiles="@(PluginFiles)" DestinationFolder="$(AvaloniaPluginDir)%(RecursiveDir)" SkipUnchangedFiles="true" /> | |
| <!-- Copy plugin output to Avalonia build directory --> | |
| <Target Name="CopyToAvaloniaOutput" AfterTargets="Build"> | |
| <PropertyGroup> | |
| <AvaloniaPluginDir>..\..\Output\$(Configuration)\Avalonia\Plugins\$(AssemblyName)\</AvaloniaPluginDir> | |
| </PropertyGroup> | |
| <ItemGroup> | |
| <PluginFiles Include="$(OutputPath)**\*.*" /> | |
| </ItemGroup> | |
| <RemoveDir Directories="$(AvaloniaPluginDir)" /> | |
| <Copy SourceFiles="@(PluginFiles)" DestinationFolder="$(AvaloniaPluginDir)%(RecursiveDir)" SkipUnchangedFiles="true" /> | |
| </Target> |
🤖 Prompt for AI Agents
In
`@Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj`
around lines 72 - 80, The CopyToAvaloniaOutput Target currently copies
PluginFiles but does not remove deleted/stale files from the AvaloniaPluginDir;
modify the CopyToAvaloniaOutput target (referencing Target Name
"CopyToAvaloniaOutput", PropertyGroup "AvaloniaPluginDir", ItemGroup
"PluginFiles" and the Copy task) to clean the destination before copying by
deleting or removing the $(AvaloniaPluginDir) contents (e.g., add a Delete or
RemoveDir step for $(AvaloniaPluginDir) and recreate the directory with MakeDir)
so only current build outputs are present after the target runs.
| private static async Task<string?> PromptUserSelectFolderAsync(string? initialDirectory = null) | ||
| { | ||
| var mainWindow = AvaloniaApp.Current?.ApplicationLifetime is global::Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktop | ||
| ? desktop.MainWindow | ||
| : null; | ||
|
|
||
| if (folderBrowserDialog.ShowDialog() != DialogResult.OK) | ||
| return path; | ||
| if (mainWindow == null) return null; | ||
|
|
||
| path = folderBrowserDialog.SelectedPath; | ||
| } | ||
| else if (type is ResultType.File) | ||
| var folders = await mainWindow.StorageProvider.OpenFolderPickerAsync(new global::Avalonia.Platform.Storage.FolderPickerOpenOptions | ||
| { | ||
| var openFileDialog = new OpenFileDialog(); | ||
| if (initialDirectory is not null) | ||
| openFileDialog.InitialDirectory = initialDirectory; | ||
| AllowMultiple = false | ||
| }); | ||
|
|
||
| if (openFileDialog.ShowDialog() != DialogResult.OK) | ||
| return path; | ||
| return folders.Count > 0 ? folders[0].Path.LocalPath : null; | ||
| } | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Avalonia FolderPickerOpenOptions SuggestedStartLocation initial directory property
💡 Result:
In Avalonia, the “initial directory” for the folder picker is set via PickerOptions.SuggestedStartLocation (inherited by FolderPickerOpenOptions). It’s an IStorageFolder?, not a string path. [1][2]
Example (set start folder from a filesystem path):
var topLevel = TopLevel.GetTopLevel(this);
var start = await topLevel.StorageProvider
.TryGetFolderFromPathAsync(new Uri(@"file:///C:/Projects"));
var folders = await topLevel.StorageProvider.OpenFolderPickerAsync(
new FolderPickerOpenOptions
{
SuggestedStartLocation = start,
AllowMultiple = false
});You can also use a well-known folder (when supported) via TryGetWellKnownFolderAsync(...). [1]
Notes:
- This is only a suggestion; the OS/dialog may ignore it (missing access, folder doesn’t exist, some Linux pickers, etc.). [1]
Sources:
[1] Avalonia Docs – File Picker Options / SuggestedStartLocation
[2] Avalonia API Reference – PickerOptions.SuggestedStartLocation property
🏁 Script executed:
# First, let's locate and examine the file to verify the current state
fd "SettingsViewModel.cs" --type fRepository: Flow-Launcher/Flow.Launcher
Length of output: 992
🏁 Script executed:
# Read the file and show the relevant sections
wc -l "Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs"Repository: Flow-Launcher/Flow.Launcher
Length of output: 144
🏁 Script executed:
# Read lines 544-559
sed -n '544,559p' "Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs" | cat -nRepository: Flow-Launcher/Flow.Launcher
Length of output: 872
🏁 Script executed:
# Read lines 560-573 to see the second occurrence
sed -n '560,573p' "Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs" | cat -nRepository: Flow-Launcher/Flow.Launcher
Length of output: 835
🏁 Script executed:
# Also check the method signatures to see if both have initialDirectory parameter
sed -n '540,575p' "Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs" | cat -nRepository: Flow-Launcher/Flow.Launcher
Length of output: 1740
Use SuggestedStartLocation in Avalonia picker options to honor initialDirectory.
Both PromptUserSelectFolderAsync() and PromptUserSelectFileAsync() accept an initialDirectory parameter that is never used. Avalonia's FolderPickerOpenOptions and FilePickerOpenOptions support SuggestedStartLocation (an IStorageFolder? property). Convert the string path to IStorageFolder via TryGetFolderFromPathAsync(new Uri(...)) and set it to preserve the existing UX.
🤖 Prompt for AI Agents
In `@Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs` around
lines 544 - 559, PromptUserSelectFolderAsync currently ignores the
initialDirectory parameter; convert the provided string into an IStorageFolder
and set it as SuggestedStartLocation on the FolderPickerOpenOptions so the
picker opens at the intended folder. Specifically, in
PromptUserSelectFolderAsync call Avalonia's
StorageProvider.TryGetFolderFromPathAsync(new Uri(path)) (guarding null/empty
initialDirectory and catching any Uri/lookup failures) and assign the resulting
IStorageFolder to SuggestedStartLocation on the FolderPickerOpenOptions; apply
the same pattern in PromptUserSelectFileAsync (use
FilePickerOpenOptions.SuggestedStartLocation) to preserve the existing UX when
an initialDirectory is provided.
| <ItemGroup> | ||
| <PackageReference Include="Avalonia" Version="11.2.3" /> | ||
| <PackageReference Include="FluentAvaloniaUI" Version="2.2.0" /> | ||
| </ItemGroup> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check latest versions of Avalonia packages on NuGet
echo "Checking Avalonia version:"
curl -s "https://api.nuget.org/v3-flatcontainer/avalonia/index.json" | jq -r '.versions[-5:]'
echo ""
echo "Checking FluentAvaloniaUI version:"
curl -s "https://api.nuget.org/v3-flatcontainer/fluentavaloniaui/index.json" | jq -r '.versions[-5:]'Repository: Flow-Launcher/Flow.Launcher
Length of output: 265
Update Avalonia and FluentAvaloniaUI package versions to current releases.
The current versions are outdated: Avalonia 11.2.3 (latest: 11.3.11) and FluentAvaloniaUI 2.2.0 (latest: 2.5.0). Update to at least FluentAvaloniaUI 2.5.0 and Avalonia 11.3.11 to receive bug fixes and improvements.
🤖 Prompt for AI Agents
In `@Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj`
around lines 61 - 64, The project uses outdated package versions for Avalonia
and FluentAvaloniaUI; update the PackageReference entries named "Avalonia" and
"FluentAvaloniaUI" in Flow.Launcher.Plugin.WebSearch.csproj to at least
Version="11.3.11" for Avalonia and Version="2.5.0" for FluentAvaloniaUI, save
the .csproj, then run a restore/build to ensure the new packages resolve
correctly and update any binding/namespace changes if the compiler flags them.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
2 issues found across 115 files (changes from recent commits).
Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="AGENTS.md">
<violation number="1" location="AGENTS.md:166">
P3: The gotchas list says plugins run in separate AppDomains, but the code loads plugins via AssemblyLoadContext in the same process. This mismatch can mislead contributors about isolation behavior; update the doc to reflect the actual loading model.</violation>
</file>
<file name="Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj">
<violation number="1" location="Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj:84">
P2: The PR adds several post-build cleanup targets using PowerShell scripts to delete files (runtimes, XML docs, duplicate DLLs). This violates the principle of configuring the build properly instead of relying on post-build removal.
1. **Runtimes**: Instead of deleting `linux-x64` etc. manually, set `<RuntimeIdentifier>win-x64</RuntimeIdentifier>` in the `PropertyGroup`. This prevents NuGet from restoring/copying non-Windows assets.
2. **XML Docs**: Instead of deleting `.xml` files, use `<AllowedReferenceRelatedFileExtensions>.pdb</AllowedReferenceRelatedFileExtensions>` in the `PropertyGroup` to exclude XML documentation from dependencies.
3. **Duplicate DLLs**: Instead of deleting duplicates from `Plugins`, configure the referenced Plugin projects to set `<Private>false</Private>` (CopyLocal=false) for assemblies already provided by the host.
(Based on your team's feedback about avoiding post-build file removal; configure build properly.) [FEEDBACK_USED]</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| @@ -0,0 +1,153 @@ | |||
| <Project Sdk="Microsoft.NET.Sdk"> | |||
There was a problem hiding this comment.
P2: The PR adds several post-build cleanup targets using PowerShell scripts to delete files (runtimes, XML docs, duplicate DLLs). This violates the principle of configuring the build properly instead of relying on post-build removal.
- Runtimes: Instead of deleting
linux-x64etc. manually, set<RuntimeIdentifier>win-x64</RuntimeIdentifier>in thePropertyGroup. This prevents NuGet from restoring/copying non-Windows assets. - XML Docs: Instead of deleting
.xmlfiles, use<AllowedReferenceRelatedFileExtensions>.pdb</AllowedReferenceRelatedFileExtensions>in thePropertyGroupto exclude XML documentation from dependencies. - Duplicate DLLs: Instead of deleting duplicates from
Plugins, configure the referenced Plugin projects to set<Private>false</Private>(CopyLocal=false) for assemblies already provided by the host.
(Based on your team's feedback about avoiding post-build file removal; configure build properly.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj, line 84:
<comment>The PR adds several post-build cleanup targets using PowerShell scripts to delete files (runtimes, XML docs, duplicate DLLs). This violates the principle of configuring the build properly instead of relying on post-build removal.
1. **Runtimes**: Instead of deleting `linux-x64` etc. manually, set `<RuntimeIdentifier>win-x64</RuntimeIdentifier>` in the `PropertyGroup`. This prevents NuGet from restoring/copying non-Windows assets.
2. **XML Docs**: Instead of deleting `.xml` files, use `<AllowedReferenceRelatedFileExtensions>.pdb</AllowedReferenceRelatedFileExtensions>` in the `PropertyGroup` to exclude XML documentation from dependencies.
3. **Duplicate DLLs**: Instead of deleting duplicates from `Plugins`, configure the referenced Plugin projects to set `<Private>false</Private>` (CopyLocal=false) for assemblies already provided by the host.
(Based on your team's feedback about avoiding post-build file removal; configure build properly.) </comment>
<file context>
@@ -78,4 +79,75 @@
+ <!-- Remove duplicate DLLs from plugins that already exist in the root folder -->
+ <!-- Remove by name since root typically has newer compatible versions -->
+ <Target Name="RemoveDuplicatePluginDlls" AfterTargets="Build" Condition="Exists('$(OutputPath)Plugins')">
+ <Exec Command="powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "Get-ChildItem '$(OutputPath.TrimEnd('\'))' -Filter '*.dll' | ForEach-Object { $name = $_.Name; Get-ChildItem '$(OutputPath)Plugins' -Recurse -Filter $name | Remove-Item -Force }"" IgnoreExitCode="true" />
+ </Target>
</file context>
|
|
||
| 1. Build order matters - plugins must build before WPF | ||
| 2. Build kills running `Flow.Launcher.exe` automatically | ||
| 3. Plugins run in separate app domains |
There was a problem hiding this comment.
P3: The gotchas list says plugins run in separate AppDomains, but the code loads plugins via AssemblyLoadContext in the same process. This mismatch can mislead contributors about isolation behavior; update the doc to reflect the actual loading model.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At AGENTS.md, line 166:
<comment>The gotchas list says plugins run in separate AppDomains, but the code loads plugins via AssemblyLoadContext in the same process. This mismatch can mislead contributors about isolation behavior; update the doc to reflect the actual loading model.</comment>
<file context>
@@ -317,114 +145,31 @@ public class FuzzyMatcherTest
----
+1. Build order matters - plugins must build before WPF
+2. Build kills running `Flow.Launcher.exe` automatically
+3. Plugins run in separate app domains
+4. Settings auto-save via Fody PropertyChanged
+5. Some tests require Windows Search service (`WSearch`)
</file context>
| 3. Plugins run in separate app domains | |
| 3. Plugins are loaded via AssemblyLoadContext in the same process (no AppDomain isolation) |
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 Fix all issues with AI agents
In `@Flow.Launcher.Avalonia/AvaloniaPublicAPI.cs`:
- Around line 27-52: ChangeQuery currently sets _getMainViewModel().QueryText
unconditionally and ignores the requery flag; modify ChangeQuery to retrieve the
MainViewModel via _getMainViewModel(), compare the incoming query to
vm.QueryText, set vm.QueryText if different, and if requery is true and the text
is unchanged call the ViewModel method that re-executes the search (e.g.,
vm.Query() or the existing vm.ChangeQueryText/ChangeQuery method that honors
isReQuery) so plugins passing requery=true will force a re-execution; update
references to ChangeQuery in AvaloniaPublicAPI to use vm methods rather than
directly assigning QueryText when requery behavior is required.
In `@Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj`:
- Around line 98-101: The RemoveXmlDocFiles target is deleting project items
because the Delete task uses Files="@(None)" which matches source XML assets;
remove the Delete Files="@(None)" line from the Target RemoveXmlDocFiles and
keep only the output-folder cleanup Exec command (the PowerShell Get-ChildItem
... '$(OutputPath.TrimEnd('\'))' -Recurse -Filter '*.xml' | Remove-Item ...) so
that only built output XML files are removed and repo/source XML files are
preserved.
- Around line 34-46: The project references older package versions causing
missed updates/security patches; update the PackageReference entries in
Flow.Launcher.Avalonia.csproj for the listed packages: set Avalonia,
Avalonia.Desktop, Avalonia.Themes.Fluent and Avalonia.Fonts.Inter from 11.2.3 to
11.3.11, set FluentAvaloniaUI from 2.2.0 to 2.4.1, set DynamicData from 9.0.4 to
9.4.1, and set Microsoft.Extensions.DependencyInjection from 9.0.0 to 9.0.12
(leave the Debug-only Avalonia.Diagnostics condition as-is); modify the Version
attribute for each corresponding PackageReference element (e.g.,
PackageReference Include="Avalonia" Version="...") to the new version numbers
and restore/build to verify no compatibility issues.
In `@Flow.Launcher.Core/Plugin/JsonRPCV2Models/codemap.md`:
- Line 3: Replace the incorrect placeholder reference "Explorer" in the codemap
header comment with the correct component name for this file (e.g.,
"JsonRPCV2Models") or a neutral description; update the comment in codemap.md so
it accurately describes the architectural scope for the JsonRPCV2Models
directory (look for the string "Explorer" in codemap.md and change it to
"JsonRPCV2Models" or an appropriate description).
In `@Plugins/Flow.Launcher.Plugin.Shell/ViewModels/codemap.md`:
- Line 3: Replace the incorrect placeholder comment referencing "Explorer" in
the codemap.md file with an accurate reference to the Shell plugin ViewModels;
locate the HTML comment "<!-- Explorer: Fill in this section with architectural
understanding -->" and change its text to something like "<!-- Shell Plugin
ViewModels: Fill in this section with architectural understanding -->" (or
equivalent wording that correctly identifies the Shell plugin's ViewModels
directory).
In `@Scripts/compare_sizes.ps1`:
- Line 28: The difference formula on the Write-Host line is confusing because it
adds $wpfPlugins.Sum back into ($avTotal - $wpfTotal) even though $wpfTotal was
already computed excluding the Avalonia subfolder; update the expression to a
clear comparison (use ($avTotal - $wpfTotal) for "how much larger Avalonia is
than WPF") and add a brief comment above the Write-Host mentioning what totals
include/exclude, referencing the variables $avTotal, $wpfTotal, and
$wpfPlugins.Sum so future readers understand the intent.
In `@Scripts/plugin_runtimes.ps1`:
- Around line 4-5: The script fails if the Avalonia plugins path is missing
because Get-ChildItem on "Output/Release/Avalonia/Plugins" will throw; add a
Test-Path check for that directory before calling Get-ChildItem (guarding the
logic that sets $pluginRuntimes) and perform an early, non-failing exit (or
return/continue) when the path doesn't exist so the rest of the script can run
without hard failure.
In `@Scripts/size_analysis.ps1`:
- Around line 39-40: The log line claims the Avalonia subfolder is excluded but
calls Get-FolderSize('Output\Debug') which includes all subfolders; either
update the message or change the size calculation to exclude the Avalonia
folder. Fix options: 1) modify the Write-Host text ("excluding Avalonia
subfolder") to remove the exclusion claim, or 2) update the size computation by
calling a helper or filter that sums Get-ChildItem results for 'Output\Debug'
while excluding 'Output\Debug\Avalonia' (use the existing Get-FolderSize
function name as a reference) and then use that result in the Write-Host for the
WPF Debug total. Ensure the affected lines are the Write-Host calls that
reference "=== WPF Debug ===" and the Total using Get-FolderSize 'Output\Debug'.
🧹 Nitpick comments (11)
Scripts/plugin_runtimes.ps1 (1)
8-17: Make size/platform reporting resilient to empty or erroring folders.
Measure-Objectcan return$null(no files), andGet-ChildItem -Recursecan error on access issues. Coerce size to 0 and handle empty platforms for clearer output.♻️ Proposed fix (resilient size/platforms)
- $subdirs = Get-ChildItem $runtimeDir.FullName -Directory - $size = (Get-ChildItem $runtimeDir.FullName -Recurse -File | Measure-Object Length -Sum).Sum + $subdirs = Get-ChildItem $runtimeDir.FullName -Directory -ErrorAction SilentlyContinue + $size = (Get-ChildItem $runtimeDir.FullName -Recurse -File -ErrorAction SilentlyContinue | + Measure-Object Length -Sum).Sum + if (-not $size) { $size = 0 } @@ - Write-Host (" Platforms: {0}" -f ($subdirs.Name -join ", ")) + $platforms = if ($subdirs) { $subdirs.Name -join ", " } else { "None" } + Write-Host (" Platforms: {0}" -f $platforms)Scripts/plugin_analysis.ps1 (2)
5-6: Add existence checks for root directories.Lines 5 and 6 access
Output/DebugandOutput/Debug/Avaloniawithout verifying they exist. If either path is missing (e.g., before first build), the script will throw errors. Consider wrapping withTest-Pathchecks for robustness.Suggested fix
# Get root DLLs for WPF and Avalonia -$wpfRootDlls = Get-ChildItem "Output/Debug" -Filter "*.dll" | Select-Object -ExpandProperty Name -$avRootDlls = Get-ChildItem "Output/Debug/Avalonia" -Filter "*.dll" | Select-Object -ExpandProperty Name +$wpfRootDlls = @() +$avRootDlls = @() +if (Test-Path "Output/Debug") { + $wpfRootDlls = Get-ChildItem "Output/Debug" -Filter "*.dll" | Select-Object -ExpandProperty Name +} +if (Test-Path "Output/Debug/Avalonia") { + $avRootDlls = Get-ChildItem "Output/Debug/Avalonia" -Filter "*.dll" | Select-Object -ExpandProperty Name +}
27-30: Missing existence check for Avalonia Plugins directory.Line 13 checks
Test-Path "Output/Debug/Avalonia/Plugins"before the duplicate calculation, but line 27 accesses the same path without a guard. For consistency and to avoid errors when the Plugins folder doesn't exist, wrap this section similarly.Suggested fix
# Show each plugin size in Avalonia Write-Host "" Write-Host "=== Avalonia plugin sizes ===" -ForegroundColor Yellow -Get-ChildItem "Output/Debug/Avalonia/Plugins" -Directory | ForEach-Object { - $size = (Get-ChildItem $_.FullName -Recurse -File | Measure-Object Length -Sum).Sum - [PSCustomObject]@{ Name = $_.Name; SizeMB = [math]::Round($size / 1MB, 2) } -} | Sort-Object SizeMB -Descending | Format-Table -AutoSize +if (Test-Path "Output/Debug/Avalonia/Plugins") { + Get-ChildItem "Output/Debug/Avalonia/Plugins" -Directory | ForEach-Object { + $size = (Get-ChildItem $_.FullName -Recurse -File | Measure-Object Length -Sum).Sum + [PSCustomObject]@{ Name = $_.Name; SizeMB = [math]::Round($size / 1MB, 2) } + } | Sort-Object SizeMB -Descending | Format-Table -AutoSize +} else { + Write-Host "Plugins folder not found" +}Scripts/show_remaining.ps1 (2)
4-8: Add existence checks before accessing directories.The script assumes
Output/Debug/AvaloniaandOutput/Debug/Avalonia/Pluginsexist. If run before a build completes or in an incorrect working directory, it will fail with errors.Suggested fix
# Show remaining duplicates with details Write-Host "=== Remaining Duplicate DLLs in Avalonia Plugins ===" -ForegroundColor Cyan +if (-not (Test-Path "Output/Debug/Avalonia")) { + Write-Host "Avalonia output not found. Run build first." -ForegroundColor Red + exit 1 +} + $avRootDlls = Get-ChildItem "Output/Debug/Avalonia" -Filter "*.dll" $remaining = @() + +if (-not (Test-Path "Output/Debug/Avalonia/Plugins")) { + Write-Host "No Plugins folder found" + exit 0 +}
16-24: Handle potential null version values.
FileVersioncan be null for some DLLs (e.g., native binaries without version resources). The comparison at line 24 would still work but may produce misleading results when both are null (showingSameVersion = True).Suggested improvement
# Check if version matches - $rootVersion = $matchingRoot.VersionInfo.FileVersion - $pluginVersion = $pluginDll.VersionInfo.FileVersion + $rootVersion = $matchingRoot.VersionInfo.FileVersion ?? "N/A" + $pluginVersion = $pluginDll.VersionInfo.FileVersion ?? "N/A" $remaining += [PSCustomObject]@{ Plugin = $pluginName DLL = $pluginDll.Name SizeMB = [math]::Round($pluginDll.Length / 1MB, 2) RootVersion = $rootVersion PluginVersion = $pluginVersion - SameVersion = ($rootVersion -eq $pluginVersion) + SameVersion = ($rootVersion -ne "N/A" -and $rootVersion -eq $pluginVersion) }Scripts/find_duplicates.ps1 (2)
4-10: Add existence checks for robustness.Similar to other scripts, this will fail if
Output/Debug/AvaloniaorOutput/Debug/Avalonia/Pluginsdirectories don't exist.Suggested fix
# Duplicate DLL analysis Write-Host "=== Duplicate DLLs in Avalonia Plugins ===" -ForegroundColor Cyan +if (-not (Test-Path "Output/Debug/Avalonia")) { + Write-Host "Avalonia output directory not found" -ForegroundColor Red + exit 1 +} + $avRootDlls = Get-ChildItem "Output/Debug/Avalonia" -Filter "*.dll" $avRootDllNames = $avRootDlls | Select-Object -ExpandProperty Name $totalDuplicateSize = 0 $duplicatesByPlugin = @{} +if (-not (Test-Path "Output/Debug/Avalonia/Plugins")) { + Write-Host "No Plugins folder to analyze" + exit 0 +} + Get-ChildItem "Output/Debug/Avalonia/Plugins" -Directory | ForEach-Object {
36-40: Clarify the dedup calculation scope.Line 37 calculates
$currentSizefrom all files underOutput/Debug/Avaloniarecursively, which includes the Plugins subdirectory. The$reducedSizeis correct since duplicates are only counted from within Plugins. Consider adding a comment to clarify this is intentional, or separate the metrics for clarity.Suggested clarification
# Also show the total current size vs potential reduced size +# Note: currentSize includes all Avalonia files (root + plugins + runtimes) +# Dedup savings come only from removing plugin DLLs that duplicate root DLLs $currentSize = (Get-ChildItem "Output/Debug/Avalonia" -Recurse -File | Measure-Object Length -Sum).Sum $reducedSize = $currentSize - $totalDuplicateSize Write-Host ("Current Avalonia size: {0:N2} MB" -f ($currentSize / 1MB)) Write-Host ("After dedup: {0:N2} MB" -f ($reducedSize / 1MB))Scripts/compare_sizes.ps1 (1)
3-7: Inconsistent error handling for missing directories.Lines 3 and 6 access paths without
-ErrorAction SilentlyContinue, while line 19 and 24 use it. For consistency, either add existence checks or use-ErrorAction SilentlyContinuethroughout.Suggested fix
Write-Host "=== WPF Debug (excluding Avalonia) ===" -ForegroundColor Cyan -$wpfRoot = Get-ChildItem "Output/Debug" -File | Measure-Object Length -Sum +$wpfRoot = Get-ChildItem "Output/Debug" -File -ErrorAction SilentlyContinue | Measure-Object Length -Sum Write-Host ("Root files: {0:N2} MB" -f ($wpfRoot.Sum / 1MB)) -$wpfPlugins = Get-ChildItem "Output/Debug/Plugins" -Recurse -File | Measure-Object Length -Sum +$wpfPlugins = Get-ChildItem "Output/Debug/Plugins" -Recurse -File -ErrorAction SilentlyContinue | Measure-Object Length -Sum Write-Host ("Plugins folder: {0:N2} MB" -f ($wpfPlugins.Sum / 1MB))Scripts/release_sizes.ps1 (1)
12-18: Inconsistent error handling.Lines 12 and 17 lack
-ErrorAction SilentlyContinuewhile other lines include it. This inconsistency could cause errors if the WPF Release directory exists but lacks expected subfolders.Suggested fix for consistency
Write-Host "" Write-Host "=== Breakdown ===" -ForegroundColor Yellow -$wpfRoot = (Get-ChildItem "Output/Release" -File | Measure-Object Length -Sum).Sum +$wpfRoot = (Get-ChildItem "Output/Release" -File -ErrorAction SilentlyContinue | Measure-Object Length -Sum).Sum $avRoot = (Get-ChildItem "Output/Release/Avalonia" -File -ErrorAction SilentlyContinue | Measure-Object Length -Sum).Sum Write-Host ("WPF root files: {0:N2} MB" -f ($wpfRoot / 1MB)) Write-Host ("Avalonia root files: {0:N2} MB" -f ($avRoot / 1MB)) -$wpfPlugins = (Get-ChildItem "Output/Release/Plugins" -Recurse -File | Measure-Object Length -Sum).Sum +$wpfPlugins = (Get-ChildItem "Output/Release/Plugins" -Recurse -File -ErrorAction SilentlyContinue | Measure-Object Length -Sum).Sum $avPlugins = (Get-ChildItem "Output/Release/Avalonia/Plugins" -Recurse -File -ErrorAction SilentlyContinue | Measure-Object Length -Sum).SumScripts/size_analysis.ps1 (1)
2-2: Remove unused parameter.The
$pathparameter is declared but never used in the script. Either remove it or integrate it into the hardcoded paths below.Suggested fix
# Size analysis script -param([string]$path = ".")Flow.Launcher/packages.lock.json (1)
175-184: Avalonia 11.2.3 is safe, but consider upgrading to a newer stable version.The lock file correctly reflects the addition of Avalonia 11.2.3 as a transitive dependency. No known security vulnerabilities are detected for this version. However, a newer stable version (Avalonia 11.3.11, released Jan 13, 2026) is available on NuGet. Consider upgrading to the latest version for access to bug fixes and improvements, or confirm that 11.2.3 is intentionally pinned for compatibility reasons.
| private readonly Settings _settings; | ||
| private readonly Func<MainViewModel> _getMainViewModel; | ||
| private readonly Internationalization _i18n; | ||
|
|
||
| public AvaloniaPublicAPI(Settings settings, Func<MainViewModel> getMainViewModel, Internationalization i18n) | ||
| { | ||
| _settings = settings; | ||
| _getMainViewModel = getMainViewModel; | ||
| _i18n = i18n; | ||
| } | ||
|
|
||
| #pragma warning disable CS0067 | ||
| public event VisibilityChangedEventHandler? VisibilityChanged; | ||
| public event ActualApplicationThemeChangedEventHandler? ActualApplicationThemeChanged; | ||
| #pragma warning restore CS0067 | ||
|
|
||
| // Essential for plugins | ||
| public void ChangeQuery(string query, bool requery = false) => _getMainViewModel().QueryText = query; | ||
|
|
||
| public string GetTranslation(string key) => _i18n.GetTranslation(key); | ||
|
|
||
| public List<PluginPair> GetAllPlugins() => PluginManager.GetAllLoadedPlugins(); | ||
| public List<PluginPair> GetAllInitializedPlugins(bool includeFailed) => PluginManager.GetAllInitializedPlugins(includeFailed); | ||
| public MatchResult FuzzySearch(string query, string stringToCompare) => | ||
| Ioc.Default.GetRequiredService<StringMatcher>().FuzzyMatch(query, stringToCompare); | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
rg -n "ChangeQuery\s*\(|ReQuery\s*\(|Requery" -g '*.cs' -A 3Repository: Flow-Launcher/Flow.Launcher
Length of output: 14843
🏁 Script executed:
# Also search for QueryText property and how it's used
rg -n "QueryText\s*=" -g '*.cs' -A 2 -B 2Repository: Flow-Launcher/Flow.Launcher
Length of output: 6533
🏁 Script executed:
# Look for any methods that might indicate requery intent
rg -n "class MainViewModel|QueryText|ExecuteQuery" -g '*.cs' -A 2Repository: Flow-Launcher/Flow.Launcher
Length of output: 30993
Respect the requery flag in ChangeQuery.
The requery parameter is currently ignored, causing plugins that pass requery=true to fail to trigger re-execution when the query text remains unchanged. The WPF implementation correctly delegates this to ChangeQueryText, which checks isReQuery and calls Query() accordingly. Avalonia should implement similar logic or delegate to a VM method that respects the flag. Consider making ChangeQuery conditionally call a method that re-executes the query when requery=true and the text is unchanged.
🤖 Prompt for AI Agents
In `@Flow.Launcher.Avalonia/AvaloniaPublicAPI.cs` around lines 27 - 52,
ChangeQuery currently sets _getMainViewModel().QueryText unconditionally and
ignores the requery flag; modify ChangeQuery to retrieve the MainViewModel via
_getMainViewModel(), compare the incoming query to vm.QueryText, set
vm.QueryText if different, and if requery is true and the text is unchanged call
the ViewModel method that re-executes the search (e.g., vm.Query() or the
existing vm.ChangeQueryText/ChangeQuery method that honors isReQuery) so plugins
passing requery=true will force a re-execution; update references to ChangeQuery
in AvaloniaPublicAPI to use vm methods rather than directly assigning QueryText
when requery behavior is required.
| @@ -0,0 +1,19 @@ | |||
| # Flow.Launcher.Core/Plugin/JsonRPCV2Models/ | |||
|
|
|||
| <!-- Explorer: Fill in this section with architectural understanding --> | |||
There was a problem hiding this comment.
Fix the placeholder comment reference.
The comment references "Explorer" but this codemap is for the JsonRPCV2Models directory.
📝 Proposed fix
-<!-- Explorer: Fill in this section with architectural understanding -->
+<!-- Fill in this section with architectural understanding -->🤖 Prompt for AI Agents
In `@Flow.Launcher.Core/Plugin/JsonRPCV2Models/codemap.md` at line 3, Replace the
incorrect placeholder reference "Explorer" in the codemap header comment with
the correct component name for this file (e.g., "JsonRPCV2Models") or a neutral
description; update the comment in codemap.md so it accurately describes the
architectural scope for the JsonRPCV2Models directory (look for the string
"Explorer" in codemap.md and change it to "JsonRPCV2Models" or an appropriate
description).
| @@ -0,0 +1,19 @@ | |||
| # Plugins/Flow.Launcher.Plugin.Shell/ViewModels/ | |||
|
|
|||
| <!-- Explorer: Fill in this section with architectural understanding --> | |||
There was a problem hiding this comment.
Fix the placeholder comment reference.
The comment references "Explorer" but this codemap is for the Shell plugin's ViewModels directory.
📝 Proposed fix
-<!-- Explorer: Fill in this section with architectural understanding -->
+<!-- Fill in this section with architectural understanding -->📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <!-- Explorer: Fill in this section with architectural understanding --> | |
| <!-- Fill in this section with architectural understanding --> |
🤖 Prompt for AI Agents
In `@Plugins/Flow.Launcher.Plugin.Shell/ViewModels/codemap.md` at line 3, Replace
the incorrect placeholder comment referencing "Explorer" in the codemap.md file
with an accurate reference to the Shell plugin ViewModels; locate the HTML
comment "<!-- Explorer: Fill in this section with architectural understanding
-->" and change its text to something like "<!-- Shell Plugin ViewModels: Fill
in this section with architectural understanding -->" (or equivalent wording
that correctly identifies the Shell plugin's ViewModels directory).
| $avTotal = (Get-ChildItem "Output/Debug/Avalonia" -Recurse -File | Measure-Object Length -Sum).Sum | ||
| Write-Host ("WPF total (excl. Avalonia subfolder): {0:N2} MB" -f ($wpfTotal / 1MB)) | ||
| Write-Host ("Avalonia total: {0:N2} MB" -f ($avTotal / 1MB)) | ||
| Write-Host ("Difference: {0:N2} MB" -f (($avTotal - $wpfTotal + $wpfPlugins.Sum) / 1MB)) |
There was a problem hiding this comment.
Clarify or fix the difference calculation.
The formula ($avTotal - $wpfTotal + $wpfPlugins.Sum) is confusing. If the goal is to show how much larger Avalonia is than WPF, adding $wpfPlugins.Sum back seems incorrect since $wpfTotal already excludes the Avalonia subfolder per line 24. Consider clarifying the intent with a comment or simplifying to ($avTotal - $wpfTotal).
🤖 Prompt for AI Agents
In `@Scripts/compare_sizes.ps1` at line 28, The difference formula on the
Write-Host line is confusing because it adds $wpfPlugins.Sum back into ($avTotal
- $wpfTotal) even though $wpfTotal was already computed excluding the Avalonia
subfolder; update the expression to a clear comparison (use ($avTotal -
$wpfTotal) for "how much larger Avalonia is than WPF") and add a brief comment
above the Write-Host mentioning what totals include/exclude, referencing the
variables $avTotal, $wpfTotal, and $wpfPlugins.Sum so future readers understand
the intent.
| $pluginRuntimes = Get-ChildItem "Output/Release/Avalonia/Plugins" -Recurse -Directory -Filter "runtimes" | ||
|
|
There was a problem hiding this comment.
Guard missing Avalonia plugins path to avoid hard failure.
If Output/Release/Avalonia/Plugins doesn’t exist (clean repo, Debug build, or Avalonia not built), Get-ChildItem throws and the script stops. Add a Test-Path early exit to keep this tool usable.
✅ Proposed fix (graceful exit)
-$pluginRuntimes = Get-ChildItem "Output/Release/Avalonia/Plugins" -Recurse -Directory -Filter "runtimes"
+$root = "Output/Release/Avalonia/Plugins"
+if (-not (Test-Path $root)) {
+ Write-Warning "Plugin runtime root not found: $root"
+ return
+}
+$pluginRuntimes = Get-ChildItem $root -Recurse -Directory -Filter "runtimes"🤖 Prompt for AI Agents
In `@Scripts/plugin_runtimes.ps1` around lines 4 - 5, The script fails if the
Avalonia plugins path is missing because Get-ChildItem on
"Output/Release/Avalonia/Plugins" will throw; add a Test-Path check for that
directory before calling Get-ChildItem (guarding the logic that sets
$pluginRuntimes) and perform an early, non-failing exit (or return/continue)
when the path doesn't exist so the rest of the script can run without hard
failure.
| Write-Host "=== WPF Debug ===" -ForegroundColor Cyan | ||
| Write-Host "Total: $(Get-FolderSize 'Output\Debug') MB (excluding Avalonia subfolder)" |
There was a problem hiding this comment.
Misleading comment: Avalonia subfolder is included.
The output claims "excluding Avalonia subfolder" but Get-FolderSize 'Output\Debug' recursively includes all files, including those under Output\Debug\Avalonia. Either fix the comment or adjust the logic to actually exclude the Avalonia subfolder.
Suggested fix to match the comment
Write-Host "=== WPF Debug ===" -ForegroundColor Cyan
-Write-Host "Total: $(Get-FolderSize 'Output\Debug') MB (excluding Avalonia subfolder)"
+$wpfSize = if (Test-Path 'Output\Debug') {
+ $files = Get-ChildItem -Path 'Output\Debug' -Recurse -File -ErrorAction SilentlyContinue |
+ Where-Object { $_.FullName -notmatch '\\Avalonia\\' }
+ [math]::Round(($files | Measure-Object -Property Length -Sum).Sum / 1MB, 2)
+} else { 0 }
+Write-Host "Total: $wpfSize MB (excluding Avalonia subfolder)"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Write-Host "=== WPF Debug ===" -ForegroundColor Cyan | |
| Write-Host "Total: $(Get-FolderSize 'Output\Debug') MB (excluding Avalonia subfolder)" | |
| Write-Host "=== WPF Debug ===" -ForegroundColor Cyan | |
| $wpfSize = if (Test-Path 'Output\Debug') { | |
| $files = Get-ChildItem -Path 'Output\Debug' -Recurse -File -ErrorAction SilentlyContinue | | |
| Where-Object { $_.FullName -notmatch '\\Avalonia\\' } | |
| [math]::Round(($files | Measure-Object -Property Length -Sum).Sum / 1MB, 2) | |
| } else { 0 } | |
| Write-Host "Total: $wpfSize MB (excluding Avalonia subfolder)" |
🤖 Prompt for AI Agents
In `@Scripts/size_analysis.ps1` around lines 39 - 40, The log line claims the
Avalonia subfolder is excluded but calls Get-FolderSize('Output\Debug') which
includes all subfolders; either update the message or change the size
calculation to exclude the Avalonia folder. Fix options: 1) modify the
Write-Host text ("excluding Avalonia subfolder") to remove the exclusion claim,
or 2) update the size computation by calling a helper or filter that sums
Get-ChildItem results for 'Output\Debug' while excluding 'Output\Debug\Avalonia'
(use the existing Get-FolderSize function name as a reference) and then use that
result in the Write-Host for the WPF Debug total. Ensure the affected lines are
the Write-Host calls that reference "=== WPF Debug ===" and the Total using
Get-FolderSize 'Output\Debug'.
- Remove PowerShell-based duplicate DLL cleanup targets from Avalonia csproj - Remove unnecessary runtime folder cleanup targets - Update WPF packages.lock.json dependencies These cleanup steps are now handled differently or are no longer needed. 💘 Generated with Crush Assisted-by: Kimi for Coding via Crush <crush@charm.land>
This comment has been minimized.
This comment has been minimized.
… checklist - Add PreviewHotkey property to HotkeySettingsViewModel - Add preview hotkey UI to HotkeySettingsPage - Implement dynamic hotkey handling in MainWindow code-behind - Remove hardcoded F1 keybinding from XAML - Update AVALONIA_MIGRATION_CHECKLIST.md with accurate progress: - Overall progress: 60-65% (was 35-40%) - Plugin Store: 95% done (was 0%) - Plugin Settings: 90% done (was 33%) - Proxy Settings: 100% done (was 59%) 💘 Generated with Crush Assisted-by: Kimi for Coding via Crush <crush@charm.land>
This comment has been minimized.
This comment has been minimized.
Text highlighting was already implemented via HighlightTextConverter and TitleHighlightData/SubTitleHighlightData in ResultViewModel. 💘 Generated with Crush Assisted-by: Kimi for Coding via Crush <crush@charm.land>
This comment has been minimized.
This comment has been minimized.
Update all project files from net9.0 to net10.0: - Core projects: net9.0-windows → net10.0-windows - App projects: net9.0-windows10.0.19041.0 → net10.0-windows10.0.19041.0 - All plugin projects updated - Microsoft.Extensions packages updated to 10.0.0-preview.1 Build verified with 1220 warnings (pre-existing), 0 errors. 💘 Generated with Crush Assisted-by: Kimi for Coding via Crush <crush@charm.land>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@AVALONIA_MIGRATION_CHECKLIST.md`:
- Line 415: The TODO list still shows "HighlightTextConverter - Search match
highlighting" as high priority even though HighlightTextConverter has been
implemented; update the checklist entry by removing that line or marking it
checked (e.g., change the unchecked box to a checked box) so the item is no
longer listed as outstanding and the document reflects the implemented
HighlightTextConverter.
- Line 311: Update the checklist entry for HighlightTextConverter from ❌ Missing
to ✅ Done to match the implemented status; specifically change the table row
currently showing "HighlightTextConverter | :x: Missing | Bold gold highlighting
for matched characters" so it indicates completion (consistent with the "Text
highlighting" item at line 339 and the high-priority TODO at line 415 and the PR
commit message stating it was implemented via HighlightTextConverter).
In `@Flow.Launcher.Avalonia/MainWindow.axaml`:
- Around line 62-70: The SearchIconCanvas is intercepting pointer events so
clicks don't reach the query TextBox; update the SearchIconCanvas (and/or
SearchIcon) to be non-hit-testable by setting its IsHitTestVisible property to
false so pointer events pass through to the underlying query box (refer to the
SearchIconCanvas and SearchIcon elements to locate where to apply the change).
🧹 Nitpick comments (5)
Flow.Launcher.Avalonia/ViewModel/SettingPages/HotkeySettingsViewModel.cs (1)
13-16: Prefer constructor injection to avoid service-locator coupling.
Service-locator use makes testing harder and hides dependencies. Consider adding an injectable constructor and chaining from the parameterless one if XAML instantiation requires it.♻️ Suggested refactor
- public HotkeySettingsViewModel() - { - _settings = Ioc.Default.GetRequiredService<Settings>(); - } + public HotkeySettingsViewModel() + : this(Ioc.Default.GetRequiredService<Settings>()) + { + } + + public HotkeySettingsViewModel(Settings settings) + { + _settings = settings ?? throw new ArgumentNullException(nameof(settings)); + }AVALONIA_MIGRATION_CHECKLIST.md (3)
440-461: Add language identifiers to fenced code blocks.The markdown linter flags these code blocks for missing language specifiers. Adding
textas the language identifier will resolve the warnings while maintaining the file tree formatting.🔧 Suggested fix
For line 440:
-``` +```text Flow.Launcher/For line 463:
-``` +```text Flow.Launcher.Avalonia/Also applies to: 463-511
219-219: Verify Settings Progress calculation accuracy.The stated "~1100/2609 lines (~42%)" doesn't match the sum of individual page line counts from the sections above:
- General: ~270, Theme: ~70, Hotkey: ~26, Plugin: ~170, Store: ~200, Proxy: ~47, About: ~25
- Sum: 808 lines (31% of 2609), not ~1100 (42%)
Please verify and update either the individual page estimates or the total calculation.
332-332: Add blank lines around tables for markdown compliance.The markdown linter expects blank lines before and after tables. Adding these will resolve MD058 warnings.
📐 Example fix (line 332)
### Search & Results + | Feature | Status | Notes |Apply similar fixes to tables at lines 346, 356, and 365.
Also applies to: 346-346, 356-356, 365-365
Flow.Launcher.Avalonia/MainWindow.axaml.cs (1)
135-182: Consider implementingCanExecuteconditions for command consistency.Per the ICommand contract,
RelayCommand.Execute()does not re-checkCanExecute()—the caller is responsible. Currently, these four commands lackCanExecuteguards and don't have conditional disable logic defined. While the implementations already include internal validation (e.g.,LoadContextMenu()checks if a result exists), explicitly implementingCanExecuteconditions would align with MVVM patterns and clarify intent when these commands become conditionally disabled in the future.
| | BoolToIsVisibleConverter | :white_check_mark: Done | Boolean to IsVisible | | ||
| | TranslationConverter | :white_check_mark: Done | Localization converter | | ||
| | CommonConverters | :white_check_mark: Done | Various common converters | | ||
| | HighlightTextConverter | :x: Missing | Bold gold highlighting for matched characters | |
There was a problem hiding this comment.
Inconsistency: HighlightTextConverter status conflicts with line 339 and commit message.
This converter is marked as ❌ Missing here, but line 339 marks "Text highlighting" as ✅ Done, and line 415 lists it as a high-priority TODO. The PR commit message explicitly states: "Marked text highlighting as done in checklist (implemented via HighlightTextConverter)."
Update this line to reflect the actual implementation status.
📝 Suggested fix
-| HighlightTextConverter | :x: Missing | Bold gold highlighting for matched characters |
+| HighlightTextConverter | :white_check_mark: Done | Bold gold highlighting for matched characters |📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| | HighlightTextConverter | :x: Missing | Bold gold highlighting for matched characters | | |
| | HighlightTextConverter | :white_check_mark: Done | Bold gold highlighting for matched characters | |
🤖 Prompt for AI Agents
In `@AVALONIA_MIGRATION_CHECKLIST.md` at line 311, Update the checklist entry for
HighlightTextConverter from ❌ Missing to ✅ Done to match the implemented status;
specifically change the table row currently showing "HighlightTextConverter |
:x: Missing | Bold gold highlighting for matched characters" so it indicates
completion (consistent with the "Text highlighting" item at line 339 and the
high-priority TODO at line 415 and the PR commit message stating it was
implemented via HighlightTextConverter).
| ## Priority Recommendations | ||
|
|
||
| ### High Priority (Core UX) | ||
| 1. [ ] **HighlightTextConverter** - Search match highlighting |
There was a problem hiding this comment.
Remove completed item from high priority list.
Per the PR commit message, HighlightTextConverter is already implemented. This item should be checked off or removed from the TODO list.
✅ Suggested fix
-1. [ ] **HighlightTextConverter** - Search match highlighting
+1. [x] **HighlightTextConverter** - Search match highlighting - DONE📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 1. [ ] **HighlightTextConverter** - Search match highlighting | |
| 1. [x] **HighlightTextConverter** - Search match highlighting - DONE |
🤖 Prompt for AI Agents
In `@AVALONIA_MIGRATION_CHECKLIST.md` at line 415, The TODO list still shows
"HighlightTextConverter - Search match highlighting" as high priority even
though HighlightTextConverter has been implemented; update the checklist entry
by removing that line or marking it checked (e.g., change the unchecked box to a
checked box) so the item is no longer listed as outstanding and the document
reflects the implemented HighlightTextConverter.
| <!-- Search Icon --> | ||
| <Canvas Name="SearchIconCanvas" | ||
| Classes="searchIconPosition" | ||
| HorizontalAlignment="Right"> | ||
| <Path Name="SearchIcon" | ||
| Classes="searchIcon" | ||
| Data="{DynamicResource SearchIconGeometry}" | ||
| Stretch="Uniform" /> | ||
| </Canvas> |
There was a problem hiding this comment.
Prevent the search icon overlay from stealing focus.
The icon canvas can intercept pointer events, so clicking the icon area won’t focus the query box. Making it non-hit-testable keeps click-to-focus behavior intact.
🧩 Suggested tweak
- <Canvas Name="SearchIconCanvas"
- Classes="searchIconPosition"
- HorizontalAlignment="Right">
+ <Canvas Name="SearchIconCanvas"
+ Classes="searchIconPosition"
+ HorizontalAlignment="Right"
+ IsHitTestVisible="False">Avalonia IsHitTestVisible default behavior for overlapping controls and passing pointer events through
🤖 Prompt for AI Agents
In `@Flow.Launcher.Avalonia/MainWindow.axaml` around lines 62 - 70, The
SearchIconCanvas is intercepting pointer events so clicks don't reach the query
TextBox; update the SearchIconCanvas (and/or SearchIcon) to be non-hit-testable
by setting its IsHitTestVisible property to false so pointer events pass through
to the underlying query box (refer to the SearchIconCanvas and SearchIcon
elements to locate where to apply the change).
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
3 issues found across 18 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="Flow.Launcher.Core/Flow.Launcher.Core.csproj">
<violation number="1" location="Flow.Launcher.Core/Flow.Launcher.Core.csproj:4">
P0: The project target framework is upgraded to `net10.0-windows`, but the PR description states the target is `.NET 9`. This discrepancy suggests an unintended change or outdated description.
Additionally:
1. The `Flow.Launcher.Avalonia` project is described as targeting .NET 9. A .NET 9 application cannot reference a .NET 10 library (`Core`).
2. The current build environment (and likely CI) only supports up to .NET 9.0 (verified via `dotnet --list-sdks`), so this change will cause build failures.
3. Bundling a major .NET version upgrade with the Avalonia migration increases risk and scope.
Recommended: Revert to `net9.0-windows` to match the PR description and ensure compatibility.</violation>
</file>
<file name="Flow.Launcher/Flow.Launcher.csproj">
<violation number="1" location="Flow.Launcher/Flow.Launcher.csproj:5">
P2: This upgrade to .NET 10 directly contradicts the PR description, which states the project targets .NET 9. Additionally, upgrading the stable WPF application to a new major version increases the scope and risk of this migration PR.
If the upgrade is intentional, update the PR description to reflect this change. Otherwise, revert to `net9.0-windows10.0.19041.0`.</violation>
<violation number="2" location="Flow.Launcher/Flow.Launcher.csproj:147">
P1: Usage of `10.0.0-preview.1` packages is unsafe for production. In Feb 2026, .NET 10 has been stable since Nov 2025. These preview packages are approximately one year old and should be replaced with stable versions (e.g., `10.0.0`) to ensure security and stability.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net9.0-windows</TargetFramework> | ||
| <TargetFramework>net10.0-windows</TargetFramework> |
There was a problem hiding this comment.
P0: The project target framework is upgraded to net10.0-windows, but the PR description states the target is .NET 9. This discrepancy suggests an unintended change or outdated description.
Additionally:
- The
Flow.Launcher.Avaloniaproject is described as targeting .NET 9. A .NET 9 application cannot reference a .NET 10 library (Core). - The current build environment (and likely CI) only supports up to .NET 9.0 (verified via
dotnet --list-sdks), so this change will cause build failures. - Bundling a major .NET version upgrade with the Avalonia migration increases risk and scope.
Recommended: Revert to net9.0-windows to match the PR description and ensure compatibility.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Flow.Launcher.Core/Flow.Launcher.Core.csproj, line 4:
<comment>The project target framework is upgraded to `net10.0-windows`, but the PR description states the target is `.NET 9`. This discrepancy suggests an unintended change or outdated description.
Additionally:
1. The `Flow.Launcher.Avalonia` project is described as targeting .NET 9. A .NET 9 application cannot reference a .NET 10 library (`Core`).
2. The current build environment (and likely CI) only supports up to .NET 9.0 (verified via `dotnet --list-sdks`), so this change will cause build failures.
3. Bundling a major .NET version upgrade with the Avalonia migration increases risk and scope.
Recommended: Revert to `net9.0-windows` to match the PR description and ensure compatibility.</comment>
<file context>
@@ -1,7 +1,7 @@
<PropertyGroup>
- <TargetFramework>net9.0-windows</TargetFramework>
+ <TargetFramework>net10.0-windows</TargetFramework>
<UseWpf>true</UseWpf>
<UseWindowsForms>true</UseWindowsForms>
</file context>
| <TargetFramework>net10.0-windows</TargetFramework> | |
| <TargetFramework>net9.0-windows</TargetFramework> |
| <PackageReference Include="MdXaml.Svg" Version="1.27.0" /> | ||
| <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.9" /> | ||
| <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.9" /> | ||
| <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0-preview.1.25080.5" /> |
There was a problem hiding this comment.
P1: Usage of 10.0.0-preview.1 packages is unsafe for production. In Feb 2026, .NET 10 has been stable since Nov 2025. These preview packages are approximately one year old and should be replaced with stable versions (e.g., 10.0.0) to ensure security and stability.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Flow.Launcher/Flow.Launcher.csproj, line 147:
<comment>Usage of `10.0.0-preview.1` packages is unsafe for production. In Feb 2026, .NET 10 has been stable since Nov 2025. These preview packages are approximately one year old and should be replaced with stable versions (e.g., `10.0.0`) to ensure security and stability.</comment>
<file context>
@@ -144,8 +144,8 @@
<PackageReference Include="MdXaml.Svg" Version="1.27.0" />
- <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.9" />
- <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.9" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0-preview.1.25080.5" />
+ <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-preview.1.25080.5" />
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
</file context>
| <PropertyGroup> | ||
| <OutputType>WinExe</OutputType> | ||
| <TargetFramework>net9.0-windows10.0.19041.0</TargetFramework> | ||
| <TargetFramework>net10.0-windows10.0.19041.0</TargetFramework> |
There was a problem hiding this comment.
P2: This upgrade to .NET 10 directly contradicts the PR description, which states the project targets .NET 9. Additionally, upgrading the stable WPF application to a new major version increases the scope and risk of this migration PR.
If the upgrade is intentional, update the PR description to reflect this change. Otherwise, revert to net9.0-windows10.0.19041.0.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Flow.Launcher/Flow.Launcher.csproj, line 5:
<comment>This upgrade to .NET 10 directly contradicts the PR description, which states the project targets .NET 9. Additionally, upgrading the stable WPF application to a new major version increases the scope and risk of this migration PR.
If the upgrade is intentional, update the PR description to reflect this change. Otherwise, revert to `net9.0-windows10.0.19041.0`.</comment>
<file context>
@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>WinExe</OutputType>
- <TargetFramework>net9.0-windows10.0.19041.0</TargetFramework>
+ <TargetFramework>net10.0-windows10.0.19041.0</TargetFramework>
<UseWPF>true</UseWPF>
<UseWindowsForms>false</UseWindowsForms>
</file context>
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj`:
- Around line 20-28: The PropertyGroup entries currently replace the
DefineConstants value; update both Debug and Release PropertyGroup blocks so
they append SDK/product constants instead of overriding by adding the existing
$(DefineConstants) token and then the SDK symbols (e.g. NET10_0, WINDOWS or
other platform/runtime symbols you expect) to the DefineConstants attribute;
locate the DefineConstants attributes in the Flow.Launcher.Avalonia.csproj and
change them to include $(DefineConstants) followed by the additional constants
(keep DEBUG/TRACE/RELEASE/AVALONIA as appropriate).
- Around line 34-46: Summary: Several PackageReference entries in
Flow.Launcher.Avalonia.csproj (Avalonia, Avalonia.Desktop,
Avalonia.Themes.Fluent, Avalonia.Fonts.Inter, FluentAvaloniaUI,
CommunityToolkit.Mvvm, DynamicData) do not ship explicit net10.0 assets and rely
on NuGet compatibility fallbacks. Fix: in Flow.Launcher.Avalonia.csproj inspect
each PackageReference (e.g., Avalonia 11.2.3, FluentAvaloniaUI 2.2.0,
CommunityToolkit.Mvvm 8.4.0, DynamicData 9.0.4) and either upgrade to package
versions that explicitly target net10.0 or add a comment/verification step in
the csproj/PR to confirm net10 compatibility; if no net10 build exists, document
acceptance of NuGet fallback or replace with alternatives that support net10.0;
ensure Microsoft.Extensions.* preview packages are intentionally used
(DependencyInjection and Hosting) and update them to GA net10-compatible
versions where available.
In `@Flow.Launcher.Test/Flow.Launcher.Test.csproj`:
- Line 4: The repository SDK pinning and CI use .NET 9 while projects target
net10.0; update the SDK and CI to .NET 10 by changing the "version" value in
global.json's "sdk" object to a 10.x value (e.g., "10.0.*") and update the
GitHub Actions setup step(s) (actions/setup-dotnet invocation(s)) to use
dotnet-version: 10.0.x (including any matrix entries or multiple workflow files
like the two CI workflows) so the build toolchain matches the net10.0 target
across projects.
In `@Flow.Launcher/Flow.Launcher.csproj`:
- Around line 147-148: Update the two PackageReference entries that currently
point to the preview version "10.0.0-preview.1.25080.5" so they reference a
stable release (preferably "10.0.2" or at minimum "10.0.0"); specifically change
the PackageReference for Microsoft.Extensions.DependencyInjection and
Microsoft.Extensions.Hosting to the stable version string to avoid using preview
packages.
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | ||
| <OutputPath>..\Output\Debug\Avalonia\</OutputPath> | ||
| <DefineConstants>DEBUG;TRACE;AVALONIA</DefineConstants> | ||
| </PropertyGroup> | ||
|
|
||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> | ||
| <OutputPath>..\Output\Release\Avalonia\</OutputPath> | ||
| <DefineConstants>TRACE;RELEASE;AVALONIA</DefineConstants> | ||
| </PropertyGroup> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check for conditional compilation that depends on SDK-defined constants
rg -nP '^\s*#if\s+(NET|NET10_0|NET10_0_WINDOWS|WINDOWS)\b' --type csRepository: Flow-Launcher/Flow.Launcher
Length of output: 53
🏁 Script executed:
#!/bin/bash
# Check if there are ANY conditional compilation directives in C# files
rg -nP '^\s*#if\b' --type cs | head -20Repository: Flow-Launcher/Flow.Launcher
Length of output: 1628
🏁 Script executed:
#!/bin/bash
# Check the TargetFramework and other project properties in the .csproj file
rg -A 2 'TargetFramework|DefineConstants' Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csprojRepository: Flow-Launcher/Flow.Launcher
Length of output: 623
Append SDK constants to preserve best practice consistency.
While the codebase doesn't currently depend on SDK-defined constants (like NET10_0 or WINDOWS) in conditional compilation, it's best practice to append to $(DefineConstants) rather than override it to safeguard against future uses and maintain consistency.
🛠️ Proposed fix
- <DefineConstants>DEBUG;TRACE;AVALONIA</DefineConstants>
+ <DefineConstants>$(DefineConstants);DEBUG;TRACE;AVALONIA</DefineConstants>
...
- <DefineConstants>TRACE;RELEASE;AVALONIA</DefineConstants>
+ <DefineConstants>$(DefineConstants);TRACE;RELEASE;AVALONIA</DefineConstants>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | |
| <OutputPath>..\Output\Debug\Avalonia\</OutputPath> | |
| <DefineConstants>DEBUG;TRACE;AVALONIA</DefineConstants> | |
| </PropertyGroup> | |
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> | |
| <OutputPath>..\Output\Release\Avalonia\</OutputPath> | |
| <DefineConstants>TRACE;RELEASE;AVALONIA</DefineConstants> | |
| </PropertyGroup> | |
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | |
| <OutputPath>..\Output\Debug\Avalonia\</OutputPath> | |
| <DefineConstants>$(DefineConstants);DEBUG;TRACE;AVALONIA</DefineConstants> | |
| </PropertyGroup> | |
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> | |
| <OutputPath>..\Output\Release\Avalonia\</OutputPath> | |
| <DefineConstants>$(DefineConstants);TRACE;RELEASE;AVALONIA</DefineConstants> | |
| </PropertyGroup> |
🤖 Prompt for AI Agents
In `@Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj` around lines 20 - 28,
The PropertyGroup entries currently replace the DefineConstants value; update
both Debug and Release PropertyGroup blocks so they append SDK/product constants
instead of overriding by adding the existing $(DefineConstants) token and then
the SDK symbols (e.g. NET10_0, WINDOWS or other platform/runtime symbols you
expect) to the DefineConstants attribute; locate the DefineConstants attributes
in the Flow.Launcher.Avalonia.csproj and change them to include
$(DefineConstants) followed by the additional constants (keep
DEBUG/TRACE/RELEASE/AVALONIA as appropriate).
| <ItemGroup> | ||
| <PackageReference Include="Avalonia" Version="11.2.3" /> | ||
| <PackageReference Include="Avalonia.Desktop" Version="11.2.3" /> | ||
| <PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.3" /> | ||
| <PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.3" /> | ||
| <PackageReference Include="FluentAvaloniaUI" Version="2.2.0" /> | ||
| <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" /> | ||
| <PackageReference Include="DynamicData" Version="9.0.4" /> | ||
| <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0-preview.1.25080.5" /> | ||
| <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-preview.1.25080.5" /> | ||
| <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.--> | ||
| <PackageReference Include="Avalonia.Diagnostics" Version="11.2.3" Condition="'$(Configuration)' == 'Debug'" /> | ||
| </ItemGroup> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "Flow.Launcher.Avalonia.csproj" -type fRepository: Flow-Launcher/Flow.Launcher
Length of output: 125
🏁 Script executed:
cat -n ./Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj | head -50Repository: Flow-Launcher/Flow.Launcher
Length of output: 3087
🌐 Web query:
Avalonia 11.2.3 net10.0 target framework support
💡 Result:
Avalonia 11.2.3 does not ship “net10.0” target frameworks (no lib/net10.0/* assets). For example, Avalonia and Avalonia.Desktop 11.2.3 target net6.0, net8.0, and netstandard2.0. [1][2]
That said, a net10.0 app can still reference Avalonia 11.2.3: NuGet marks these packages as compatible with net10.0 via framework compatibility rules (it will select the closest matching assets, e.g., net8.0/net6.0). [1][2]
Platform note: Avalonia.Android 11.2.3 targets net8.0-android34.0, and NuGet also computes compatibility for net10.0-android (but it’s still not a native net10.0-android* asset). [3]
Sources: [1] [2] [3]
🌐 Web query:
FluentAvaloniaUI 2.2.0 net10.0 target framework support
💡 Result:
FluentAvaloniaUI 2.2.0 does not ship a net10.0-targeted build. On NuGet, the package’s included TFMs are net6.0 and .NETStandard 2.0/2.1. (nuget.org)
If your app targets net10.0, NuGet will still allow the reference because it’s computed as compatible (“net10.0 was computed”) and it will fall back to using the package’s net6.0` (or .NET Standard) assets. (nuget.org)
Citations:
- 1: https://www.nuget.org/packages/FluentAvaloniaUI/2.2.0?utm_source=openai
- 2: https://www.nuget.org/packages/FluentAvaloniaUI/2.2.0?utm_source=openai
🌐 Web query:
CommunityToolkit.Mvvm 8.4.0 net10.0 target framework support
💡 Result:
CommunityToolkit.Mvvm 8.4.0 does not ship a net10.0-specific build. Its included TFMs are:
netstandard2.0netstandard2.1net8.0net8.0-windows10.0.17763(nuget.org)
A project targeting net10.0 can still reference it, because NuGet marks the package as compatible with “.NET 8.0 or higher” and lists net10.0 as a computed compatible TFM (meaning it will typically use the net8.0 assets). (nuget.org)
Citations:
- 1: https://www.nuget.org/packages/CommunityToolkit.Mvvm/8.4.0?utm_source=openai
- 2: https://www.nuget.org/packages/CommunityToolkit.Mvvm/8.4.0?utm_source=openai
🌐 Web query:
DynamicData 9.0.4 net10.0 target framework support
💡 Result:
DynamicData 9.0.4 does not ship a net10.0-targeted assembly. Its included TFMs are:
net6.0netstandard2.0net462(nuget.org)
A project targeting net10.0 can still reference DynamicData 9.0.4 because net10.0 is compatible with libraries targeting net6.0 (NuGet will select the net6.0 asset). (nuget.org)
Citations:
- 1: https://www.nuget.org/packages/DynamicData/9.0.4
- 2: https://www.nuget.org/packages/DynamicData/9.0.4
🌐 Web query:
Microsoft.Extensions.DependencyInjection 10.0.0-preview.1.25080.5 net10.0 support
💡 Result:
Yes. Microsoft.Extensions.DependencyInjection 10.0.0-preview.1.25080.5 includes an explicit net10.0 target (alongside net8.0, net9.0, netstandard2.0, netstandard2.1, and net462). [1]
🌐 Web query:
Microsoft.Extensions.Hosting 10.0.0-preview.1.25080.5 net10.0 support
💡 Result:
Yes—Microsoft.Extensions.Hosting 10.0.0-preview.1.25080.5 can be used from a net10.0 project.
On NuGet, the package includes assets for net8.0 and netstandard2.0, and NuGet marks it as compatible with net10.0 (computed compatibility), meaning a net10.0 app will consume the net8.0 (or netstandard2.0) build rather than a net10.0-specific build. [1]
What this means: you can reference it in net10.0, but you shouldn’t expect net10.0-only APIs/behavior unless a later package version ships net10.0-targeted assets. [1]
Most package versions lack explicit net10.0 support; they rely on NuGet framework compatibility rules.
Avalonia 11.2.3, FluentAvaloniaUI 2.2.0, CommunityToolkit.Mvvm 8.4.0, and DynamicData 9.0.4 do not ship net10.0-targeted builds. Instead, they target net8.0, net6.0, or netstandard2.0, and NuGet marks them as compatible with net10.0 by falling back to the closest matching assets (typically net8.0). While this arrangement works, it means these packages are not explicitly tested or optimized for .NET 10. The Microsoft.Extensions packages (10.0.0-preview.1.25080.5) are an exception: Microsoft.Extensions.DependencyInjection includes explicit net10.0 assets, though Microsoft.Extensions.Hosting relies on computed compatibility to net8.0 assets.
🤖 Prompt for AI Agents
In `@Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj` around lines 34 - 46,
Summary: Several PackageReference entries in Flow.Launcher.Avalonia.csproj
(Avalonia, Avalonia.Desktop, Avalonia.Themes.Fluent, Avalonia.Fonts.Inter,
FluentAvaloniaUI, CommunityToolkit.Mvvm, DynamicData) do not ship explicit
net10.0 assets and rely on NuGet compatibility fallbacks. Fix: in
Flow.Launcher.Avalonia.csproj inspect each PackageReference (e.g., Avalonia
11.2.3, FluentAvaloniaUI 2.2.0, CommunityToolkit.Mvvm 8.4.0, DynamicData 9.0.4)
and either upgrade to package versions that explicitly target net10.0 or add a
comment/verification step in the csproj/PR to confirm net10 compatibility; if no
net10 build exists, document acceptance of NuGet fallback or replace with
alternatives that support net10.0; ensure Microsoft.Extensions.* preview
packages are intentionally used (DependencyInjection and Hosting) and update
them to GA net10-compatible versions where available.
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net9.0-windows10.0.19041.0</TargetFramework> | ||
| <TargetFramework>net10.0-windows10.0.19041.0</TargetFramework> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check for SDK pinning configuration
echo "=== Checking global.json for SDK pinning ==="
if [ -f global.json ]; then
cat global.json
else
echo "No global.json found at root"
fi
echo -e "\n=== Checking Directory.Build.props for framework configuration ==="
if [ -f Directory.Build.props ]; then
cat Directory.Build.props
else
echo "No Directory.Build.props found at root"
fi
echo -e "\n=== Searching all .csproj files for TargetFramework ==="
find . -name "*.csproj" -type f | head -20 | while read file; do
echo "File: $file"
rg -n 'TargetFramework' "$file" || true
done
echo -e "\n=== Checking for net10 references across codebase ==="
rg -n 'net10\.0' --type=csproj
echo -e "\n=== Checking CI configuration (common locations) ==="
for ci_file in .github/workflows/*.yml .github/workflows/*.yaml azure-pipelines.yml .gitlab-ci.yml .circleci/config.yml; do
if [ -f "$ci_file" ]; then
echo "Found CI file: $ci_file"
rg -n 'dotnet-version|DOTNET_VERSION|sdk-version' "$ci_file" || echo "No SDK version spec in $ci_file"
fi
done
echo -e "\n=== Checking Flow.Launcher.Test.csproj in detail ==="
cat -n Flow.Launcher.Test/Flow.Launcher.Test.csproj 2>/dev/null || echo "File not found"Repository: Flow-Launcher/Flow.Launcher
Length of output: 8955
Update global.json and CI workflows to .NET 10 before merge; entire codebase is misaligned.
The test project is already using net10.0-windows10.0.19041.0, but the repository's SDK pinning in global.json is still locked to 9.0.* and CI workflows specify dotnet-version: 9.0.x. This mismatch affects the entire codebase—multiple projects have been upgraded to net10.0 (Flow.Launcher.Core, Flow.Launcher.Infrastructure, Flow.Launcher, plugins)—but the build toolchain hasn't been updated. CI will fail to build any net10.0 project with .NET 9. Update global.json to pin .NET 10 and update .github/workflows/dotnet.yml and .github/workflows/default_plugins.yml to use dotnet-version: 10.0.x. The test packages (Moq 4.20.72, nunit 4.4.0, NUnit3TestAdapter 5.1.0, Microsoft.NET.Test.Sdk 17.14.1) are all compatible with .NET 10.
🤖 Prompt for AI Agents
In `@Flow.Launcher.Test/Flow.Launcher.Test.csproj` at line 4, The repository SDK
pinning and CI use .NET 9 while projects target net10.0; update the SDK and CI
to .NET 10 by changing the "version" value in global.json's "sdk" object to a
10.x value (e.g., "10.0.*") and update the GitHub Actions setup step(s)
(actions/setup-dotnet invocation(s)) to use dotnet-version: 10.0.x (including
any matrix entries or multiple workflow files like the two CI workflows) so the
build toolchain matches the net10.0 target across projects.
| <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0-preview.1.25080.5" /> | ||
| <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-preview.1.25080.5" /> |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Microsoft.Extensions.DependencyInjection 10.0 stable release NuGet
💡 Result:
- Microsoft.Extensions.DependencyInjection 10.0 stable (GA) NuGet release: 10.0.0, published Nov 11, 2025. [1]
- Latest 10.0.x stable currently on NuGet: 10.0.2, published Jan 13, 2026. [2]
Install (NuGet Package Manager):
Install-Package Microsoft.Extensions.DependencyInjection -Version 10.0.0(or omit -Version to get the latest stable 10.0.x). [2]
Related package: Microsoft.Extensions.DependencyInjection.Abstractions is also at 10.0.2 (Jan 13, 2026). [3]
🌐 Web query:
Microsoft.Extensions.Hosting 10.0 stable release NuGet
💡 Result:
The stable 10.0 release of Microsoft.Extensions.Hosting is published on NuGet as:
- 10.0.0 (released Nov 11, 2025) [1]
- Latest stable 10.0.x: 10.0.2 (released Jan 13, 2026) [2]
Install (latest stable 10.0.x):
dotnet add package Microsoft.Extensions.Hosting --version 10.0.2Or pin the first stable 10.0:
dotnet add package Microsoft.Extensions.Hosting --version 10.0.0Update to stable versions of Microsoft.Extensions packages.
The Microsoft.Extensions.DependencyInjection and Microsoft.Extensions.Hosting packages are pinned to preview version 10.0.0-preview.1.25080.5. Stable versions 10.0.0 were released Nov 11, 2025, and the latest stable is 10.0.2 (Jan 13, 2026). Upgrade to the stable release to avoid preview-phase breaking changes and bugs.
🤖 Prompt for AI Agents
In `@Flow.Launcher/Flow.Launcher.csproj` around lines 147 - 148, Update the two
PackageReference entries that currently point to the preview version
"10.0.0-preview.1.25080.5" so they reference a stable release (preferably
"10.0.2" or at minimum "10.0.0"); specifically change the PackageReference for
Microsoft.Extensions.DependencyInjection and Microsoft.Extensions.Hosting to the
stable version string to avoid using preview packages.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@check-spelling-bot Report🔴 Please reviewSee the 📂 files view, the 📜action log, or 📝 job summary for details.
See ❌ Event descriptions for more information. Forbidden patterns 🙅 (1)In order to address this, you could change the content to not match the forbidden patterns (comments before forbidden patterns may help explain why they're forbidden), add patterns for acceptable instances, or adjust the forbidden patterns themselves. These forbidden patterns matched content: s.b. preexistingIf the flagged items are 🤯 false positivesIf items relate to a ...
|
I actually vibe everything for this project (given the fact that it is mostly migration) as I want to see how far the agent can go. Very surprising, opencode can generate a working version with less than 3 hours (which I've been trying for much longer before).
Summary
This PR introduces an experimental Avalonia UI implementation alongside the existing WPF UI, allowing incremental migration and side-by-side comparison.
Architecture
Main Window & Query Flow
Results Handling
Image Loading
Performance
What Works
Not Yet Implemented