Skip to content

Conversation

@taooceros
Copy link
Member

@taooceros taooceros commented Jan 15, 2026

I actually vibe everything for this project (given the fact that it is mostly migration) as I want to see how far the agent can go. Very surprising, opencode can generate a working version with less than 3 hours (which I've been trying for much longer before).

Summary

This PR introduces an experimental Avalonia UI implementation alongside the existing WPF UI, allowing incremental migration and side-by-side comparison.

Architecture

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

Main Window & Query Flow

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

Results Handling

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

Image Loading

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

Performance

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

What Works

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

Not Yet Implemented

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

Create Flow.Launcher.Avalonia project as foundation for migrating from WPF to Avalonia UI framework.

Key components:
- MainWindow with query box and results list (matching WPF layout)
- ViewModels: MainViewModel, ResultsViewModel, ResultViewModel
- Themes/Base.axaml with converted styles from WPF
- FluentAvaloniaUI for Windows 11 styling
- References existing Core/Infrastructure/Plugin projects

The project builds and runs alongside the existing WPF application.
This is Phase 1 of the incremental migration approach.
- Load settings from disk via FlowLauncherJsonStorage
- Initialize PluginManager and query plugins on text change
- Add minimal AvaloniaPublicAPI implementing IPublicAPI
- Execute plugin results on Enter key
- Add WPF framework reference for IPublicAPI compatibility
…oading

- Use DynamicData SourceList with automatic sorting by score descending
- Add ReplaceResults() with EditDiff for minimal UI updates (reduces flickering)
- Keep previous results visible while typing until new results arrive
- Add ImageLoader with Windows Shell API (IShellItemImageFactory) for exe/ico icons
- Use AlphaFormat.Unpremul to correctly render transparent icons without white borders
- Query all plugins in parallel and merge/sort results globally
- Wrap each plugin query in Task.Run() to ensure synchronous plugin code
  doesn't block the UI thread
- Show results progressively as each plugin completes using ConcurrentBag
- Update UI after each plugin returns instead of waiting for all plugins
Copilot AI review requested due to automatic review settings January 15, 2026 08:31
@taooceros taooceros marked this pull request as draft January 15, 2026 08:31
@github-actions github-actions bot added this to the 2.1.0 milestone Jan 15, 2026
@github-actions

This comment has been minimized.

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

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new Avalonia UI frontend for Flow Launcher as an alternative to the existing WPF implementation. The migration adds a complete new project with UI components, ViewModels, and platform integration while maintaining compatibility with the existing plugin system.

Changes:

  • Adds new Flow.Launcher.Avalonia project with complete UI implementation including MainWindow, result list views, and styling
  • Implements Windows global hotkey support and image loading utilities with native Win32 interop
  • Adds console logging output to infrastructure logger for easier debugging

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 50 comments.

Show a summary per file
File Description
Flow.Launcher.sln Adds Flow.Launcher.Avalonia project to solution with build configurations
Flow.Launcher.Infrastructure/Logger/Log.cs Adds console output for log messages
Flow.Launcher.Avalonia/app.manifest Defines Windows compatibility and DPI awareness settings
Flow.Launcher.Avalonia/Views/ResultListBox.axaml XAML markup for search results list display
Flow.Launcher.Avalonia/Views/ResultListBox.axaml.cs Code-behind for result list interactions
Flow.Launcher.Avalonia/ViewModel/ResultsViewModel.cs ViewModel managing result collection with sorting and selection
Flow.Launcher.Avalonia/ViewModel/ResultViewModel.cs ViewModel for individual result items
Flow.Launcher.Avalonia/ViewModel/MainViewModel.cs Main application ViewModel coordinating queries and UI state
Flow.Launcher.Avalonia/Themes/Resources.axaml Color and resource definitions for UI theme
Flow.Launcher.Avalonia/Themes/Base.axaml Style definitions for UI components
Flow.Launcher.Avalonia/Program.cs Application entry point
Flow.Launcher.Avalonia/MainWindow.axaml Main window XAML markup
Flow.Launcher.Avalonia/MainWindow.axaml.cs Main window code-behind with event handling
Flow.Launcher.Avalonia/Helper/ImageLoader.cs Image loading utility with caching and Win32 shell integration
Flow.Launcher.Avalonia/Helper/HotKeyMapper.cs Global hotkey registration management
Flow.Launcher.Avalonia/Helper/GlobalHotkey.cs Win32-based global hotkey implementation
Flow.Launcher.Avalonia/Flow.Launcher.Avalonia.csproj Project configuration and dependencies
Flow.Launcher.Avalonia/Converters/CommonConverters.cs Value converters for data binding
Flow.Launcher.Avalonia/Converters/BoolToIsVisibleConverter.cs Boolean to visibility converter
Flow.Launcher.Avalonia/AvaloniaPublicAPI.cs IPublicAPI implementation for plugin compatibility
Flow.Launcher.Avalonia/App.axaml Application XAML with theme configuration
Flow.Launcher.Avalonia/App.axaml.cs Application code-behind with initialization logic

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

Corrected spelling of 'true/pm' to 'true/PM' - the value appears to be malformed. It should be just 'true' for the dpiAware element, or 'PerMonitor' for per-monitor DPI awareness.

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

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

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

Console logging added to production code affects performance and should be conditional. Consider wrapping this in a DEBUG preprocessor directive or using a configuration flag to enable/disable console output, as this will log every message in Release builds.

Suggested change
// Also output to console for easy debugging
System.Console.WriteLine($"[{level}] {classNameWithMethod}: {message}");
// Also output to console for easy debugging
#if DEBUG
System.Console.WriteLine($"[{level}] {classNameWithMethod}: {message}");
#endif

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

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

The Dispose method doesn't follow the standard IDisposable pattern. Consider implementing the full pattern with a protected virtual Dispose(bool disposing) method and a finalizer to handle unmanaged resources properly, especially since this class manages disposable resources.

Copilot uses AI. Check for mistakes.

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

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

Direct Console.WriteLine calls for logging should use the application's logging infrastructure (Flow.Launcher.Infrastructure.Logger.Log) for consistency and proper log management. This applies to all Console.WriteLine statements in this file.

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

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

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

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

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

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

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

Field '_iconPath' can be 'readonly'.

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

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

Field '_title' can be 'readonly'.

Copilot uses AI. Check for mistakes.

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

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

Field '_listBox' can be 'readonly'.

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

[ObservableProperty]
private bool _isVisible = true;
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

Field '_isVisible' can be 'readonly'.

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

[ObservableProperty]
private ResultsViewModel _results;
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

Field '_results' can be 'readonly'.

Copilot uses AI. Check for mistakes.
- Add ActiveView enum to track Results vs ContextMenu view state
- Add ContextMenu ResultsViewModel and view switching logic
- Implement LoadContextMenuCommand using PluginManager.GetContextMenusForPlugin
- Add keyboard navigation: Shift+Enter/Right to open, Left/Escape to close
- Update SelectNextItem/SelectPrevItem to navigate appropriate list
- Update EscCommand to return from context menu before hiding
- Create Internationalization service that parses WPF XAML language files
- Load translations from main Languages/ folder and all plugin Languages/ folders
- Add LocalizeExtension markup extension and Translator helper for XAML/code
- Fix IPublicAPI.GetTranslation to use the i18n service for plugin context menus
- Update MainWindow to use localized placeholder text
@github-actions

This comment has been minimized.

- Internationalization.Initialize() called in constructor when DI creates it
- Remove InitializeInternationalization() method from App.axaml.cs
- Update AvaloniaPublicAPI and LocalizeExtension to use Ioc.Default.GetService
- Reorder ConfigureDI before i18n initialization
@github-actions

This comment has been minimized.

@TBM13
Copy link
Contributor

TBM13 commented Jan 16, 2026

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

@taooceros
Copy link
Member Author

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

There should be some performance improvement for animation. Besides, I also want to have the chance to make flow cross platform as I am gradually migrating my workflow to mac.

- MainWindowVisibility starts as false (window hidden)
- Window IsVisible bound to MainWindowVisibility
- Set MainWindowVisibility = true in OnPluginsReady() after plugins load
@github-actions

This comment has been minimized.

- Add Glyph and GlyphAvailable properties to ResultViewModel
- Set Glyph from plugin Result when creating result items
- Add resultGlyph style matching icon size (32x32)
- Update ResultListBox to show glyph or image icon based on ShowGlyph property
- ShowGlyph = true when UseGlyphIcons is enabled AND glyph is available
@gitstream-cm
Copy link

gitstream-cm bot commented Jan 16, 2026

🥷 Code experts: VictoriousRaptor

VictoriousRaptor has most 👩‍💻 activity in the files.
VictoriousRaptor has most 🧠 knowledge in the files.

See details

Flow.Launcher.Infrastructure/Logger/Log.cs

Activity based on git-commit:

VictoriousRaptor
JAN
DEC
NOV
OCT
SEP
AUG

Knowledge based on git-blame:
VictoriousRaptor: 89%

Flow.Launcher.sln

Activity based on git-commit:

VictoriousRaptor
JAN
DEC
NOV
OCT
SEP
AUG

Knowledge based on git-blame:
VictoriousRaptor: 100%

✨ Comment /gs review for LinearB AI review. Learn how to automate it here.

@github-actions

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

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

This comment has been minimized.

@github-actions

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

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

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

@check-spelling-bot Report

🔴 Please review

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

❌ Errors and Warnings Count
❌ check-file-path 93
⚠️ non-alpha-in-dictionary 2

See ❌ Event descriptions for more information.

If the flagged items are 🤯 false positives

If items relate to a ...

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

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

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

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

  • well-formed pattern.

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

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

    Note that patterns can't match multiline strings.

taooceros and others added 9 commits January 18, 2026 23:28
- 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
@taooceros taooceros marked this pull request as ready for review January 27, 2026 00:23
- Add pull_request types to trigger on opened, synchronize, reopened, ready_for_review
- Add 'Flow Launcher Avalonia' artifact uploading Output/Release/Avalonia/
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 27, 2026

Warning

Rate limit exceeded

@taooceros has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 16 minutes and 32 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

- 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants