Skip to content

Avalonia migration#4214

Draft
taooceros wants to merge 45 commits intodevfrom
avalonia_migration
Draft

Avalonia migration#4214
taooceros wants to merge 45 commits intodevfrom
avalonia_migration

Conversation

@taooceros
Copy link
Copy Markdown
Member

@taooceros taooceros commented Jan 15, 2026

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

  • New Flow.Launcher.Avalonia project targeting .NET 9 with Windows 10.0.19041
  • Shares core components (Flow.Launcher.Plugin, Flow.Launcher.Infrastructure, Flow.Launcher.Core) with WPF
  • Separate output directory (Output/Debug/Avalonia) to run independently from WPF version
  • Uses CommunityToolkit.Mvvm for MVVM pattern with source generators

Main Window & Query Flow

  • Basic main window with query textbox and results list
  • Full plugin system integration - queries route through existing PluginManager
  • Global hotkey support (Alt+Space) using native Windows hooks
  • Results sorted globally by score across all plugins
  • Progressive result display - results appear as each plugin completes without waiting for all

Results Handling

  • DynamicData SourceList for reactive collection management with automatic sorting
  • EditDiff for minimal UI updates when results change, reducing flickering
  • Results persist while typing until new results arrive
  • Keyboard navigation (Up/Down arrows, Enter to execute, Escape to hide)

Image Loading

  • Custom ImageLoader using Windows Shell API (IShellItemImageFactory)
  • Extracts icons from exe, dll, ico, lnk files and folders
  • Proper alpha channel handling with AlphaFormat.Unpremul for transparent icons
  • Async loading with caching

Performance

  • Plugin queries wrapped in Task.Run() to prevent UI blocking from synchronous plugin code
  • Parallel plugin execution with ConcurrentBag for thread-safe result accumulation
  • Background image loading to keep UI responsive

What Works

  • Launching and toggling window with global hotkey
  • Querying plugins and displaying results
  • Executing results (opening files, apps, etc.)
  • Icons for executables, folders, and standard image formats

Not Yet Implemented

  • Settings UI
  • Context menus
  • Themes/styling
  • System tray
  • Auto-updates
  • Many other WPF features

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
Copilot AI review requested due to automatic review settings January 15, 2026 08:31
@taooceros taooceros marked this pull request as draft January 15, 2026 08:31
@github-actions github-actions bot added this to the 2.1.0 milestone Jan 15, 2026
@github-actions

This comment has been minimized.

@taooceros taooceros modified the milestones: 2.1.0, Future, 3.0.0 Jan 15, 2026
@prlabeler prlabeler bot added bug Something isn't working enhancement New feature or request labels Jan 15, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.Avalonia project 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>
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>

Copilot uses AI. Check for mistakes.
Comment on lines +153 to +154
// Also output to console for easy debugging
System.Console.WriteLine($"[{level}] {classNameWithMethod}: {message}");
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
// 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

Copilot uses AI. Check for mistakes.
Comment on lines +135 to +139
public void Dispose()
{
_subscription.Dispose();
_sourceList.Dispose();
}
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.

