Conversation
Create Flow.Launcher.Avalonia project as foundation for migrating from WPF to Avalonia UI framework. Key components: - MainWindow with query box and results list (matching WPF layout) - ViewModels: MainViewModel, ResultsViewModel, ResultViewModel - Themes/Base.axaml with converted styles from WPF - FluentAvaloniaUI for Windows 11 styling - References existing Core/Infrastructure/Plugin projects The project builds and runs alongside the existing WPF application. This is Phase 1 of the incremental migration approach.
- Load settings from disk via FlowLauncherJsonStorage - Initialize PluginManager and query plugins on text change - Add minimal AvaloniaPublicAPI implementing IPublicAPI - Execute plugin results on Enter key - Add WPF framework reference for IPublicAPI compatibility
…oading - Use DynamicData SourceList with automatic sorting by score descending - Add ReplaceResults() with EditDiff for minimal UI updates (reduces flickering) - Keep previous results visible while typing until new results arrive - Add ImageLoader with Windows Shell API (IShellItemImageFactory) for exe/ico icons - Use AlphaFormat.Unpremul to correctly render transparent icons without white borders - Query all plugins in parallel and merge/sort results globally
- Wrap each plugin query in Task.Run() to ensure synchronous plugin code doesn't block the UI thread - Show results progressively as each plugin completes using ConcurrentBag - Update UI after each plugin returns instead of waiting for all plugins
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
This PR introduces a new Avalonia UI frontend for Flow Launcher as an alternative to the existing WPF implementation. The migration adds a complete new project with UI components, ViewModels, and platform integration while maintaining compatibility with the existing plugin system.
Changes:
- Adds new
Flow.Launcher.Avaloniaproject with complete UI implementation including MainWindow, result list views, and styling - Implements Windows global hotkey support and image loading utilities with native Win32 interop
- Adds console logging output to infrastructure logger for easier debugging
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 50 comments.
Show a summary per file
| File | Description |
|---|---|
| Flow.Launcher.sln | Adds Flow.Launcher.Avalonia project to solution with build configurations |
| Flow.Launcher.Infrastructure/Logger/Log.cs | Adds console output for log messages |
| Flow.Launcher.Avalonia/app.manifest | Defines Windows compatibility and DPI awareness settings |
| Flow.Launcher.Avalonia/Views/ResultListBox.axaml | XAML markup for search results list display |
| Flow.Launcher.Avalonia/Views/ResultListBox.axaml.cs | Code-behind for result list interactions |
| Flow.Launcher.Avalonia/ViewModel/ResultsViewModel.cs | ViewModel managing result collection with sorting and selection |
| Flow.Launcher.Avalonia/ViewModel/ResultViewModel.cs | ViewModel for individual result items |
| Flow.Launcher.Avalonia/ViewModel/MainViewModel.cs | Main application ViewModel coordinating queries and UI state |
| Flow.Launcher.Avalonia/Themes/Resources.axaml | Color and resource definitions for UI theme |
| Flow.Launcher.Avalonia/Themes/Base.axaml | Style definitions for UI components |
| Flow.Launcher.Avalonia/Program.cs | Application entry point |
| Flow.Launcher.Avalonia/MainWindow.axaml | Main window XAML markup |
| Flow.Launcher.Avalonia/MainWindow.axaml.cs | Main window code-behind with event handling |
| Flow.Launcher.Avalonia/Helper/ImageLoader.cs | Image loading utility with caching and Win32 shell integration |
| Flow.Launcher.Avalonia/Helper/HotKeyMapper.cs | Global hotkey registration management |
| Flow.Launcher.Avalonia/Helper/GlobalHotkey.cs | Win32-based global hotkey implementation |
| Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj | Project configuration and dependencies |
| Flow.Launcher.Avalonia/Converters/CommonConverters.cs | Value converters for data binding |
| Flow.Launcher.Avalonia/Converters/BoolToIsVisibleConverter.cs | Boolean to visibility converter |
| Flow.Launcher.Avalonia/AvaloniaPublicAPI.cs | IPublicAPI implementation for plugin compatibility |
| Flow.Launcher.Avalonia/App.axaml | Application XAML with theme configuration |
| Flow.Launcher.Avalonia/App.axaml.cs | Application code-behind with initialization logic |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| <application xmlns="urn:schemas-microsoft-com:asm.v3"> | ||
| <windowsSettings> | ||
| <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> |
There was a problem hiding this comment.
Corrected spelling of 'true/pm' to 'true/PM' - the value appears to be malformed. It should be just 'true' for the dpiAware element, or 'PerMonitor' for per-monitor DPI awareness.
| <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> | |
| <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> |
| // Also output to console for easy debugging | ||
| System.Console.WriteLine($"[{level}] {classNameWithMethod}: {message}"); |
There was a problem hiding this comment.
Console logging added to production code affects performance and should be conditional. Consider wrapping this in a DEBUG preprocessor directive or using a configuration flag to enable/disable console output, as this will log every message in Release builds.
| // Also output to console for easy debugging | |
| System.Console.WriteLine($"[{level}] {classNameWithMethod}: {message}"); | |
| // Also output to console for easy debugging | |
| #if DEBUG | |
| System.Console.WriteLine($"[{level}] {classNameWithMethod}: {message}"); | |
| #endif |
| public void Dispose() | ||
| { | ||
| _subscription.Dispose(); | ||
| _sourceList.Dispose(); | ||
| } |
There was a problem hiding this comment.
The Dispose method doesn't follow the standard IDisposable pattern. Consider implementing the full pattern with a protected virtual Dispose(bool disposing) method and a finalizer to handle unmanaged resources properly, especially since this class manages disposable resources.
|
|
||
| if (RegisterClass(ref wc) == 0) | ||
| { | ||
| Console.WriteLine("[GlobalHotkey] Failed to register window class"); |
There was a problem hiding this comment.
Direct Console.WriteLine calls for logging should use the application's logging infrastructure (Flow.Launcher.Infrastructure.Logger.Log) for consistency and proper log management. This applies to all Console.WriteLine statements in this file.
| var wc = new WNDCLASS | ||
| { | ||
| lpfnWndProc = _wndProc, | ||
| hInstance = GetModuleHandle(null), |
There was a problem hiding this comment.
Replace this call with a call to managed code if possible.
| hInstance = GetModuleHandle(null), | |
| hInstance = Marshal.GetHINSTANCE(typeof(GlobalHotkey).Module), |
| private string _subTitle = string.Empty; | ||
|
|
||
| [ObservableProperty] | ||
| private string _iconPath = string.Empty; |
There was a problem hiding this comment.
Field '_iconPath' can be 'readonly'.
| public partial class ResultViewModel : ObservableObject | ||
| { | ||
| [ObservableProperty] | ||
| private string _title = string.Empty; |
There was a problem hiding this comment.
Field '_title' can be 'readonly'.
|
|
||
| public partial class ResultListBox : UserControl | ||
| { | ||
| private ListBox? _listBox; |
There was a problem hiding this comment.
Field '_listBox' can be 'readonly'.
| private int _selectedIndex; | ||
|
|
||
| [ObservableProperty] | ||
| private bool _isVisible = true; |
There was a problem hiding this comment.
Field '_isVisible' can be 'readonly'.
| private bool _hasResults; | ||
|
|
||
| [ObservableProperty] | ||
| private ResultsViewModel _results; |
There was a problem hiding this comment.
Field '_results' can be 'readonly'.
- Add ActiveView enum to track Results vs ContextMenu view state - Add ContextMenu ResultsViewModel and view switching logic - Implement LoadContextMenuCommand using PluginManager.GetContextMenusForPlugin - Add keyboard navigation: Shift+Enter/Right to open, Left/Escape to close - Update SelectNextItem/SelectPrevItem to navigate appropriate list - Update EscCommand to return from context menu before hiding
- Create Internationalization service that parses WPF XAML language files - Load translations from main Languages/ folder and all plugin Languages/ folders - Add LocalizeExtension markup extension and Translator helper for XAML/code - Fix IPublicAPI.GetTranslation to use the i18n service for plugin context menus - Update MainWindow to use localized placeholder text
This comment has been minimized.
This comment has been minimized.
- Internationalization.Initialize() called in constructor when DI creates it - Remove InitializeInternationalization() method from App.axaml.cs - Update AvaloniaPublicAPI and LocalizeExtension to use Ioc.Default.GetService - Reorder ConfigureDI before i18n initialization
This comment has been minimized.
This comment has been minimized.
|
Is there any actual benefit to switching to Avalonia or it's just for testing purposes? |
There should be some performance improvement for animation. Besides, I also want to have the chance to make flow cross platform as I am gradually migrating my workflow to mac. |
- MainWindowVisibility starts as false (window hidden) - Window IsVisible bound to MainWindowVisibility - Set MainWindowVisibility = true in OnPluginsReady() after plugins load
This comment has been minimized.
This comment has been minimized.
- Add Glyph and GlyphAvailable properties to ResultViewModel - Set Glyph from plugin Result when creating result items - Add resultGlyph style matching icon size (32x32) - Update ResultListBox to show glyph or image icon based on ShowGlyph property - ShowGlyph = true when UseGlyphIcons is enabled AND glyph is available
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj`:
- Around line 20-28: The PropertyGroup entries currently replace the
DefineConstants value; update both Debug and Release PropertyGroup blocks so
they append SDK/product constants instead of overriding by adding the existing
$(DefineConstants) token and then the SDK symbols (e.g. NET10_0, WINDOWS or
other platform/runtime symbols you expect) to the DefineConstants attribute;
locate the DefineConstants attributes in the Flow.Launcher.Avalonia.csproj and
change them to include $(DefineConstants) followed by the additional constants
(keep DEBUG/TRACE/RELEASE/AVALONIA as appropriate).
- Around line 34-46: Summary: Several PackageReference entries in
Flow.Launcher.Avalonia.csproj (Avalonia, Avalonia.Desktop,
Avalonia.Themes.Fluent, Avalonia.Fonts.Inter, FluentAvaloniaUI,
CommunityToolkit.Mvvm, DynamicData) do not ship explicit net10.0 assets and rely
on NuGet compatibility fallbacks. Fix: in Flow.Launcher.Avalonia.csproj inspect
each PackageReference (e.g., Avalonia 11.2.3, FluentAvaloniaUI 2.2.0,
CommunityToolkit.Mvvm 8.4.0, DynamicData 9.0.4) and either upgrade to package
versions that explicitly target net10.0 or add a comment/verification step in
the csproj/PR to confirm net10 compatibility; if no net10 build exists, document
acceptance of NuGet fallback or replace with alternatives that support net10.0;
ensure Microsoft.Extensions.* preview packages are intentionally used
(DependencyInjection and Hosting) and update them to GA net10-compatible
versions where available.
In `@Flow.Launcher.Test/Flow.Launcher.Test.csproj`:
- Line 4: The repository SDK pinning and CI use .NET 9 while projects target
net10.0; update the SDK and CI to .NET 10 by changing the "version" value in
global.json's "sdk" object to a 10.x value (e.g., "10.0.*") and update the
GitHub Actions setup step(s) (actions/setup-dotnet invocation(s)) to use
dotnet-version: 10.0.x (including any matrix entries or multiple workflow files
like the two CI workflows) so the build toolchain matches the net10.0 target
across projects.
In `@Flow.Launcher/Flow.Launcher.csproj`:
- Around line 147-148: Update the two PackageReference entries that currently
point to the preview version "10.0.0-preview.1.25080.5" so they reference a
stable release (preferably "10.0.2" or at minimum "10.0.0"); specifically change
the PackageReference for Microsoft.Extensions.DependencyInjection and
Microsoft.Extensions.Hosting to the stable version string to avoid using preview
packages.
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | ||
| <OutputPath>..\Output\Debug\Avalonia\</OutputPath> | ||
| <DefineConstants>DEBUG;TRACE;AVALONIA</DefineConstants> | ||
| </PropertyGroup> | ||
|
|
||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> | ||
| <OutputPath>..\Output\Release\Avalonia\</OutputPath> | ||
| <DefineConstants>TRACE;RELEASE;AVALONIA</DefineConstants> | ||
| </PropertyGroup> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check for conditional compilation that depends on SDK-defined constants
rg -nP '^\s*#if\s+(NET|NET10_0|NET10_0_WINDOWS|WINDOWS)\b' --type csRepository: Flow-Launcher/Flow.Launcher
Length of output: 53
🏁 Script executed:
#!/bin/bash
# Check if there are ANY conditional compilation directives in C# files
rg -nP '^\s*#if\b' --type cs | head -20Repository: Flow-Launcher/Flow.Launcher
Length of output: 1628
🏁 Script executed:
#!/bin/bash
# Check the TargetFramework and other project properties in the .csproj file
rg -A 2 'TargetFramework|DefineConstants' Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csprojRepository: Flow-Launcher/Flow.Launcher
Length of output: 623
Append SDK constants to preserve best practice consistency.
While the codebase doesn't currently depend on SDK-defined constants (like NET10_0 or WINDOWS) in conditional compilation, it's best practice to append to $(DefineConstants) rather than override it to safeguard against future uses and maintain consistency.
🛠️ Proposed fix
- <DefineConstants>DEBUG;TRACE;AVALONIA</DefineConstants>
+ <DefineConstants>$(DefineConstants);DEBUG;TRACE;AVALONIA</DefineConstants>
...
- <DefineConstants>TRACE;RELEASE;AVALONIA</DefineConstants>
+ <DefineConstants>$(DefineConstants);TRACE;RELEASE;AVALONIA</DefineConstants>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | |
| <OutputPath>..\Output\Debug\Avalonia\</OutputPath> | |
| <DefineConstants>DEBUG;TRACE;AVALONIA</DefineConstants> | |
| </PropertyGroup> | |
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> | |
| <OutputPath>..\Output\Release\Avalonia\</OutputPath> | |
| <DefineConstants>TRACE;RELEASE;AVALONIA</DefineConstants> | |
| </PropertyGroup> | |
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | |
| <OutputPath>..\Output\Debug\Avalonia\</OutputPath> | |
| <DefineConstants>$(DefineConstants);DEBUG;TRACE;AVALONIA</DefineConstants> | |
| </PropertyGroup> | |
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> | |
| <OutputPath>..\Output\Release\Avalonia\</OutputPath> | |
| <DefineConstants>$(DefineConstants);TRACE;RELEASE;AVALONIA</DefineConstants> | |
| </PropertyGroup> |
🤖 Prompt for AI Agents
In `@Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj` around lines 20 - 28,
The PropertyGroup entries currently replace the DefineConstants value; update
both Debug and Release PropertyGroup blocks so they append SDK/product constants
instead of overriding by adding the existing $(DefineConstants) token and then
the SDK symbols (e.g. NET10_0, WINDOWS or other platform/runtime symbols you
expect) to the DefineConstants attribute; locate the DefineConstants attributes
in the Flow.Launcher.Avalonia.csproj and change them to include
$(DefineConstants) followed by the additional constants (keep
DEBUG/TRACE/RELEASE/AVALONIA as appropriate).
| <ItemGroup> | ||
| <PackageReference Include="Avalonia" Version="11.2.3" /> | ||
| <PackageReference Include="Avalonia.Desktop" Version="11.2.3" /> | ||
| <PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.3" /> | ||
| <PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.3" /> | ||
| <PackageReference Include="FluentAvaloniaUI" Version="2.2.0" /> | ||
| <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" /> | ||
| <PackageReference Include="DynamicData" Version="9.0.4" /> | ||
| <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0-preview.1.25080.5" /> | ||
| <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-preview.1.25080.5" /> | ||
| <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.--> | ||
| <PackageReference Include="Avalonia.Diagnostics" Version="11.2.3" Condition="'$(Configuration)' == 'Debug'" /> | ||
| </ItemGroup> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "Flow.Launcher.Avalonia.csproj" -type fRepository: Flow-Launcher/Flow.Launcher
Length of output: 125
🏁 Script executed:
cat -n ./Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj | head -50Repository: Flow-Launcher/Flow.Launcher
Length of output: 3087
🌐 Web query:
Avalonia 11.2.3 net10.0 target framework support
💡 Result:
Avalonia 11.2.3 does not ship “net10.0” target frameworks (no lib/net10.0/* assets). For example, Avalonia and Avalonia.Desktop 11.2.3 target net6.0, net8.0, and netstandard2.0. [1][2]
That said, a net10.0 app can still reference Avalonia 11.2.3: NuGet marks these packages as compatible with net10.0 via framework compatibility rules (it will select the closest matching assets, e.g., net8.0/net6.0). [1][2]
Platform note: Avalonia.Android 11.2.3 targets net8.0-android34.0, and NuGet also computes compatibility for net10.0-android (but it’s still not a native net10.0-android* asset). [3]
Sources: [1] [2] [3]
🌐 Web query:
FluentAvaloniaUI 2.2.0 net10.0 target framework support
💡 Result:
FluentAvaloniaUI 2.2.0 does not ship a net10.0-targeted build. On NuGet, the package’s included TFMs are net6.0 and .NETStandard 2.0/2.1. (nuget.org)
If your app targets net10.0, NuGet will still allow the reference because it’s computed as compatible (“net10.0 was computed”) and it will fall back to using the package’s net6.0` (or .NET Standard) assets. (nuget.org)
Citations:
- 1: https://www.nuget.org/packages/FluentAvaloniaUI/2.2.0?utm_source=openai
- 2: https://www.nuget.org/packages/FluentAvaloniaUI/2.2.0?utm_source=openai
🌐 Web query:
CommunityToolkit.Mvvm 8.4.0 net10.0 target framework support
💡 Result:
CommunityToolkit.Mvvm 8.4.0 does not ship a net10.0-specific build. Its included TFMs are:
netstandard2.0netstandard2.1net8.0net8.0-windows10.0.17763(nuget.org)
A project targeting net10.0 can still reference it, because NuGet marks the package as compatible with “.NET 8.0 or higher” and lists net10.0 as a computed compatible TFM (meaning it will typically use the net8.0 assets). (nuget.org)
Citations:
- 1: https://www.nuget.org/packages/CommunityToolkit.Mvvm/8.4.0?utm_source=openai
- 2: https://www.nuget.org/packages/CommunityToolkit.Mvvm/8.4.0?utm_source=openai
🌐 Web query:
DynamicData 9.0.4 net10.0 target framework support
💡 Result:
DynamicData 9.0.4 does not ship a net10.0-targeted assembly. Its included TFMs are:
net6.0netstandard2.0net462(nuget.org)
A project targeting net10.0 can still reference DynamicData 9.0.4 because net10.0 is compatible with libraries targeting net6.0 (NuGet will select the net6.0 asset). (nuget.org)
Citations:
- 1: https://www.nuget.org/packages/DynamicData/9.0.4
- 2: https://www.nuget.org/packages/DynamicData/9.0.4
🌐 Web query:
Microsoft.Extensions.DependencyInjection 10.0.0-preview.1.25080.5 net10.0 support
💡 Result:
Yes. Microsoft.Extensions.DependencyInjection 10.0.0-preview.1.25080.5 includes an explicit net10.0 target (alongside net8.0, net9.0, netstandard2.0, netstandard2.1, and net462). [1]
🌐 Web query:
Microsoft.Extensions.Hosting 10.0.0-preview.1.25080.5 net10.0 support
💡 Result:
Yes—Microsoft.Extensions.Hosting 10.0.0-preview.1.25080.5 can be used from a net10.0 project.
On NuGet, the package includes assets for net8.0 and netstandard2.0, and NuGet marks it as compatible with net10.0 (computed compatibility), meaning a net10.0 app will consume the net8.0 (or netstandard2.0) build rather than a net10.0-specific build. [1]
What this means: you can reference it in net10.0, but you shouldn’t expect net10.0-only APIs/behavior unless a later package version ships net10.0-targeted assets. [1]
Most package versions lack explicit net10.0 support; they rely on NuGet framework compatibility rules.
Avalonia 11.2.3, FluentAvaloniaUI 2.2.0, CommunityToolkit.Mvvm 8.4.0, and DynamicData 9.0.4 do not ship net10.0-targeted builds. Instead, they target net8.0, net6.0, or netstandard2.0, and NuGet marks them as compatible with net10.0 by falling back to the closest matching assets (typically net8.0). While this arrangement works, it means these packages are not explicitly tested or optimized for .NET 10. The Microsoft.Extensions packages (10.0.0-preview.1.25080.5) are an exception: Microsoft.Extensions.DependencyInjection includes explicit net10.0 assets, though Microsoft.Extensions.Hosting relies on computed compatibility to net8.0 assets.
🤖 Prompt for AI Agents
In `@Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj` around lines 34 - 46,
Summary: Several PackageReference entries in Flow.Launcher.Avalonia.csproj
(Avalonia, Avalonia.Desktop, Avalonia.Themes.Fluent, Avalonia.Fonts.Inter,
FluentAvaloniaUI, CommunityToolkit.Mvvm, DynamicData) do not ship explicit
net10.0 assets and rely on NuGet compatibility fallbacks. Fix: in
Flow.Launcher.Avalonia.csproj inspect each PackageReference (e.g., Avalonia
11.2.3, FluentAvaloniaUI 2.2.0, CommunityToolkit.Mvvm 8.4.0, DynamicData 9.0.4)
and either upgrade to package versions that explicitly target net10.0 or add a
comment/verification step in the csproj/PR to confirm net10 compatibility; if no
net10 build exists, document acceptance of NuGet fallback or replace with
alternatives that support net10.0; ensure Microsoft.Extensions.* preview
packages are intentionally used (DependencyInjection and Hosting) and update
them to GA net10-compatible versions where available.
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net9.0-windows10.0.19041.0</TargetFramework> | ||
| <TargetFramework>net10.0-windows10.0.19041.0</TargetFramework> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check for SDK pinning configuration
echo "=== Checking global.json for SDK pinning ==="
if [ -f global.json ]; then
cat global.json
else
echo "No global.json found at root"
fi
echo -e "\n=== Checking Directory.Build.props for framework configuration ==="
if [ -f Directory.Build.props ]; then
cat Directory.Build.props
else
echo "No Directory.Build.props found at root"
fi
echo -e "\n=== Searching all .csproj files for TargetFramework ==="
find . -name "*.csproj" -type f | head -20 | while read file; do
echo "File: $file"
rg -n 'TargetFramework' "$file" || true
done
echo -e "\n=== Checking for net10 references across codebase ==="
rg -n 'net10\.0' --type=csproj
echo -e "\n=== Checking CI configuration (common locations) ==="
for ci_file in .github/workflows/*.yml .github/workflows/*.yaml azure-pipelines.yml .gitlab-ci.yml .circleci/config.yml; do
if [ -f "$ci_file" ]; then
echo "Found CI file: $ci_file"
rg -n 'dotnet-version|DOTNET_VERSION|sdk-version' "$ci_file" || echo "No SDK version spec in $ci_file"
fi
done
echo -e "\n=== Checking Flow.Launcher.Test.csproj in detail ==="
cat -n Flow.Launcher.Test/Flow.Launcher.Test.csproj 2>/dev/null || echo "File not found"Repository: Flow-Launcher/Flow.Launcher
Length of output: 8955
Update global.json and CI workflows to .NET 10 before merge; entire codebase is misaligned.
The test project is already using net10.0-windows10.0.19041.0, but the repository's SDK pinning in global.json is still locked to 9.0.* and CI workflows specify dotnet-version: 9.0.x. This mismatch affects the entire codebase—multiple projects have been upgraded to net10.0 (Flow.Launcher.Core, Flow.Launcher.Infrastructure, Flow.Launcher, plugins)—but the build toolchain hasn't been updated. CI will fail to build any net10.0 project with .NET 9. Update global.json to pin .NET 10 and update .github/workflows/dotnet.yml and .github/workflows/default_plugins.yml to use dotnet-version: 10.0.x. The test packages (Moq 4.20.72, nunit 4.4.0, NUnit3TestAdapter 5.1.0, Microsoft.NET.Test.Sdk 17.14.1) are all compatible with .NET 10.
🤖 Prompt for AI Agents
In `@Flow.Launcher.Test/Flow.Launcher.Test.csproj` at line 4, The repository SDK
pinning and CI use .NET 9 while projects target net10.0; update the SDK and CI
to .NET 10 by changing the "version" value in global.json's "sdk" object to a
10.x value (e.g., "10.0.*") and update the GitHub Actions setup step(s)
(actions/setup-dotnet invocation(s)) to use dotnet-version: 10.0.x (including
any matrix entries or multiple workflow files like the two CI workflows) so the
build toolchain matches the net10.0 target across projects.
| <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0-preview.1.25080.5" /> | ||
| <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-preview.1.25080.5" /> |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Microsoft.Extensions.DependencyInjection 10.0 stable release NuGet
💡 Result:
- Microsoft.Extensions.DependencyInjection 10.0 stable (GA) NuGet release: 10.0.0, published Nov 11, 2025. [1]
- Latest 10.0.x stable currently on NuGet: 10.0.2, published Jan 13, 2026. [2]
Install (NuGet Package Manager):
Install-Package Microsoft.Extensions.DependencyInjection -Version 10.0.0(or omit -Version to get the latest stable 10.0.x). [2]
Related package: Microsoft.Extensions.DependencyInjection.Abstractions is also at 10.0.2 (Jan 13, 2026). [3]
🌐 Web query:
Microsoft.Extensions.Hosting 10.0 stable release NuGet
💡 Result:
The stable 10.0 release of Microsoft.Extensions.Hosting is published on NuGet as:
- 10.0.0 (released Nov 11, 2025) [1]
- Latest stable 10.0.x: 10.0.2 (released Jan 13, 2026) [2]
Install (latest stable 10.0.x):
dotnet add package Microsoft.Extensions.Hosting --version 10.0.2Or pin the first stable 10.0:
dotnet add package Microsoft.Extensions.Hosting --version 10.0.0Update to stable versions of Microsoft.Extensions packages.
The Microsoft.Extensions.DependencyInjection and Microsoft.Extensions.Hosting packages are pinned to preview version 10.0.0-preview.1.25080.5. Stable versions 10.0.0 were released Nov 11, 2025, and the latest stable is 10.0.2 (Jan 13, 2026). Upgrade to the stable release to avoid preview-phase breaking changes and bugs.
🤖 Prompt for AI Agents
In `@Flow.Launcher/Flow.Launcher.csproj` around lines 147 - 148, Update the two
PackageReference entries that currently point to the preview version
"10.0.0-preview.1.25080.5" so they reference a stable release (preferably
"10.0.2" or at minimum "10.0.0"); specifically change the PackageReference for
Microsoft.Extensions.DependencyInjection and Microsoft.Extensions.Hosting to the
stable version string to avoid using preview packages.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@check-spelling-bot Report🔴 Please reviewSee the 📂 files view, the 📜action log, or 📝 job summary for details.
See ❌ Event descriptions for more information. Forbidden patterns 🙅 (1)In order to address this, you could change the content to not match the forbidden patterns (comments before forbidden patterns may help explain why they're forbidden), add patterns for acceptable instances, or adjust the forbidden patterns themselves. These forbidden patterns matched content: s.b. preexistingIf the flagged items are 🤯 false positivesIf items relate to a ...
|
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Split repository guidance by domain so contributors can find module-specific rules without duplicating repo-wide conventions.
|
@taooceros could you list us what's left to do in this PR please. |
|
I will get a summary soon. |
Bring the Avalonia host closer to WPF parity by wiring the missing settings dialogs, selectors, and public API flows. Also fix the async JSON save path and settings DI so direct-launch validation can get past the earlier runtime failures. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Track the requested local agent metadata in the repo and add a minimal OpenCode project config for editor/schema support. Keep the old .gemini workspace out of the repository state. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Move the new WPF-inspired palette into shared Avalonia theme resources so the main shell can pull consistent brushes and borders from one place. This keeps later styling polish tied to a single resource layer instead of scattered hex values. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Apply the shared theme brushes to the live result list and the theme settings preview so the settings sample matches the actual search window. This makes the new palette visible in the places users judge the UI most quickly. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Replace the rigid footer button rows on the About page with flexible wrapping so longer labels do not clip or disappear. This keeps the action layout stable across different widths and localized text lengths. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Mark the completed dialogs, settings work, and runtime wiring so the migration plan matches the current Avalonia host state. This keeps the remaining work list focused on the gaps that still matter instead of already-finished items. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
GSD-Unit: Q1
There was a problem hiding this comment.
15 issues found across 291 files
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. cubic prioritises the most important files to review.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="Flow.Launcher.Avalonia/Views/Controls/HotkeyControl.axaml.cs">
<violation number="1" location="Flow.Launcher.Avalonia/Views/Controls/HotkeyControl.axaml.cs:66">
P2: Replace the hardcoded "None" text with a localized string so the hotkey empty state respects current UI language.</violation>
<violation number="2" location="Flow.Launcher.Avalonia/Views/Controls/HotkeyControl.axaml.cs:94">
P1: When saving a new toggle hotkey, re-register the new value; current logic only re-registers when the value is unchanged.</violation>
</file>
<file name="Flow.Launcher.Avalonia/Views/SettingPages/SettingsWindow.axaml">
<violation number="1" location="Flow.Launcher.Avalonia/Views/SettingPages/SettingsWindow.axaml:9">
P3: Localize the window title instead of hardcoding English text.</violation>
</file>
<file name="Flow.Launcher.Avalonia/Views/Dialogs/ProgressBoxWindow.axaml">
<violation number="1" location="Flow.Launcher.Avalonia/Views/Dialogs/ProgressBoxWindow.axaml:17">
P3: Avoid hardcoded `FontSize` here and rely on default/shared text styles for consistent theming and accessibility.
(Based on your team's feedback about relying on default UI styles and avoiding hardcoded font sizes.) [FEEDBACK_USED]</violation>
</file>
<file name=".gitignore">
<violation number="1" location=".gitignore:308">
P2: The `.history` and `codemap.md` ignore entries were accidentally concatenated into one pattern, so `.history` is no longer properly ignored and the codemap rule is mis-scoped.</violation>
</file>
<file name=".github/workflows/dotnet.yml">
<violation number="1" location=".github/workflows/dotnet.yml:95">
P2: Use the same `actions/upload-artifact` major version as the rest of the workflow (`@v7`) to avoid inconsistent behavior across artifact upload steps.</violation>
</file>
<file name="Flow.Launcher.Avalonia/Views/SettingPages/CustomQueryHotkeyWindow.axaml">
<violation number="1" location="Flow.Launcher.Avalonia/Views/SettingPages/CustomQueryHotkeyWindow.axaml:25">
P3: Localize this hardcoded help text instead of embedding English directly in the view.</violation>
</file>
<file name="Flow.Launcher.Avalonia/Storage/History.cs">
<violation number="1" location="Flow.Launcher.Avalonia/Storage/History.cs:26">
P2: Check for an existing history entry before evicting; otherwise duplicate executions at capacity delete an unrelated item.</violation>
</file>
<file name="Flow.Launcher.Avalonia/Views/SettingPages/CustomShortcutWindow.axaml">
<violation number="1" location="Flow.Launcher.Avalonia/Views/SettingPages/CustomShortcutWindow.axaml:24">
P2: Localize the new description text instead of hardcoding English so it participates in the app’s i18n flow.</violation>
</file>
<file name="Flow.Launcher.Avalonia/Views/Dialogs/NotificationWindow.axaml.cs">
<violation number="1" location="Flow.Launcher.Avalonia/Views/Dialogs/NotificationWindow.axaml.cs:104">
P2: Notification stacking can overlap when a non-last toast closes, because positioning uses only a global active count instead of actual occupied slots.</violation>
</file>
<file name="Flow.Launcher.Avalonia/Views/Dialogs/ProgressBoxWindow.axaml.cs">
<violation number="1" location="Flow.Launcher.Avalonia/Views/Dialogs/ProgressBoxWindow.axaml.cs:90">
P2: Hidden progress windows are not cleaned up because close is gated by `IsVisible`; if the user clicks Hide, `finally` skips `Close()` and the window instance is left alive.</violation>
</file>
<file name="Flow.Launcher.Core/Flow.Launcher.Core.csproj">
<violation number="1" location="Flow.Launcher.Core/Flow.Launcher.Core.csproj:4">
P1: This TFM upgrade to `net10.0-windows` is incompatible with the repository’s `global.json` SDK pin (`9.0.*`), which can break local/CI builds with NETSDK1045 unless the SDK pin is updated in the same PR.</violation>
</file>
<file name="Flow.Launcher.Avalonia/Resource/LocalizeExtension.cs">
<violation number="1" location="Flow.Launcher.Avalonia/Resource/LocalizeExtension.cs:47">
P2: Return a dynamic resource binding instead of a literal translation string so localized text updates after runtime language changes.</violation>
</file>
<file name="Flow.Launcher.Avalonia/Views/Dialogs/ReportWindow.axaml.cs">
<violation number="1" location="Flow.Launcher.Avalonia/Views/Dialogs/ReportWindow.axaml.cs:49">
P2: Validate `IssueUrl` before `Process.Start`; malformed URLs can throw and make the report dialog fail when users click Open Issue.</violation>
<violation number="2" location="Flow.Launcher.Avalonia/Views/Dialogs/ReportWindow.axaml.cs:70">
P2: Guard `pluginException.Metadata.Website` before calling `StartsWith`; a null website can throw and break the error-report flow.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| { | ||
| Hotkey = dialog.ResultValue; | ||
|
|
||
| if (shouldUnregisterToggle && string.Equals(dialog.ResultValue, originalHotkey, System.StringComparison.Ordinal)) |
There was a problem hiding this comment.
P1: When saving a new toggle hotkey, re-register the new value; current logic only re-registers when the value is unchanged.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Flow.Launcher.Avalonia/Views/Controls/HotkeyControl.axaml.cs, line 94:
<comment>When saving a new toggle hotkey, re-register the new value; current logic only re-registers when the value is unchanged.</comment>
<file context>
@@ -0,0 +1,112 @@
+ {
+ Hotkey = dialog.ResultValue;
+
+ if (shouldUnregisterToggle && string.Equals(dialog.ResultValue, originalHotkey, System.StringComparison.Ordinal))
+ {
+ HotKeyMapper.SetToggleHotkey(originalHotkey);
</file context>
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net9.0-windows</TargetFramework> | ||
| <TargetFramework>net10.0-windows</TargetFramework> |
There was a problem hiding this comment.
P1: This TFM upgrade to net10.0-windows is incompatible with the repository’s global.json SDK pin (9.0.*), which can break local/CI builds with NETSDK1045 unless the SDK pin is updated in the same PR.
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>This TFM upgrade to `net10.0-windows` is incompatible with the repository’s `global.json` SDK pin (`9.0.*`), which can break local/CI builds with NETSDK1045 unless the SDK pin is updated in the same PR.</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>
| KeysToDisplay.Clear(); | ||
| if (string.IsNullOrEmpty(Hotkey)) | ||
| { | ||
| KeysToDisplay.Add("None"); |
There was a problem hiding this comment.
P2: Replace the hardcoded "None" text with a localized string so the hotkey empty state respects current UI language.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Flow.Launcher.Avalonia/Views/Controls/HotkeyControl.axaml.cs, line 66:
<comment>Replace the hardcoded "None" text with a localized string so the hotkey empty state respects current UI language.</comment>
<file context>
@@ -0,0 +1,112 @@
+ KeysToDisplay.Clear();
+ if (string.IsNullOrEmpty(Hotkey))
+ {
+ KeysToDisplay.Add("None");
+ return;
+ }
</file context>
| .vscode | ||
| .history No newline at end of file | ||
| .history**/codemap.md | ||
| .slim/ |
There was a problem hiding this comment.
P2: The .history and codemap.md ignore entries were accidentally concatenated into one pattern, so .history is no longer properly ignored and the codemap rule is mis-scoped.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .gitignore, line 308:
<comment>The `.history` and `codemap.md` ignore entries were accidentally concatenated into one pattern, so `.history` is no longer properly ignored and the codemap rule is mis-scoped.</comment>
<file context>
@@ -304,4 +304,34 @@ Output-Performance.txt
-.history
\ No newline at end of file
+.history**/codemap.md
+.slim/
+
+# Local planning artifacts
</file context>
| Output\Packages\RELEASES | ||
| compression-level: 0 | ||
| - name: Upload Avalonia Build | ||
| uses: actions/upload-artifact@v5 |
There was a problem hiding this comment.
P2: Use the same actions/upload-artifact major version as the rest of the workflow (@v7) to avoid inconsistent behavior across artifact upload steps.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/dotnet.yml, line 95:
<comment>Use the same `actions/upload-artifact` major version as the rest of the workflow (`@v7`) to avoid inconsistent behavior across artifact upload steps.</comment>
<file context>
@@ -89,3 +91,10 @@ jobs:
Output\Packages\RELEASES
compression-level: 0
+ - name: Upload Avalonia Build
+ uses: actions/upload-artifact@v5
+ with:
+ name: Flow Launcher Avalonia
</file context>
| uses: actions/upload-artifact@v5 | |
| uses: actions/upload-artifact@v7 |
|
|
||
| private void OnOpenIssueClick(object? sender, RoutedEventArgs e) | ||
| { | ||
| Process.Start(new ProcessStartInfo(IssueUrl) { UseShellExecute = true }); |
There was a problem hiding this comment.
P2: Validate IssueUrl before Process.Start; malformed URLs can throw and make the report dialog fail when users click Open Issue.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Flow.Launcher.Avalonia/Views/Dialogs/ReportWindow.axaml.cs, line 49:
<comment>Validate `IssueUrl` before `Process.Start`; malformed URLs can throw and make the report dialog fail when users click Open Issue.</comment>
<file context>
@@ -0,0 +1,119 @@
+
+ private void OnOpenIssueClick(object? sender, RoutedEventArgs e)
+ {
+ Process.Start(new ProcessStartInfo(IssueUrl) { UseShellExecute = true });
+ }
+
</file context>
| } | ||
|
|
||
| var website = pluginException.Metadata.Website; | ||
| if (!website.StartsWith("https://github.com", StringComparison.OrdinalIgnoreCase)) |
There was a problem hiding this comment.
P2: Guard pluginException.Metadata.Website before calling StartsWith; a null website can throw and break the error-report flow.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Flow.Launcher.Avalonia/Views/Dialogs/ReportWindow.axaml.cs, line 70:
<comment>Guard `pluginException.Metadata.Website` before calling `StartsWith`; a null website can throw and break the error-report flow.</comment>
<file context>
@@ -0,0 +1,119 @@
+ }
+
+ var website = pluginException.Metadata.Website;
+ if (!website.StartsWith("https://github.com", StringComparison.OrdinalIgnoreCase))
+ {
+ return website;
</file context>
| xmlns:i18n="using:Flow.Launcher.Avalonia.Resource" | ||
| mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" | ||
| x:Class="Flow.Launcher.Avalonia.Views.SettingPages.SettingsWindow" | ||
| Title="Flow Launcher Settings" |
There was a problem hiding this comment.
P3: Localize the window title instead of hardcoding English text.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Flow.Launcher.Avalonia/Views/SettingPages/SettingsWindow.axaml, line 9:
<comment>Localize the window title instead of hardcoding English text.</comment>
<file context>
@@ -0,0 +1,31 @@
+ xmlns:i18n="using:Flow.Launcher.Avalonia.Resource"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:Class="Flow.Launcher.Avalonia.Views.SettingPages.SettingsWindow"
+ Title="Flow Launcher Settings"
+ Width="900" Height="650"
+ WindowStartupLocation="CenterScreen">
</file context>
| WindowStartupLocation="CenterScreen"> | ||
|
|
||
| <Grid RowDefinitions="Auto,Auto,*,Auto" Margin="20"> | ||
| <TextBlock FontSize="20" |
There was a problem hiding this comment.
P3: Avoid hardcoded FontSize here and rely on default/shared text styles for consistent theming and accessibility.
(Based on your team's feedback about relying on default UI styles and avoiding hardcoded font sizes.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Flow.Launcher.Avalonia/Views/Dialogs/ProgressBoxWindow.axaml, line 17:
<comment>Avoid hardcoded `FontSize` here and rely on default/shared text styles for consistent theming and accessibility.
(Based on your team's feedback about relying on default UI styles and avoiding hardcoded font sizes.) </comment>
<file context>
@@ -0,0 +1,41 @@
+ WindowStartupLocation="CenterScreen">
+
+ <Grid RowDefinitions="Auto,Auto,*,Auto" Margin="20">
+ <TextBlock FontSize="20"
+ FontWeight="SemiBold"
+ Text="{Binding TitleText}" />
</file context>
| FontSize="20" | ||
| FontWeight="SemiBold" /> | ||
|
|
||
| <TextBlock Text="Choose a hotkey and the query text to insert when it is pressed." |
There was a problem hiding this comment.
P3: Localize this hardcoded help text instead of embedding English directly in the view.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Flow.Launcher.Avalonia/Views/SettingPages/CustomQueryHotkeyWindow.axaml, line 25:
<comment>Localize this hardcoded help text instead of embedding English directly in the view.</comment>
<file context>
@@ -0,0 +1,67 @@
+ FontSize="20"
+ FontWeight="SemiBold" />
+
+ <TextBlock Text="Choose a hotkey and the query text to insert when it is pressed."
+ TextWrapping="Wrap" />
+
</file context>
There was a problem hiding this comment.
Actionable comments posted: 19
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
♻️ Duplicate comments (2)
Flow.Launcher.Avalonia/App.axaml.cs (1)
142-150:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAttach storage to the fallback
Settingsinstance too.The failure path still creates
_settingswithoutSetStorage, so later saves run against an unconfigured settings object.Suggested fix
catch (Exception e) { Log.Exception(ClassName, "Settings load failed", e); - _settings = new Settings + var storage = new FlowLauncherJsonStorage<Settings>(); + _settings = new Settings { WindowSize = 580, WindowHeightSize = 42, QueryBoxFontSize = 24, ItemHeightSize = 50, ResultItemFontSize = 14, ResultSubItemFontSize = 12, MaxResultsToShow = 6 }; + _settings.SetStorage(storage); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/App.axaml.cs` around lines 142 - 150, The fallback Settings instance created in the catch block is missing its storage attachment, so call SetStorage on the newly created _settings (the same storage used for the normal settings path) before assigning/using it; update the catch to create the Settings object and then invoke _settings.SetStorage(...) with the existing settings storage instance (the same storage reference used elsewhere when loading/saving settings), ensuring the fallback has storage configured.Flow.Launcher.Avalonia/MainWindow.axaml (1)
68-75:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winMake the search icon overlay non-hit-testable.
This overlay sits on top of the query box, so clicks on the icon area can stop focusing the
TextBox.Suggested fix
- <Canvas Name="SearchIconCanvas" - Classes="searchIconPosition" - HorizontalAlignment="Right"> + <Canvas Name="SearchIconCanvas" + Classes="searchIconPosition" + HorizontalAlignment="Right" + IsHitTestVisible="False">In Avalonia UI, does an overlaid Canvas receive pointer events by default, and does setting `IsHitTestVisible="False"` allow clicks to pass through to the TextBox underneath?🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/MainWindow.axaml` around lines 68 - 75, The overlaid SearchIconCanvas is intercepting pointer events and preventing the underlying TextBox from receiving focus; make the overlay non-hit-testable by setting IsHitTestVisible="False" (on SearchIconCanvas or on the inner SearchIcon Path) so pointer events pass through to the TextBox beneath, ensuring clicks still focus the input while preserving the visual overlay.
🟡 Minor comments (22)
Flow.Launcher.Avalonia/Views/Dialogs/NotificationWindow.axaml.cs-87-94 (1)
87-94:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winHandle icon-load failures explicitly to avoid silent task faults.
LoadIconAsyncis fire-and-forget and currently has no local exception handling. A bad/corrupt image path can fault the task without structured logging.Suggested fix
private async System.Threading.Tasks.Task LoadIconAsync(string iconPath) { - var resolvedPath = File.Exists(iconPath) - ? iconPath - : Path.Combine(Constant.ProgramDirectory, "Images", "app.png"); - - NotificationIcon = await ImageLoader.LoadAsync(resolvedPath); + try + { + var resolvedPath = File.Exists(iconPath) + ? iconPath + : Path.Combine(Constant.ProgramDirectory, "Images", "app.png"); + + NotificationIcon = await ImageLoader.LoadAsync(resolvedPath); + } + catch (Exception ex) + { + Log.Exception(nameof(NotificationWindow), "Failed to load notification icon", ex); + NotificationIcon = null; + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/Views/Dialogs/NotificationWindow.axaml.cs` around lines 87 - 94, LoadIconAsync currently awaits ImageLoader.LoadAsync but has no exception handling, causing silent task faults; wrap the ImageLoader.LoadAsync(resolvedPath) call in a try/catch inside LoadIconAsync (catch Exception) and on failure set a safe fallback for NotificationIcon and log the exception (include resolvedPath and the exception) using the existing logging mechanism so failures are visible and the UI remains stable.AGENTS.md-5-16 (1)
5-16:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRoot AGENTS table needs MD058 spacing fix.
Add blank lines around the
WHERE TO LOOKtable block.Suggested patch
## WHERE TO LOOK + | Task | Location | Notes | |------|----------|-------| | WPF app behavior | `Flow.Launcher/` | Main host, windows, settings shell, WPF-only UI rules | | Avalonia app behavior | `Flow.Launcher.Avalonia/` | Avalonia host, WPF compatibility shim, migrated settings pages | | Plugin lifecycle | `Flow.Launcher.Core/` | `PluginManager`, plugin loading, updates, manifest integration | | Shared infra | `Flow.Launcher.Infrastructure/` | settings, storage, hotkeys, logging, fuzzy matching | | Plugin SDK contract | `Flow.Launcher.Plugin/` | interfaces/models consumed by built-in and external plugins | | Built-in plugins | `Plugins/` | 12 plugin projects, shared `Main.cs` + `plugin.json` structure | | Test project | `Flow.Launcher.Test/` | NUnit 4 tests; some Explorer tests need Windows Search | | CI / release process | `.github/` | workflow automation, release PRs, plugin publishing, issue templates | + ## CHILD AGENTS🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@AGENTS.md` around lines 5 - 16, The WHERE TO LOOK table in AGENTS.md violates MD058 (missing blank lines around a block table); edit AGENTS.md and add a blank line immediately before the "## WHERE TO LOOK" heading block and another blank line immediately after the table (after the final | CI / release process | `.github/` ... | row) so the table is separated from surrounding text, ensuring proper Markdown spacing for the "WHERE TO LOOK" table block.Flow.Launcher.Infrastructure/AGENTS.md-5-14 (1)
5-14:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd blank lines around the table (MD058).
markdownlintexpects a blank line before and after this table block.Suggested patch
## WHERE TO LOOK + | Task | Location | Notes | |------|----------|-------| | User settings | `UserSettings/` | `Settings`, `DataLocation`, app/user data roots | | Storage | `Storage/` | JSON/binary persistence, backup/atomic write behavior | | Search matching | `StringMatcher.cs`, related models | fuzzy/acronym/pinyin behavior | | Logging | `Logger/` | NLog wrapper and diagnostics | | Hotkeys | `Hotkey/` | low-level keyboard integration | | Windows helpers | helper/system wrappers | Win32 and shell integration | + ## LOCAL RULES🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Infrastructure/AGENTS.md` around lines 5 - 14, The Markdown table under the "## WHERE TO LOOK" heading needs blank lines before and after it to satisfy markdownlint rule MD058; edit the AGENTS.md content so there is an empty line between the "## WHERE TO LOOK" heading and the table start, and another empty line after the table end (i.e., ensure a blank line before the first | of the table and one after the final | row) to resolve the lint warning..github/AGENTS.md-5-15 (1)
5-15:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winLint fix needed: blank lines around table (MD058).
Please add a blank line before and after this table.
Suggested patch
## WHERE TO LOOK + | Task | Location | Notes | |------|----------|-------| | CI build and artifacts | `.github/workflows/dotnet.yml` | build/test pipeline, WSearch service, artifact publishing | | Release deploy | `.github/workflows/release_deploy.yml` | website + Chocolatey dispatches, secret-driven | | Release PR body automation | `.github/workflows/release_pr.yml`, `.github/update_release_pr.py` | expects one open release PR and milestone grouping | | Built-in plugin publishing | `.github/workflows/default_plugins.yml` | publishes plugin repos, updates plugin metadata | | Issue/PR automation | `.github/workflows/pr_*.yml`, `stale.yml`, `spelling.yml` | author assignment, milestone, stale, spelling | | Dependency policy | `.github/dependabot.yml` | daily cadence, PR caps, ignored packages | | Intake rules | `.github/ISSUE_TEMPLATE/` | bug report required fields, feature request, code review template | + ## LOCAL RULES🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/AGENTS.md around lines 5 - 15, Add a blank line before and after the Markdown table under the "WHERE TO LOOK" heading so the table is separated from surrounding text (fixes MD058); locate the "WHERE TO LOOK" header and the following table block (the lines starting with "## WHERE TO LOOK" and the table rows like "| Task | Location | Notes |") and insert one empty line immediately above the "## WHERE TO LOOK" table block if not present and one empty line immediately after the table end.Plugins/AGENTS.md-11-18 (1)
11-18:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winResolve markdownlint MD058 for the
WHERE TO LOOKtable.Insert blank lines around this table block.
Suggested patch
## WHERE TO LOOK + | Task | Location | Notes | |------|----------|-------| | Entry/query logic | each plugin `Main.cs` | sync vs async behavior lives here | | Manifest | each plugin `plugin.json` | metadata, action keyword(s), icon, execute file | | Settings model | `Settings.cs` or equivalent | usually loaded through API storage helpers | | Plugin UI/settings | plugin views/viewmodels if present | some have WPF + Avalonia settings paths | + ## LOCAL RULES🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Plugins/AGENTS.md` around lines 11 - 18, The MD058 lint violation is caused by the table not being separated by blank lines; update the Plugins/AGENTS.md by adding a blank line after the "## WHERE TO LOOK" heading and another blank line after the table block (the lines starting with "| Task | Location | Notes |" through the table row with "Plugin UI/settings | plugin views/viewmodels if present | ...") so the table is isolated with an empty line above and below to satisfy markdownlint.Flow.Launcher.Core/AGENTS.md-5-14 (1)
5-14:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix markdownlint MD058 around the table.
Add blank lines before/after the
WHERE TO LOOKtable to satisfy markdownlint.Suggested patch
## WHERE TO LOOK + | Task | Location | Notes | |------|----------|-------| | Plugin lifecycle | `Plugin/PluginManager.cs` | discovery, load, init, query fanout | | Plugin manifest parsing | `Plugin/PluginConfig.cs` | scans `plugin.json`, duplicate/version filtering | | Loader implementation | `Plugin/PluginsLoader.cs` | .NET + external plugin loading | | Community plugins | `ExternalPlugins/` | manifest/store integration | | Updates | updater-related files | Squirrel + release logic | | Resources/i18n | `Resource/`, related services | host/plugin resource merging | + ## LOCAL RULES🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Core/AGENTS.md` around lines 5 - 14, Add a blank line immediately before the "## WHERE TO LOOK" heading/table and another blank line immediately after the table to satisfy markdownlint MD058; edit the AGENTS.md section containing the table that lists Plugin/PluginManager.cs, Plugin/PluginConfig.cs, Plugin/PluginsLoader.cs, ExternalPlugins/, updater-related files, and Resource/ so the table is separated by empty lines above and below.Flow.Launcher.Plugin/AGENTS.md-5-12 (1)
5-12:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winTable spacing violates MD058.
Please add blank lines before and after this table.
Suggested patch
## WHERE TO LOOK + | Task | Location | Notes | |------|----------|-------| | Core plugin interfaces | `Interfaces/` | `IPlugin`, `IAsyncPlugin`, capability interfaces | | Public models | root models, `SharedModels/` | `Result`, `Query`, metadata, settings-facing types | | Shared commands/helpers | `SharedCommands/` | reusable SDK-side pieces | | Docs | `README.md` | package/plugin authoring baseline | + ## LOCAL RULES🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Plugin/AGENTS.md` around lines 5 - 12, The Markdown table under the "## WHERE TO LOOK" heading violates MD058 by lacking blank lines around the table; fix it by inserting a single blank line immediately after the "## WHERE TO LOOK" heading and another blank line immediately after the table block (the pipe-delimited rows shown in the diff) so there is one empty line before and after the table.Flow.Launcher/AGENTS.md-5-13 (1)
5-13:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winSurround the “WHERE TO LOOK” table with blank lines.
Line 6 currently triggers markdownlint
MD058. Add a blank line before and after the table block.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher/AGENTS.md` around lines 5 - 13, The "WHERE TO LOOK" markdown table in AGENTS.md triggers MD058 because it is not surrounded by blank lines; update the file to insert a blank line immediately before the "## WHERE TO LOOK" header/table block and another blank line immediately after the table so the table is separated from surrounding content and satisfies markdownlint; target the table block shown (the header "## WHERE TO LOOK" and its pipe-delimited rows) when making the change.Flow.Launcher.Avalonia/AGENTS.md-5-13 (1)
5-13:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd blank lines around the table block.
Line 6 starts a table immediately after a heading, which triggers markdownlint (
MD058). Add one blank line before and after this table for clean rendering/lint pass.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/AGENTS.md` around lines 5 - 13, Add a blank line before and a blank line after the markdown table that starts with the heading "## WHERE TO LOOK" and the pipe-delimited table (the | Task | Location | Notes | ... block) so the table is separated from surrounding content and satisfies MD058; edit the AGENTS.md section containing that table to insert one empty line above the table and one empty line below it.AVALONIA_MIGRATION_CHECKLIST.md-347-384 (1)
347-384:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winInsert blank lines before table blocks in Core Features subsections.
At Line 348/362/372/381, tables start immediately after subsection titles and trigger
MD058. Add a blank line before each table.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@AVALONIA_MIGRATION_CHECKLIST.md` around lines 347 - 384, The markdown linter MD058 is triggered because tables in the "Search & Results", "Window Management", and "System Integration" subsections start immediately after their subsection titles; add a single blank line before each table to separate the heading and the table (i.e., insert a blank line before the table that begins under the "Search & Results" header, the table under "Window Management", and the table under "System Integration") so each subsection title is followed by a blank line then the table.AVALONIA_MIGRATION_CHECKLIST.md-458-528 (1)
458-528:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd language identifiers to fenced code blocks.
The blocks at Line 458 and Line 481 have no language (
MD040). Usetext(orbashwhere appropriate).Suggested patch
-``` +```text Flow.Launcher/ ...-
+text
Flow.Launcher.Avalonia/
...🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@AVALONIA_MIGRATION_CHECKLIST.md` around lines 458 - 528, Update the two fenced code blocks that list the project trees so they include a language identifier; specifically, change the triple-backtick fence before the block starting with "Flow.Launcher/" and the one before the block starting with "Flow.Launcher.Avalonia/" to use ```text (or ```bash for shell-style listing) instead of bare ``` so the MD040 lint warning is resolved.AVALONIA_MIGRATION_CHECKLIST.md-56-166 (1)
56-166:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix single-cell “section header” rows inside 3-column tables.
Rows like Line 58 (
| **Startup Section** |) break table column count (MD056). Keep 3 cells for those separator rows.Suggested patch pattern
-| **Startup Section** | +| **Startup Section** | | | ... -| **Behavior Section** | +| **Behavior Section** | | |🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@AVALONIA_MIGRATION_CHECKLIST.md` around lines 56 - 166, The table contains single-cell section header rows (e.g., "**Startup Section**", "**Behavior Section**", "**Position Section**", etc.) that break the 3-column table structure and trigger MD056; update each of those separator rows so they contain three pipe-separated cells (for example add two empty cells or placeholder text) to maintain the "| col1 | col2 | col3 |" format throughout the file (search for rows starting with "| **" and ensure they become three-cell rows).Flow.Launcher.Avalonia/Views/SettingPages/CustomQueryHotkeyWindow.axaml.cs-81-118 (1)
81-118:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winLocalize the validation messages too.
This dialog already routes button text through
Translate(...), but the error title/messages are still hard-coded English. That leaves part of the settings flow untranslated on non-English installs.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/Views/SettingPages/CustomQueryHotkeyWindow.axaml.cs` around lines 81 - 118, The validation dialog strings in OnDoneClick are hard-coded; update the calls that pass "Custom Query Hotkey", "Both the hotkey and query text are required.", and "That hotkey is already assigned to another custom query." to use the app localization method (Translate) before calling ShowMessageAsync, and modify ShowMessageAsync to accept already-localized title/content or call Translate for any non-localized inputs; reference the OnDoneClick method and ShowMessageAsync to locate and change the message parameters so all dialog title and content use Translate(...) like the CloseButtonText does.Flow.Launcher.Avalonia/Views/Dialogs/PluginUpdateWindow.axaml-40-53 (1)
40-53:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winMove the remaining labels through i18n resources.
Restart after updating pluginsandCancelare still hard-coded here, so this dialog won't fully localize with the rest of the Avalonia host.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/Views/Dialogs/PluginUpdateWindow.axaml` around lines 40 - 53, Replace the two hard-coded UI strings with bindings to the i18n resource keys so the dialog localizes; specifically change the CheckBox Content ("Restart after updating plugins") to use the localization resource (e.g., a resource key like RestartAfterUpdatingPlugins) and change the Cancel Button Content ("Cancel") to use the Cancel resource key, keeping the existing bindings/properties (Restart, UpdateButtonText) and event handlers (OnCancelClick, OnUpdateClick) intact so only the Content attributes are switched to resource-based bindings or StaticResource/Loc markup extensions that the project uses for localization.Flow.Launcher.Avalonia/Views/SettingPages/CustomShortcutWindow.axaml.cs-80-100 (1)
80-100:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winTrim the shortcut key before validating and saving it.
Right now
"foo"and" foo "are treated as different values, so_doesShortcutExist(...)can be bypassed and the saved shortcut key may not expand the way the user expects.Suggested change
private async void OnDoneClick(object? sender, RoutedEventArgs e) { - if (string.IsNullOrWhiteSpace(ShortcutKey) || string.IsNullOrWhiteSpace(ShortcutValue)) + var normalizedShortcutKey = ShortcutKey.Trim(); + + if (string.IsNullOrWhiteSpace(normalizedShortcutKey) || string.IsNullOrWhiteSpace(ShortcutValue)) { await ShowMessageAsync("Custom Shortcut", "Both the shortcut and expansion text are required."); return; } - if (((_update && _originalShortcutKey != ShortcutKey) || !_update) && _doesShortcutExist(ShortcutKey)) + if (((_update && _originalShortcutKey != normalizedShortcutKey) || !_update) && + _doesShortcutExist(normalizedShortcutKey)) { await ShowMessageAsync("Custom Shortcut", "That shortcut key already exists."); return; } - if (_update && _originalShortcutKey == ShortcutKey && _originalShortcutValue == ShortcutValue) + if (_update && _originalShortcutKey == normalizedShortcutKey && _originalShortcutValue == ShortcutValue) { Close(false); return; } + ShortcutKey = normalizedShortcutKey; Close(true); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/Views/SettingPages/CustomShortcutWindow.axaml.cs` around lines 80 - 100, In OnDoneClick, normalize the ShortcutKey by trimming whitespace before any validation, existence checks, or comparison with _originalShortcutKey/_originalShortcutValue so leading/trailing spaces don't bypass _doesShortcutExist or create mismatched saved keys; i.e., compute a trimmedKey = ShortcutKey?.Trim(), use trimmedKey for the Null/WhiteSpace check, pass trimmedKey into _doesShortcutExist(trimmedKey), compare trimmedKey to _originalShortcutKey when determining no-op updates, and ensure the value used when saving/closing is the trimmed key.Flow.Launcher.Avalonia/ViewModel/MainViewModel.cs-290-295 (1)
290-295:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
LoadHistory()runs the same history query twice.After
QueryText = string.Empty;,OnQueryTextChanged()already invokesQueryAsync(). The extra_ = QueryAsync();immediately below repeats the history load every time this view opens.Suggested change
if (ActiveView == ActiveView.Results) { _queryTextBeforeHistory = QueryText; ActiveView = ActiveView.History; QueryText = string.Empty; - _ = QueryAsync(); if (HistoryView.Results.Count > 0) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/ViewModel/MainViewModel.cs` around lines 290 - 295, The history load is triggered twice because setting QueryText = string.Empty fires OnQueryTextChanged which already calls QueryAsync(), so remove the redundant explicit call to QueryAsync(); in the MainViewModel method that switches views (the block referencing ActiveView, _queryTextBeforeHistory, QueryText and calling QueryAsync()), delete the line "_ = QueryAsync();" so QueryAsync is only invoked via OnQueryTextChanged when QueryText is set to empty.Flow.Launcher.Avalonia/Views/SettingPages/GeneralSettingsPage.axaml-80-100 (1)
80-100:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winMove the new search-window labels into localization resources.
Custom screen,Custom position, andSet X and Y when alignment is Customare user-facing English strings in an otherwise localized page. These should usei18n:Localizekeys like the rest of the settings UI.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/Views/SettingPages/GeneralSettingsPage.axaml` around lines 80 - 100, Replace the hardcoded English strings in the SettingsExpanderItem elements with i18n:Localize resource keys: change Content="Custom screen" to Content="{i18n:Localize CustomScreen}" (or your chosen key), Content="Custom position" to Content="{i18n:Localize CustomPosition}", and Description="Set X and Y when alignment is Custom" to Description="{i18n:Localize CustomPositionDescription}"; add corresponding keys to the localization resource file so SearchWindowAlign and other localized entries remain consistent and bind to UI:SettingsExpanderItem and its Footer ComboBox elements unchanged.Flow.Launcher.Avalonia/Views/SettingPages/AboutSettingsPage.axaml-44-46 (1)
44-46:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winLocalize the new section descriptions.
These descriptions are hardcoded English while the rest of the page is localized, so this page will regress into a mixed-language UI for non-English users. Please move them into the i18n resources as well.
Also applies to: 58-60, 72-74
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/Views/SettingPages/AboutSettingsPage.axaml` around lines 44 - 46, The Description attributes in AboutSettingsPage.axaml's SettingsExpander elements (e.g., the one with Header="{i18n:Localize website}" and IconSource="World") are hardcoded English; replace those literal Description strings with i18n resource lookups (use i18n:Localize or the existing localization markup) and add corresponding keys to the i18n resource files; do the same for the other two expanders referenced (the elements around lines 58-60 and 72-74) so all Header and Description text is pulled from localization keys rather than hardcoded English.Flow.Launcher.Avalonia/Views/Dialogs/WelcomeWindow.axaml-50-53 (1)
50-53:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winSwap these Page 3 labels or hotkeys.
HotkeyUpDownDescis currently shown next to left/right arrows, andHotkeyLeftRightDescis shown next to up/down arrows. The onboarding text is backwards.Suggested fix
- <TextBlock Grid.Row="0" Margin="0,0,0,10" Text="{i18n:Localize HotkeyUpDownDesc}" /> + <TextBlock Grid.Row="0" Margin="0,0,0,10" Text="{i18n:Localize HotkeyLeftRightDesc}" /> <controls:HotkeyDisplay Grid.Row="0" Grid.Column="1" Keys="←+→" /> - <TextBlock Grid.Row="1" Margin="0,0,0,10" Text="{i18n:Localize HotkeyLeftRightDesc}" /> + <TextBlock Grid.Row="1" Margin="0,0,0,10" Text="{i18n:Localize HotkeyUpDownDesc}" /> <controls:HotkeyDisplay Grid.Row="1" Grid.Column="1" Keys="↑+↓" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/Views/Dialogs/WelcomeWindow.axaml` around lines 50 - 53, The two Page 3 items are swapped: the TextBlock using i18n:Localize HotkeyUpDownDesc is paired with Keys="←+→" and HotkeyLeftRightDesc is paired with Keys="↑+↓"; swap them so HotkeyUpDownDesc is paired with Keys="↑+↓" and HotkeyLeftRightDesc is paired with Keys="←+→". Locate the two controls: the TextBlock elements referencing HotkeyUpDownDesc and HotkeyLeftRightDesc and the adjacent controls:HotkeyDisplay elements with Keys="←+→" and Keys="↑+↓", and swap the Keys values or swap the TextBlock positions so each description matches the corresponding HotkeyDisplay.Flow.Launcher.Avalonia/Views/SettingPages/HotkeySettingsPage.axaml-102-114 (1)
102-114:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winDifferentiate the fixed
Shift+Enterrow from the editable context-menu hotkey.Both rows render the same label, so users can’t tell which one they can change and which one is hard-coded.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/Views/SettingPages/HotkeySettingsPage.axaml` around lines 102 - 114, The two SettingsExpanderItem rows use the same Content/localization key so users can’t tell the editable hotkey (HotkeyControl bound to OpenContextMenuHotkey) from the fixed one (HotkeyDisplay Keys="Shift+Enter"); update the second SettingsExpanderItem to use a different label (e.g., a new localization key like OpenContextMenuHotkey_Fixed or append "(fixed)" to the content) or a distinct visual cue so the fixed Shift+Enter row is clearly labeled as non-editable while leaving the HotkeyControl row unchanged.Flow.Launcher.Avalonia/Views/SettingPages/ThemeSettingsPage.axaml-28-40 (1)
28-40:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winLocalize the remaining hard-coded labels on this page.
This still exposes English-only UI (
Hello there,Theme resources,Sizing and fonts,Theme Builder,Warning, etc.), so the page bypasses the app’s i18n path in visible settings content.Also applies to: 88-93, 116-116, 201-205, 296-299
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/Views/SettingPages/ThemeSettingsPage.axaml` around lines 28 - 40, Replace all hard-coded TextBlock/Text content on ThemeSettingsPage.axaml (e.g., the Text attributes "Hello there", "Sample result title", "Sample result subtitle" and visible headings like "Theme resources", "Sizing and fonts", "Theme Builder", "Warning") with localized resource lookups or bindings to the app's i18n keys (use the same pattern used elsewhere in the project such as StaticResource/x:Static or a LocalizationManager binding). Create or use descriptive resource keys (e.g., ThemeSettings_Hello, ThemeSettings_ResultTitle, ThemeSettings_ResultSubtitle, ThemeSettings_ThemeResources, ThemeSettings_SizingAndFonts, ThemeSettings_ThemeBuilder, ThemeSettings_Warning) and replace the literal strings with those resource references so the page follows the app’s localization path.Flow.Launcher.Avalonia/ViewModel/SettingPages/GeneralSettingsViewModel.cs-75-86 (1)
75-86:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winHistory-style labels won't refresh after a language switch.
UpdateLabels()mutatesHistoryStyleOption.Display, butHistoryStyleOptiondoes not raise property changes and the view model never re-raisesHistoryStyles/SelectedHistoryStyle. This dropdown will keep the old language until the page is recreated.Also applies to: 407-423, 809-829
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/ViewModel/SettingPages/GeneralSettingsViewModel.cs` around lines 75 - 86, UpdateLabels currently mutates HistoryStyleOption.Display but the UI doesn't refresh because HistoryStyleOption doesn't raise PropertyChanged and the VM doesn't re-raise HistoryStyles/SelectedHistoryStyle; fix by making HistoryStyleOption raise INotifyPropertyChanged for its Display (implement PropertyChanged in UpdateLabel) and/or after calling HistoryStyles.ForEach(x => x.UpdateLabel(_i18n)) call the view model's RaisePropertyChanged(nameof(HistoryStyles)) and RaisePropertyChanged(nameof(SelectedHistoryStyle)) so the dropdown updates when UpdateLabels() runs.
🧹 Nitpick comments (4)
Flow.Launcher.Avalonia/Views/Dialogs/NotificationWindow.axaml.cs (1)
17-18: 🏗️ Heavy liftUse CommunityToolkit MVVM primitives instead of manual code-behind state/handlers.
This dialog currently mixes manual
INotifyPropertyChangedand click handlers in the view class; please move bindable state/commands to anObservableObjectview model and bind commands from AXAML.As per coding guidelines:
Flow.Launcher.Avalonia/**/*.cs: Avalonia UI code should use CommunityToolkit.Mvvm patterns such asObservableObject,[ObservableProperty],[RelayCommand].Also applies to: 48-57, 123-153
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/Views/Dialogs/NotificationWindow.axaml.cs` around lines 17 - 18, The NotificationWindow class currently implements INotifyPropertyChanged and contains click handlers; create a NotificationViewModel that inherits CommunityToolkit.Mvvm.ComponentModel.ObservableObject, convert window state fields to [ObservableProperty]s and handlers to [RelayCommand] methods, then set NotificationWindow.DataContext to an instance of that view model and remove the manual INotifyPropertyChanged implementation and code-behind click handlers from NotificationWindow; update AXAML to bind buttons and properties to the view model commands/properties instead of using code-behind.Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj (1)
68-68: Add explicit separator in OutputPath glob for consistency across all plugins.While MSBuild's normalization makes the current pattern
$(OutputPath)**\*.*work correctly and deterministically, adding an explicit backslash makes the glob pattern clearer:$(OutputPath)\**\*.*. Apply this consistently across all plugin projects where this pattern is used.Files affected
All 12 plugin csproj files use this pattern:
- Flow.Launcher.Plugin.Explorer (line 68)
- Flow.Launcher.Plugin.WindowsSettings (line 78)
- Flow.Launcher.Plugin.Sys (line 75)
- Flow.Launcher.Plugin.Program (line 84)
- Flow.Launcher.Plugin.ProcessKiller (line 78)
- Flow.Launcher.Plugin.WebSearch (line 72)
- Flow.Launcher.Plugin.Url (line 70)
- Flow.Launcher.Plugin.PluginIndicator (line 69)
- Flow.Launcher.Plugin.PluginsManager (line 51)
- Flow.Launcher.Plugin.Shell (line 74)
- Flow.Launcher.Plugin.BrowserBookmark (line 125)
- Flow.Launcher.Plugin.Calculator (line 78)
Proposed fix
- <PluginFiles Include="$(OutputPath)**\*.*" /> + <PluginFiles Include="$(OutputPath)\**\*.*" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj` at line 68, Update the PluginFiles Include pattern to explicitly add a directory separator after $(OutputPath) so the glob is unambiguous: change the attribute value from "$(OutputPath)**\*.*" to "$(OutputPath)\**\*.*" in this csproj (look for the PluginFiles Include entry and the OutputPath pattern), and apply the same edit to the other plugin csproj files listed to ensure consistency across all plugins.Flow.Launcher.Avalonia/Views/Dialogs/ProgressBoxWindow.axaml (1)
1-14: ⚡ Quick winReorder root AXAML attributes to match repo convention.
x:Classis currently placed after severalxmlns/design attributes; please reorder the root attributes to follow the project’s prescribed order for consistent formatting.As per coding guidelines:
**/*.{xaml,axaml}: In XAML/AXAML attribute order: x:Class → xmlns → key/name → layout → size → margin/padding → rest.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/Views/Dialogs/ProgressBoxWindow.axaml` around lines 1 - 14, Reorder the root Window attributes in ProgressBoxWindow.axaml so x:Class is the first attribute (x:Class="Flow.Launcher.Avalonia.Views.Dialogs.ProgressBoxWindow"), followed by the xmlns declarations, then key/name attributes, layout attributes, size attributes (Width/Height), margin/padding, and then the remaining attributes (e.g., CanResize, WindowStartupLocation); update the Window tag for the ProgressBoxWindow XAML to follow the project's prescribed attribute ordering convention.Flow.Launcher.Avalonia/Views/Dialogs/ProgressBoxWindow.axaml.cs (1)
11-25: 🏗️ Heavy liftAlign this dialog with the Avalonia MVVM pattern used in the host.
This window currently acts as its own view-model (
INotifyPropertyChanged+ code-behind click handlers). Please move state/commands into anObservableObjectview-model and bind commands from AXAML for consistency with the Avalonia migration pattern.As per coding guidelines:
Flow.Launcher.Avalonia/**/*.cs: Avalonia UI code should use CommunityToolkit.Mvvm patterns such asObservableObject,[ObservableProperty],[RelayCommand].Also applies to: 125-139
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Flow.Launcher.Avalonia/Views/Dialogs/ProgressBoxWindow.axaml.cs` around lines 11 - 25, The ProgressBoxWindow class currently implements view-model responsibilities (implements INotifyPropertyChanged, holds _isIndeterminate, _progressValue, _progressText and click handlers) — refactor by extracting a new ViewModel class that derives from CommunityToolkit.Mvvm.ComponentModel.ObservableObject (e.g., ProgressBoxViewModel) and use [ObservableProperty] for IsIndeterminate, ProgressValue, ProgressText and a [RelayCommand] for the cancel action that invokes the existing _cancelProgress logic; update ProgressBoxWindow to set DataContext = new ProgressBoxViewModel(TitleText, cancelAction) and remove INotifyPropertyChanged implementation and backing fields from the code-behind, then wire the AXAML to bind to the new ViewModel properties and command names instead of relying on code-behind handlers (ensure ProgressBoxWindow constructor still accepts caption and cancelAction and passes them into the ViewModel).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a0986ad8-fa43-42ba-912f-9c97ce418693
📒 Files selected for processing (81)
.github/AGENTS.md.github/workflows/dotnet.yml.gitignore.gsd/quick/1-a-review-about-whether-we-have-touched-a/1-SUMMARY.mdAGENTS.mdAVALONIA_MIGRATION_CHECKLIST.mdFlow.Launcher.Avalonia/.agent/README.mdFlow.Launcher.Avalonia/AGENTS.mdFlow.Launcher.Avalonia/App.axaml.csFlow.Launcher.Avalonia/AvaloniaPublicAPI.csFlow.Launcher.Avalonia/Flow.Launcher.Avalonia.csprojFlow.Launcher.Avalonia/Helper/AutoStartup.csFlow.Launcher.Avalonia/Helper/HotKeyMapper.csFlow.Launcher.Avalonia/Helper/ResultHelper.csFlow.Launcher.Avalonia/MainWindow.axamlFlow.Launcher.Avalonia/MainWindow.axaml.csFlow.Launcher.Avalonia/Storage/History.csFlow.Launcher.Avalonia/Themes/Base.axamlFlow.Launcher.Avalonia/Themes/Resources.axamlFlow.Launcher.Avalonia/ViewModel/MainViewModel.csFlow.Launcher.Avalonia/ViewModel/ResultViewModel.csFlow.Launcher.Avalonia/ViewModel/SettingPages/AboutSettingsViewModel.csFlow.Launcher.Avalonia/ViewModel/SettingPages/GeneralSettingsViewModel.csFlow.Launcher.Avalonia/ViewModel/SettingPages/HotkeySettingsViewModel.csFlow.Launcher.Avalonia/ViewModel/SettingPages/PluginStoreSettingsViewModel.csFlow.Launcher.Avalonia/ViewModel/SettingPages/ThemeSettingsViewModel.csFlow.Launcher.Avalonia/Views/Controls/HotkeyControl.axaml.csFlow.Launcher.Avalonia/Views/Controls/HotkeyDisplay.axamlFlow.Launcher.Avalonia/Views/Controls/HotkeyDisplay.axaml.csFlow.Launcher.Avalonia/Views/Dialogs/NotificationWindow.axamlFlow.Launcher.Avalonia/Views/Dialogs/NotificationWindow.axaml.csFlow.Launcher.Avalonia/Views/Dialogs/PluginUpdateWindow.axamlFlow.Launcher.Avalonia/Views/Dialogs/PluginUpdateWindow.axaml.csFlow.Launcher.Avalonia/Views/Dialogs/ProgressBoxWindow.axamlFlow.Launcher.Avalonia/Views/Dialogs/ProgressBoxWindow.axaml.csFlow.Launcher.Avalonia/Views/Dialogs/ReportWindow.axamlFlow.Launcher.Avalonia/Views/Dialogs/ReportWindow.axaml.csFlow.Launcher.Avalonia/Views/Dialogs/WelcomeWindow.axamlFlow.Launcher.Avalonia/Views/Dialogs/WelcomeWindow.axaml.csFlow.Launcher.Avalonia/Views/ResultListBox.axamlFlow.Launcher.Avalonia/Views/SettingPages/AboutSettingsPage.axamlFlow.Launcher.Avalonia/Views/SettingPages/CustomQueryHotkeyWindow.axamlFlow.Launcher.Avalonia/Views/SettingPages/CustomQueryHotkeyWindow.axaml.csFlow.Launcher.Avalonia/Views/SettingPages/CustomShortcutWindow.axamlFlow.Launcher.Avalonia/Views/SettingPages/CustomShortcutWindow.axaml.csFlow.Launcher.Avalonia/Views/SettingPages/GeneralSettingsPage.axamlFlow.Launcher.Avalonia/Views/SettingPages/HotkeySettingsPage.axamlFlow.Launcher.Avalonia/Views/SettingPages/ReleaseNotesWindow.axamlFlow.Launcher.Avalonia/Views/SettingPages/ReleaseNotesWindow.axaml.csFlow.Launcher.Avalonia/Views/SettingPages/SelectBrowserWindow.axamlFlow.Launcher.Avalonia/Views/SettingPages/SelectBrowserWindow.axaml.csFlow.Launcher.Avalonia/Views/SettingPages/SelectFileManagerWindow.axamlFlow.Launcher.Avalonia/Views/SettingPages/SelectFileManagerWindow.axaml.csFlow.Launcher.Avalonia/Views/SettingPages/ThemeSettingsPage.axamlFlow.Launcher.Core/AGENTS.mdFlow.Launcher.Core/packages.lock.jsonFlow.Launcher.Infrastructure/AGENTS.mdFlow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csprojFlow.Launcher.Infrastructure/Storage/JsonStorage.csFlow.Launcher.Infrastructure/packages.lock.jsonFlow.Launcher.Plugin/AGENTS.mdFlow.Launcher.Plugin/Flow.Launcher.Plugin.csprojFlow.Launcher.Plugin/packages.lock.jsonFlow.Launcher.Test/Avalonia/ProgrammaticQueryFocusRequestTest.csFlow.Launcher.Test/Flow.Launcher.Test.csprojFlow.Launcher/AGENTS.mdFlow.Launcher/Languages/en.xamlFlow.Launcher/Storage/History.csFlow.Launcher/packages.lock.jsonPlugins/AGENTS.mdPlugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csprojPlugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csprojPlugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.csPlugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.csPlugins/Flow.Launcher.Plugin.PluginsManager/Views/PluginsManagerSettings.xamlPlugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csprojPlugins/Flow.Launcher.Plugin.ProcessKiller/Views/SettingsControl.xamlPlugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csprojPlugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csprojPlugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csprojopencode.json
✅ Files skipped from review due to trivial changes (7)
- Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj
- Flow.Launcher.Avalonia/.agent/README.md
- Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj
- .gitignore
- Flow.Launcher.Avalonia/Themes/Base.axaml
- Flow.Launcher/Languages/en.xaml
- Flow.Launcher.Plugin/packages.lock.json
🚧 Files skipped from review as they are similar to previous changes (7)
- Flow.Launcher.Test/Flow.Launcher.Test.csproj
- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
- .github/workflows/dotnet.yml
- Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj
- Flow.Launcher.Avalonia/ViewModel/SettingPages/HotkeySettingsViewModel.cs
- Flow.Launcher.Avalonia/ViewModel/ResultViewModel.cs
- Flow.Launcher.Avalonia/AvaloniaPublicAPI.cs
| if (needsRecreation) | ||
| { | ||
| UnscheduleLogonTask(); | ||
| ScheduleLogonTask(); | ||
| } |
There was a problem hiding this comment.
Don’t report auto-start as healthy after a failed repair.
Both check paths delete and recreate the startup entry when they see a mismatch, but they ignore the Schedule*/Unschedule* return values and still report success. If the re-registration fails, CheckIsEnabled(...) will treat startup as enabled even though the repair just removed the existing entry.
Suggested fix
if (needsRecreation)
{
- UnscheduleLogonTask();
- ScheduleLogonTask();
+ return UnscheduleLogonTask() && ScheduleLogonTask();
}
}
return true;
...
if (!action.Equals(Constant.ExecutablePath, StringComparison.OrdinalIgnoreCase)
&& !action.Equals($"\"{Constant.ExecutablePath}\"", StringComparison.OrdinalIgnoreCase))
{
- UnscheduleRegistry();
- ScheduleRegistry();
+ return UnscheduleRegistry() && ScheduleRegistry();
}
return true;Also applies to: 115-117
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Flow.Launcher.Avalonia/Helper/AutoStartup.cs` around lines 85 - 89, The code
currently ignores the return values from UnscheduleLogonTask() and
ScheduleLogonTask() when needsRecreation is true and still reports success;
modify the logic in the block that uses needsRecreation (and the similar block
at lines 115-117) to check the boolean results from UnscheduleLogonTask() and
ScheduleLogonTask(), and only consider the repair successful if both calls
return true (or propagate failure accordingly); if either call fails, ensure
CheckIsEnabled(...) will not treat auto-start as healthy (e.g., set the
status/flag to false or return failure) and log the failure so callers know the
re-registration failed.
| internal static void SetToggleHotkey(string hotkeyString) | ||
| { | ||
| RemoveToggleHotkey(); | ||
|
|
||
| if (string.IsNullOrWhiteSpace(hotkeyString)) | ||
| { | ||
| Log.Warn(ClassName, "Empty hotkey string"); | ||
| return; | ||
| } | ||
|
|
||
| var (mods, key) = GlobalHotkey.ParseHotkeyString(hotkeyString); | ||
|
|
||
| if (key == 0) | ||
| { | ||
| Log.Error(ClassName, $"Failed to parse hotkey: {hotkeyString}"); | ||
| return; | ||
| } | ||
|
|
||
| _toggleHotkeyId = GlobalHotkey.Register(mods, key, OnToggleHotkey); | ||
|
|
||
| if (_toggleHotkeyId < 0) | ||
| { | ||
| Log.Error(ClassName, $"Failed to register hotkey: {hotkeyString}"); | ||
| } | ||
| else | ||
| { | ||
| Log.Info(ClassName, $"Registered toggle hotkey: {hotkeyString}"); | ||
| } |
There was a problem hiding this comment.
Keep the current toggle hotkey alive until the replacement succeeds.
SetToggleHotkey() unregisters the existing toggle hotkey before it knows the new value parses and registers cleanly. Any bad or unavailable value leaves the launcher without its main global hotkey.
A safer flow here is: validate/attempt the new registration first, then swap registrations only after success, or roll back to the previous registration if the new one fails.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Flow.Launcher.Avalonia/Helper/HotKeyMapper.cs` around lines 52 - 79,
SetToggleHotkey currently removes the existing hotkey before
validating/registering the new one; change the flow so the current hotkey is
kept until the new hotkey is proven valid and registered. In SetToggleHotkey use
GlobalHotkey.ParseHotkeyString to validate first and then attempt
GlobalHotkey.Register(mods, key, OnToggleHotkey) into a temporary id; if parse
fails (key == 0) or the temporary register returns < 0, log the error and return
without calling RemoveToggleHotkey or changing _toggleHotkeyId; if the temporary
register succeeds, then call RemoveToggleHotkey (to clear the old
_toggleHotkeyId), assign _toggleHotkeyId = tempId and log success. Ensure all
references use SetToggleHotkey, GlobalHotkey.ParseHotkeyString,
GlobalHotkey.Register, RemoveToggleHotkey, _toggleHotkeyId, and OnToggleHotkey.
| if (LastOpenedHistoryItems.Count >= _maxHistory) | ||
| { | ||
| LastOpenedHistoryItems.RemoveAt(0); | ||
| } | ||
|
|
||
| var existingHistoryItem = LastOpenedHistoryItems.FirstOrDefault(x => x.Equals(queryText, result)); | ||
| if (existingHistoryItem is not null) | ||
| { | ||
| existingHistoryItem.ExecutedDateTime = DateTime.Now; | ||
| existingHistoryItem.Query = queryText; | ||
| return; | ||
| } | ||
|
|
||
| LastOpenedHistoryItems.Add(new LastOpenedHistoryResult(queryText, result)); |
There was a problem hiding this comment.
Reorder capacity trimming to avoid dropping entries on updates.
Line 26 trims first, then Line 31 checks for existing entries. At max size, updating an existing history row still removes the oldest item, causing unintended data loss.
Suggested fix
- if (LastOpenedHistoryItems.Count >= _maxHistory)
- {
- LastOpenedHistoryItems.RemoveAt(0);
- }
-
var existingHistoryItem = LastOpenedHistoryItems.FirstOrDefault(x => x.Equals(queryText, result));
if (existingHistoryItem is not null)
{
existingHistoryItem.ExecutedDateTime = DateTime.Now;
existingHistoryItem.Query = queryText;
return;
}
+
+ if (LastOpenedHistoryItems.Count >= _maxHistory)
+ {
+ LastOpenedHistoryItems.RemoveAt(0);
+ }
LastOpenedHistoryItems.Add(new LastOpenedHistoryResult(queryText, result));📝 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.
| if (LastOpenedHistoryItems.Count >= _maxHistory) | |
| { | |
| LastOpenedHistoryItems.RemoveAt(0); | |
| } | |
| var existingHistoryItem = LastOpenedHistoryItems.FirstOrDefault(x => x.Equals(queryText, result)); | |
| if (existingHistoryItem is not null) | |
| { | |
| existingHistoryItem.ExecutedDateTime = DateTime.Now; | |
| existingHistoryItem.Query = queryText; | |
| return; | |
| } | |
| LastOpenedHistoryItems.Add(new LastOpenedHistoryResult(queryText, result)); | |
| var existingHistoryItem = LastOpenedHistoryItems.FirstOrDefault(x => x.Equals(queryText, result)); | |
| if (existingHistoryItem is not null) | |
| { | |
| existingHistoryItem.ExecutedDateTime = DateTime.Now; | |
| existingHistoryItem.Query = queryText; | |
| return; | |
| } | |
| if (LastOpenedHistoryItems.Count >= _maxHistory) | |
| { | |
| LastOpenedHistoryItems.RemoveAt(0); | |
| } | |
| LastOpenedHistoryItems.Add(new LastOpenedHistoryResult(queryText, result)); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Flow.Launcher.Avalonia/Storage/History.cs` around lines 26 - 39, The capacity
trim runs before checking for an existing entry which causes removal of the
oldest item when an update should have occurred; change the logic in the method
using LastOpenedHistoryItems and _maxHistory so you first search for
existingHistoryItem (using LastOpenedHistoryItems.FirstOrDefault(...)), update
its ExecutedDateTime and Query and return if found, and only when adding a new
LastOpenedHistoryResult(queryText, result) perform the size check and remove the
oldest item (RemoveAt(0)) if LastOpenedHistoryItems.Count >= _maxHistory before
calling Add.
| public void OnPluginsReady() | ||
| { | ||
| _pluginsReady = true; | ||
| MainWindowVisibility = true; | ||
| Log.Info(ClassName, "Plugins ready - window shown"); | ||
| if (!string.IsNullOrWhiteSpace(QueryText)) | ||
| _ = QueryAsync(); | ||
| } |
There was a problem hiding this comment.
Don't force the launcher visible when plugins finish loading.
OnPluginsReady() flips MainWindowVisibility to true unconditionally, so a background/auto-start launch will surface the main window as soon as plugin init completes. This should only resume a pending query; visibility still needs to come from the normal show/hotkey flow.
Suggested change
public void OnPluginsReady()
{
_pluginsReady = true;
- MainWindowVisibility = true;
- Log.Info(ClassName, "Plugins ready - window shown");
+ Log.Info(ClassName, "Plugins ready");
if (!string.IsNullOrWhiteSpace(QueryText))
+ {
_ = QueryAsync();
+ }
}📝 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.
| public void OnPluginsReady() | |
| { | |
| _pluginsReady = true; | |
| MainWindowVisibility = true; | |
| Log.Info(ClassName, "Plugins ready - window shown"); | |
| if (!string.IsNullOrWhiteSpace(QueryText)) | |
| _ = QueryAsync(); | |
| } | |
| public void OnPluginsReady() | |
| { | |
| _pluginsReady = true; | |
| Log.Info(ClassName, "Plugins ready"); | |
| if (!string.IsNullOrWhiteSpace(QueryText)) | |
| { | |
| _ = QueryAsync(); | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Flow.Launcher.Avalonia/ViewModel/MainViewModel.cs` around lines 228 - 235,
OnPluginsReady currently forces the UI visible by setting MainWindowVisibility =
true; remove that line so plugin initialization does not surface the window
automatically. Keep setting _pluginsReady = true and the Log.Info(ClassName,
"Plugins ready - window shown") message should be updated or left as
appropriate, and preserve the pending-query behavior by calling QueryAsync()
when QueryText is non-empty (i.e., keep the existing _ = QueryAsync() block).
Ensure the method only resumes a pending query and does not change
MainWindowVisibility; reference: OnPluginsReady, MainWindowVisibility,
QueryAsync.
| private void OnOpenIssueClick(object? sender, RoutedEventArgs e) | ||
| { | ||
| Process.Start(new ProcessStartInfo(IssueUrl) { UseShellExecute = true }); | ||
| } | ||
|
|
||
| private void OnOpenLogsClick(object? sender, RoutedEventArgs e) | ||
| { | ||
| Process.Start(new ProcessStartInfo(DataLocation.VersionLogDirectory) { UseShellExecute = true }); | ||
| } |
There was a problem hiding this comment.
Handle shell launch failures in dialog actions.
Process.Start on Line 49 and Line 54 can throw and crash this crash-reporting window path. Wrap these opens with safe error handling.
Suggested fix pattern
private void OnOpenIssueClick(object? sender, RoutedEventArgs e)
{
- Process.Start(new ProcessStartInfo(IssueUrl) { UseShellExecute = true });
+ TryOpenWithShell(IssueUrl);
}
private void OnOpenLogsClick(object? sender, RoutedEventArgs e)
{
- Process.Start(new ProcessStartInfo(DataLocation.VersionLogDirectory) { UseShellExecute = true });
+ TryOpenWithShell(DataLocation.VersionLogDirectory);
}
+
+private static void TryOpenWithShell(string target)
+{
+ try
+ {
+ Process.Start(new ProcessStartInfo(target) { UseShellExecute = true });
+ }
+ catch
+ {
+ // keep report window stable even if shell open fails
+ }
+}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Flow.Launcher.Avalonia/Views/Dialogs/ReportWindow.axaml.cs` around lines 47 -
55, OnOpenIssueClick and OnOpenLogsClick call Process.Start directly which can
throw and crash the dialog; wrap the Process.Start calls in try/catch in both
methods (OnOpenIssueClick and OnOpenLogsClick), use the same
ProcessStartInfo(IssueUrl) and
ProcessStartInfo(DataLocation.VersionLogDirectory) respectively, and on
exception handle it safely by logging the exception (via your existing logger)
and showing a non-fatal user-facing error message (e.g., message box) so the
crash-reporting window does not crash.
| var website = pluginException.Metadata.Website; | ||
| if (!website.StartsWith("https://github.com", StringComparison.OrdinalIgnoreCase)) | ||
| { | ||
| return website; | ||
| } |
There was a problem hiding this comment.
Guard Website before calling StartsWith.
Line 70 dereferences website without a null/empty check. Plugin metadata can be missing this field, which would throw while opening the report window.
Suggested fix
var website = pluginException.Metadata.Website;
+if (string.IsNullOrWhiteSpace(website))
+{
+ return Constant.IssuesUrl;
+}
+
if (!website.StartsWith("https://github.com", StringComparison.OrdinalIgnoreCase))
{
return website;
}📝 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.
| var website = pluginException.Metadata.Website; | |
| if (!website.StartsWith("https://github.com", StringComparison.OrdinalIgnoreCase)) | |
| { | |
| return website; | |
| } | |
| var website = pluginException.Metadata.Website; | |
| if (string.IsNullOrWhiteSpace(website)) | |
| { | |
| return Constant.IssuesUrl; | |
| } | |
| if (!website.StartsWith("https://github.com", StringComparison.OrdinalIgnoreCase)) | |
| { | |
| return website; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Flow.Launcher.Avalonia/Views/Dialogs/ReportWindow.axaml.cs` around lines 69 -
73, Guard against null/empty before calling StartsWith on
pluginException.Metadata.Website: check string.IsNullOrEmpty (or
IsNullOrWhiteSpace) for the local variable website and return website (or an
appropriate default) immediately if it's null/empty, then proceed to call
website.StartsWith("https://github.com", StringComparison.OrdinalIgnoreCase);
update the block that currently reads var website =
pluginException.Metadata.Website; if (!website.StartsWith(...)) { return
website; } to perform the null/empty guard first so you never dereference
website.
| CustomBrowsers = new ObservableCollection<CustomBrowserViewModel>(_settings.CustomBrowserList.Select(x => x.Copy())); | ||
| _selectedCustomBrowserIndex = Math.Clamp(_settings.CustomBrowserIndex, 0, Math.Max(0, CustomBrowsers.Count - 1)); |
There was a problem hiding this comment.
Guard the empty-profile case.
Line 54 assumes at least one browser exists, but Lines 112-113 can reduce CustomBrowsers to zero and then reclamp the index back to 0. After that, any binding through CurrentBrowser will hit CustomBrowsers[0], and Line 161 persists an invalid selected index.
Please either prevent deleting the last entry or model “no selection” explicitly with -1/nullable current item before wiring bindings through CurrentBrowser.
Also applies to: 54-54, 112-113, 160-161
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Flow.Launcher.Avalonia/Views/SettingPages/SelectBrowserWindow.axaml.cs`
around lines 27 - 28, The code currently assumes at least one browser in
CustomBrowsers and clamps _selectedCustomBrowserIndex to 0, causing
out-of-range/bad persistence when the list becomes empty; update the logic in
the SelectBrowserWindow to model “no selection” explicitly (use -1 or nullable
index) or prevent removing the last entry: change initialization of
_selectedCustomBrowserIndex (from Math.Clamp(...)) to allow -1 when
CustomBrowsers.Count == 0, update CurrentBrowser to return null when index is
-1, update delete/remove logic that modifies CustomBrowsers (the code around the
handlers at lines referenced) to either block removal if Count == 1 or set
_selectedCustomBrowserIndex = -1 after removal, and ensure when persisting back
to _settings.CustomBrowserIndex you write a sentinel (e.g., -1) or skip
persistence for no-selection so you never persist an invalid index.
| CustomExplorers = new ObservableCollection<CustomExplorerViewModel>(_settings.CustomExplorerList.Select(x => x.Copy())); | ||
| _selectedCustomExplorerIndex = Math.Clamp(_settings.CustomExplorerIndex, 0, Math.Max(0, CustomExplorers.Count - 1)); |
There was a problem hiding this comment.
Handle CustomExplorers == 0 before dereferencing CurrentExplorer.
The same index-clamping pattern here leaves SelectedCustomExplorerIndex == 0 even when the collection is empty. That makes Line 56 and Line 58 unsafe after deleting the last profile, and Lines 143-144 then save an invalid selection back to settings.
Please keep at least one entry around or switch this window to a proper “no selection” state before binding through CurrentExplorer/CanSave.
Also applies to: 56-60, 84-85, 143-144
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Flow.Launcher.Avalonia/Views/SettingPages/SelectFileManagerWindow.axaml.cs`
around lines 29 - 30, The SelectedCustomExplorerIndex is clamped to 0 even when
CustomExplorers is empty, causing dereferences of CurrentExplorer/CanSave and
saving an invalid selection; change the selection logic to support "no
selection" by making the index nullable (or allow -1) and when constructing
CustomExplorers from _settings.CustomExplorerList set
_selectedCustomExplorerIndex = CustomExplorers.Count > 0 ?
Math.Clamp(_settings.CustomExplorerIndex, 0, CustomExplorers.Count - 1) : -1 (or
null), update SelectedCustomExplorerIndex property and any consumers
(CurrentExplorer, CanSave, save logic) to handle a missing selection (return
null or false and disable save), and ensure when deleting the last profile you
either add a default fallback CustomExplorerViewModel to CustomExplorers or set
the index to -1/null and prevent writing an invalid index back to
_settings.CustomExplorerIndex.
| </ui:SettingsExpanderItem> | ||
| </ui:SettingsExpander> | ||
|
|
||
| <ui:SettingsExpander Header="{i18n:Localize Clock} / {i18n:Localize Date}" |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does Avalonia AXAML support concatenating multiple markup extensions inside one attribute value, such as Header="{i18n:Localize Clock} / {i18n:Localize Date}"?
💡 Result:
No—Avalonia AXAML does not support concatenating multiple markup extension results by placing multiple markup extensions directly inside a single attribute value like: Header="{i18n:Localize Clock} / {i18n:Localize Date}". In Avalonia, a markup extension is a special expression enclosed in a single pair of curly braces. While Avalonia does support nesting one markup extension inside another (e.g., Binding with Converter={StaticResource ...}), the docs only describe markup-extension nesting as a structured feature, not free-form concatenation of multiple independent {…} expressions within the same attribute value. Practical alternatives: - Use a single localization/formatting mechanism that returns the whole header string (e.g., one markup extension call that formats both parts). - Or use a binding-based approach that can combine values, such as MultiBinding with a converter, then apply the localized pieces inside that converter.
Citations:
- 1: https://docs.avaloniaui.net/docs/concepts/markupextensions
- 2: https://docs.avaloniaui.net/docs/xaml/markup-extensions
- 3: https://docs.avaloniaui.net/docs/data-binding/how-to-bind-multiple-properties
Don't concatenate multiple markup extensions inside one Header attribute value.
Header="{i18n:Localize Clock} / {i18n:Localize Date}" is invalid—Avalonia AXAML does not support multiple markup extensions in a single attribute. Use a single localization key for the combined label, or compose the header with child content using a MultiBinding approach with a converter.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Flow.Launcher.Avalonia/Views/SettingPages/ThemeSettingsPage.axaml` at line
211, The Header on the SettingsExpander is using two markup extensions
concatenated ("Header=\"{i18n:Localize Clock} / {i18n:Localize Date}\"") which
Avalonia AXAML doesn't support; fix it by using a single localization key for
the combined label (add a "ClockDate" entry and use Header="{i18n:Localize
ClockDate}") or replace the Header attribute with child content (e.g., set
Header to a container element and place two localized TextBlocks or use a
MultiBinding+converter) — update the SettingsExpander element's Header usage
accordingly.
This PR introduces the experimental Avalonia UI host alongside the existing WPF host so the migration can continue incrementally while preserving side-by-side comparison.
The current migration tracker is
AVALONIA_MIGRATION_CHECKLIST.md.Summary
Flow.Launcher.Avalonia, a .NET 9 Avalonia host with its own output directory atOutput/Debug/Avalonia.Flow.Launcher.Plugin,Flow.Launcher.Infrastructure, andFlow.Launcher.Core.What Works
Migration Checklist
Windows & Dialogs — 16/19 complete (84%)
Settings Pages
Core UX / ViewModels
Helpers, Converters, and Controls
Theming and Polish
Build & Run
Notes for Reviewers
AVALONIA_MIGRATION_CHECKLIST.mdas the detailed source of truth for parity status and remaining gaps.