-
-
Notifications
You must be signed in to change notification settings - Fork 539
Avalonia migration #4214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Avalonia migration #4214
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces a new Avalonia UI frontend for Flow Launcher as an alternative to the existing WPF implementation. The migration adds a complete new project with UI components, ViewModels, and platform integration while maintaining compatibility with the existing plugin system.
Changes:
- Adds new
Flow.Launcher.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> |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected spelling of 'true/pm' to 'true/PM' - the value appears to be malformed. It should be just 'true' for the dpiAware element, or 'PerMonitor' for per-monitor DPI awareness.
| <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}"); |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Console logging added to production code affects performance and should be conditional. Consider wrapping this in a DEBUG preprocessor directive or using a configuration flag to enable/disable console output, as this will log every message in Release builds.
| // 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(); | ||
| } |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Dispose method doesn't follow the standard IDisposable pattern. Consider implementing the full pattern with a protected virtual Dispose(bool disposing) method and a finalizer to handle unmanaged resources properly, especially since this class manages disposable resources.
|
|
||
| if (RegisterClass(ref wc) == 0) | ||
| { | ||
| Console.WriteLine("[GlobalHotkey] Failed to register window class"); |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Direct Console.WriteLine calls for logging should use the application's logging infrastructure (Flow.Launcher.Infrastructure.Logger.Log) for consistency and proper log management. This applies to all Console.WriteLine statements in this file.
| var wc = new WNDCLASS | ||
| { | ||
| lpfnWndProc = _wndProc, | ||
| hInstance = GetModuleHandle(null), |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace this call with a call to managed code if possible.
| hInstance = GetModuleHandle(null), | |
| hInstance = Marshal.GetHINSTANCE(typeof(GlobalHotkey).Module), |
| private string _subTitle = string.Empty; | ||
|
|
||
| [ObservableProperty] | ||
| private string _iconPath = string.Empty; |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Field '_iconPath' can be 'readonly'.
| public partial class ResultViewModel : ObservableObject | ||
| { | ||
| [ObservableProperty] | ||
| private string _title = string.Empty; |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Field '_title' can be 'readonly'.
|
|
||
| public partial class ResultListBox : UserControl | ||
| { | ||
| private ListBox? _listBox; |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Field '_listBox' can be 'readonly'.
| private int _selectedIndex; | ||
|
|
||
| [ObservableProperty] | ||
| private bool _isVisible = true; |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Field '_isVisible' can be 'readonly'.
| private bool _hasResults; | ||
|
|
||
| [ObservableProperty] | ||
| private ResultsViewModel _results; |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Field '_results' can be 'readonly'.
- 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
|
🥷 Code experts: VictoriousRaptor VictoriousRaptor has most 👩💻 activity in the files. See details
Activity based on git-commit:
Knowledge based on git-blame:
Activity based on git-commit:
Knowledge based on git-blame: ✨ Comment |
This comment has been minimized.
This comment has been minimized.
- Embed SegoeFluentIcons.ttf as an AvaloniaResource - Register SegoeFluentIcons FontFamily in App.axaml using avares URI - Create FontLoader helper for robust font resolution (Embedded > System > File) - Add safety checks to FontLoader to prevent rendering crashes if fonts are invalid - Update ResultViewModel and ResultListBox to use resolved GlyphFontFamily
This comment has been minimized.
This comment has been minimized.
- Add HotkeyRecorderDialog with global keyboard hook for capturing hotkeys - Implement manual modifier state tracking to handle swallowed key events - Add HotkeyControl button that opens the recorder dialog - Add CheckAvailability and RemoveToggleHotkey to HotKeyMapper - Expose GetKeyFromVk helper in GlobalHotkey infrastructure - Add Settings pages (General, Plugin, Theme, Proxy, About) - Add PreviewPanel for result previews in main window - Fix hook reuse issue by clearing callback on close instead of disposing
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
…tingsExpander - Add Card, ExCard, CardGroup custom controls for settings pages - Update GeneralSettingsViewModel with new properties: - Startup: UseLogonTaskForStartup, HideOnStartup - Behavior: HideNotifyIcon, IgnoreHotkeysOnFullscreen, AlwaysPreview - Updates: AutoUpdates, AutoUpdatePlugins - Search: SearchPrecision, LastQueryMode, SearchDelay - Home: ShowHomePage, ShowHistoryResults, MaxHistoryResults - Misc: AutoRestart, ShowWarning, AlwaysStartEn, ShouldUsePinyin - Rewrite GeneralSettingsPage.axaml using SettingsExpander pattern - Update migration checklist to ~35-40% progress
This comment has been minimized.
This comment has been minimized.
- Implemented channel-based debouncing (20ms) in MainViewModel to fix result flickering (matches WPF behavior) - Added ResultForUpdate struct and ProcessResultUpdatesAsync for batching updates - Added HighlightTextConverter and TextBlockHelper for bolding matched query terms - Updated ResultListBox to display highlighted title and subtitle
This comment has been minimized.
This comment has been minimized.
- Created WpfControlHost to host WPF controls using HwndSource - Updated PluginItemViewModel to load settings via ISettingProvider - Updated PluginsSettingsPage to display settings in an expander
@check-spelling-bot Report🔴 Please reviewSee the 📂 files view, the 📜action log, or 📝 job summary for details.
See ❌ Event descriptions for more information. If the flagged items are 🤯 false positivesIf items relate to a ...
|
…ttings window host in Avalonia
- Update ISettingProvider to support CreateSettingPanelAvalonia - Create native Avalonia settings UI for Program Plugin (ProgramSetting.axaml) - Implement ProgramSettingViewModel using CommunityToolkit.Mvvm - Update PluginsSettingsViewModel to detect and load native Avalonia settings - Replace ListBox with ItemsControl in PluginsSettingsPage to fix auto-scroll issues when expanding settings - Align Avalonia package versions (11.2.3) across projects to fix type load errors
…tings - Remove Dispatcher.UIThread.Post which might cause timing issues - Call Hide() before showing SettingsWindow to ensure proper focus and visibility transition
…tings - Call Hide() before scheduling SettingsWindow.Show() to ensure proper visibility state transition - Use Dispatcher.UIThread.Post for showing the new window to avoid focus conflicts
- Restore Hide() method in MainViewModel (accidentally deleted) - Fix Show() method which was incorrectly calling HideRequested - Ensure OpenSettings cleanly hides MainWindow before showing SettingsWindow
- Delay setting desktop.MainWindow until plugins are initialized to prevent auto-show on startup - Restore missing Hide() method in MainViewModel - Fix Show() method which was incorrectly calling HideRequested - Ensure OpenSettings cleanly hides MainWindow before showing SettingsWindow
- Add plugin search with filtering across name, description, and action keywords - Implement display mode switcher (On/Off, Priority, Search Delay, Home On/Off) - Add plugin management controls (enable/disable, priority, search delays, home visibility) - Integrate both native Avalonia settings and WPF fallback support - Add action keywords editing dialog - Include plugin directory access, source code links, and uninstall functionality - Add help dialog explaining priority, search delay, and home features - Improve UI with FluentAvalonia controls and proper layout - Load plugin icons asynchronously and display plugin metrics (init time, query time) Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
- Add i18n injection to Application.Resources for DynamicResource bindings - Create Avalonia settings views for plugins: - Explorer (ExplorerSettings + QuickAccessLinkSettings dialog) - BrowserBookmark (SettingsControl + CustomBrowserSettingWindow) - Calculator (CalculatorSettings) - ProcessKiller (SettingsControl) - PluginsManager (PluginsManagerSettings) - WebSearch (SettingsControl) - Shell (ShellSetting + converter) - Program (ProgramSetting) - Add IsAvalonia detection pattern for dual-framework dialog support - Update 11 plugin .csproj files with CopyToAvaloniaOutput targets - Add System.Threading.Tasks using for async RelayCommand support
- Add PluginStoreSettingsPage with header, language filters, and search - Add PluginStoreSettingsViewModel with async loading and filtering - Add PluginStoreItemViewModel for individual plugin cards with install/update/uninstall - Connect AvaloniaPublicAPI to real PluginsManifest instead of empty stubs - Use FluentAvalonia ItemsRepeater with UniformGridLayout for virtualization - Fix icon visibility using ObjectConverters instead of StringConverters
- Add pull_request types to trigger on opened, synchronize, reopened, ready_for_review - Add 'Flow Launcher Avalonia' artifact uploading Output/Release/Avalonia/
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- Add Delete-Unused-Avalonia function to remove duplicate DLLs in Avalonia output - Add Remove-CreateDumpExe-Avalonia function to clean up createdump.exe - Call Avalonia processing functions in Main after WPF processing
- Add Net9.0-SelfContained.pubxml publish profile for Avalonia - Add Publish-Self-Contained-Avalonia function to post_build.ps1 - Call Avalonia publish before cleanup in Main function
- Port ActionKeywordSetting window to Avalonia - Includes keyword editing, enable/disable toggle, validation - Handles Enter key to submit, Space key blocked in keyword input
I actually vibe everything for this project (given the fact that it is mostly migration) as I want to see how far the agent can go. Very surprising, opencode can generate a working version with less than 3 hours (which I've been trying for much longer before).
Summary
This PR introduces an experimental Avalonia UI implementation alongside the existing WPF UI, allowing incremental migration and side-by-side comparison.
Architecture
Main Window & Query Flow
Results Handling
Image Loading
Performance
What Works
Not Yet Implemented