if (RegisterClass(ref wc) == 0)
{
Console.WriteLine("[GlobalHotkey] Failed to register window class");
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
var wc = new WNDCLASS
{
lpfnWndProc = _wndProc,
hInstance = GetModuleHandle(null),
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace this call with a call to managed code if possible.

Suggested change
hInstance = GetModuleHandle(null),
hInstance = Marshal.GetHINSTANCE(typeof(GlobalHotkey).Module),

Copilot uses AI. Check for mistakes.
private string _subTitle = string.Empty;

[ObservableProperty]
private string _iconPath = string.Empty;
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Field '_iconPath' can be 'readonly'.

Copilot uses AI. Check for mistakes.
public partial class ResultViewModel : ObservableObject
{
[ObservableProperty]
private string _title = string.Empty;
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Field '_title' can be 'readonly'.

Copilot uses AI. Check for mistakes.

public partial class ResultListBox : UserControl
{
private ListBox? _listBox;
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Field '_listBox' can be 'readonly'.

Copilot uses AI. Check for mistakes.
private int _selectedIndex;

[ObservableProperty]
private bool _isVisible = true;
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Field '_isVisible' can be 'readonly'.

Copilot uses AI. Check for mistakes.
private bool _hasResults;

[ObservableProperty]
private ResultsViewModel _results;
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Field '_results' can be 'readonly'.

Copilot uses AI. Check for mistakes.
- 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
@github-actions

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
@github-actions

This comment has been minimized.

@TBM13
Copy link
Copy Markdown
Contributor

TBM13 commented Jan 16, 2026

Is there any actual benefit to switching to Avalonia or it's just for testing purposes?

@taooceros
Copy link
Copy Markdown
Member Author

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
@github-actions

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
@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. Implementing copy logic for other common inline types (e.g., Span, Bold, Italic)
  2. 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.

MaxResultsRange creates a new List<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.

QueryBoxFontSize and ResultItemFontSize setters don't check for equality before assigning, unlike the other properties in this ViewModel (MaxResults, UseGlyphIcons, SelectedColorScheme). This can cause unnecessary PropertyChanged notifications.

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:

  1. 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.
  2. Missing spaces around pipe separators in many table rows (MD060 violations).
  3. 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 text or plaintext to 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 cloc or 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/Height can clip text at higher scaling. Using MinWidth/MinHeight preserves 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}">

Comment on lines +23 to +149
[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];
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ 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 -100

Repository: 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.axaml

Repository: 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.

Comment on lines +40 to +43
public Language? SelectedLanguage
{
get => Languages.FirstOrDefault(l => l.LanguageCode == _settings.Language);
set
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +339 to +354
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;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +54 to +61
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)
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +25 to +31
_settings.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(Settings.Language))
{
UpdateLabels();
}
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +18 to +29
<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>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "PluginStoreSettingsPage.axaml" -type f

Repository: Flow-Launcher/Flow.Launcher

Length of output: 144


🏁 Script executed:

cat -n Flow.Launcher.Avalonia/Views/SettingPages/PluginStoreSettingsPage.axaml | head -150

Repository: Flow-Launcher/Flow.Launcher

Length of output: 11657


🏁 Script executed:

rg "AutomationProperties" --type xml --type xaml -i

Repository: Flow-Launcher/Flow.Launcher

Length of output: 99


🏁 Script executed:

rg "AutomationProperties" -i

Repository: 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 Button content 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.LabeledBy is 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.

Suggested change
<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.

Comment on lines +28 to +32
<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>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
<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>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
<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".

Comment on lines +229 to +232
<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>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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")
PY

Repository: 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
@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.Diagnostics does 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 DEBUG for 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");

Comment on lines +193 to +202
/// <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.
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Search for IResultUpdated and IResultUpdateRegister usage in the codebase
rg -i "IResultUpdated|IResultUpdateRegister" --type cs

Repository: 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.cs

Repository: 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.cs

Repository: 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 -80

Repository: 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.cs

Repository: 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 -20

Repository: 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.cs

Repository: 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.cs

Repository: 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.cs

Repository: 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.

Comment on lines +267 to +269
_queryTokenSource?.Cancel();
_queryTokenSource = new CancellationTokenSource();
var token = _queryTokenSource.Token;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
_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.

Comment on lines +72 to +80
<!-- 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 link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
<!-- 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.

Comment on lines +544 to 559
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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 f

Repository: 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 -n

Repository: 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 -n

Repository: 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 -n

Repository: 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.

Comment on lines +61 to +64
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.2.3" />
<PackageReference Include="FluentAvaloniaUI" Version="2.2.0" />
</ItemGroup>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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.

@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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">
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.)

View Feedback

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 &quot;Get-ChildItem '$(OutputPath.TrimEnd('\'))' -Filter '*.dll' | ForEach-Object { $name = $_.Name; Get-ChildItem '$(OutputPath)Plugins' -Recurse -Filter $name | Remove-Item -Force }&quot;" IgnoreExitCode="true" />
+  </Target>
</file context>
Fix with Cubic


1. Build order matters - plugins must build before WPF
2. Build kills running `Flow.Launcher.exe` automatically
3. Plugins run in separate app domains
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
3. Plugins run in separate app domains
3. Plugins are loaded via AssemblyLoadContext in the same process (no AppDomain isolation)
Fix with Cubic

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-Object can return $null (no files), and Get-ChildItem -Recurse can 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/Debug and Output/Debug/Avalonia without verifying they exist. If either path is missing (e.g., before first build), the script will throw errors. Consider wrapping with Test-Path checks 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/Avalonia and Output/Debug/Avalonia/Plugins exist. 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.

FileVersion can 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 (showing SameVersion = 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/Avalonia or Output/Debug/Avalonia/Plugins directories 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 $currentSize from all files under Output/Debug/Avalonia recursively, which includes the Plugins subdirectory. The $reducedSize is 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 SilentlyContinue throughout.

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 SilentlyContinue while 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).Sum
Scripts/size_analysis.ps1 (1)

2-2: Remove unused parameter.

The $path parameter 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.

Comment on lines +27 to +52
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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n "ChangeQuery\s*\(|ReQuery\s*\(|Requery" -g '*.cs' -A 3

Repository: 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 2

Repository: 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 2

Repository: 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 -->
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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 -->
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
<!-- 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))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +4 to +5
$pluginRuntimes = Get-ChildItem "Output/Release/Avalonia/Plugins" -Recurse -Directory -Filter "runtimes"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +39 to +40
Write-Host "=== WPF Debug ===" -ForegroundColor Cyan
Write-Host "Total: $(Get-FolderSize 'Output\Debug') MB (excluding Avalonia subfolder)"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
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>
@github-actions

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>
@github-actions

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>
@github-actions

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>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 text as 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 implementing CanExecute conditions for command consistency.

Per the ICommand contract, RelayCommand.Execute() does not re-check CanExecute()—the caller is responsible. Currently, these four commands lack CanExecute guards and don't have conditional disable logic defined. While the implementations already include internal validation (e.g., LoadContextMenu() checks if a result exists), explicitly implementing CanExecute conditions 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 |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
| 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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
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.

Comment on lines +62 to +70
<!-- Search Icon -->
<Canvas Name="SearchIconCanvas"
Classes="searchIconPosition"
HorizontalAlignment="Right">
<Path Name="SearchIcon"
Classes="searchIcon"
Data="{DynamicResource SearchIconGeometry}"
Stretch="Uniform" />
</Canvas>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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).

@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

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>
Suggested change
<TargetFramework>net10.0-windows</TargetFramework>
<TargetFramework>net9.0-windows</TargetFramework>
Fix with Cubic

<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" />
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Fix with Cubic

<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0-windows10.0.19041.0</TargetFramework>
<TargetFramework>net10.0-windows10.0.19041.0</TargetFramework>
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Fix with Cubic

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +20 to +28
<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>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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 cs

Repository: 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 -20

Repository: 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.csproj

Repository: 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.

Suggested change
<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).

Comment on lines +34 to +46
<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>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -name "Flow.Launcher.Avalonia.csproj" -type f

Repository: Flow-Launcher/Flow.Launcher

Length of output: 125


🏁 Script executed:

cat -n ./Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj | head -50

Repository: 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:


🌐 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.0
  • netstandard2.1
  • net8.0
  • net8.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:


🌐 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:

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:


🌐 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>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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.

Comment on lines +147 to +148
<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" />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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.2

Or pin the first stable 10.0:

dotnet add package Microsoft.Extensions.Hosting --version 10.0.0

Update 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.

@jjw24 jjw24 marked this pull request as draft February 22, 2026 02:38
@jjw24 jjw24 modified the milestones: 3.0.0, Future Feb 22, 2026
@github-actions
Copy link
Copy Markdown

@check-spelling-bot Report

🔴 Please review

See the 📂 files view, the 📜action log, or 📝 job summary for details.

❌ Errors and Warnings Count
❌ check-file-path 199
❌ forbidden-pattern 1
⚠️ noisy-file 2
⚠️ non-alpha-in-dictionary 2

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. preexisting

[Pp]re[- ]existing
If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

30 min review bug Something isn't working enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants