mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
4.2 KiB
4.2 KiB
AGENTS.md - Flow.Launcher
Windows productivity launcher (like Alfred/Raycast) with dual UI frameworks:
- WPF:
Flow.Launcher/(original) - Avalonia:
Flow.Launcher.Avalonia/(migration ~35-40%) - .NET 9.0 targeting
net9.0-windows10.0.19041.0 - CommunityToolkit.Mvvm for MVVM
See AVALONIA_MIGRATION_CHECKLIST.md for migration progress.
Commands
# Build
dotnet build
dotnet build -c Release
nuget restore
# Test (NUnit 4.x)
dotnet test
dotnet test --filter "FullyQualifiedName~FuzzyMatcherTest"
dotnet test --filter "Name~WhenSearching"
# Run
./Output/Debug/Flow.Launcher.exe # WPF
./Output/Debug/Avalonia/Flow.Launcher.Avalonia.exe # Avalonia
Code Style
Naming
- PascalCase: types, public members, methods, properties
- camelCase: locals, parameters
- _camelCase: private fields (underscore prefix)
- PascalCase: constants (per
.editorconfig) - No
this.qualifier
C# Conventions
// File-scoped namespaces
namespace Flow.Launcher.ViewModel;
// Prefer var when type is apparent
var results = new List<Result>();
// Allman braces, always required
if (condition)
{
DoSomething();
}
// 4-space indent (code), 2-space (XML/XAML)
Imports
- Sort system directives first (
dotnet_sort_system_directives_first = true) - Using placement: outside namespace
Error Handling
- Use nullable reference types (enabled in Avalonia project)
- Prefer
is nullchecks over reference equality - Use null propagation and coalesce expressions
XAML (XamlStyler)
- One attribute per line (except ≤2)
- Space before closing slash:
<Element /> - Attribute order:
x:Class→xmlns→x:Key/Name→ layout → size → margin/padding → others
WPF: .xaml with xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Avalonia: .axaml with xmlns="https://github.com/avaloniaui"
MVVM Patterns
// Avalonia: CommunityToolkit.Mvvm
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
private string _queryText = string.Empty;
[RelayCommand]
private void Search() { }
}
// WPF: BaseModel with manual INPC
public class MainViewModel : BaseModel
{
public string QueryText
{
get => _queryText;
set
{
if (_queryText != value)
{
_queryText = value;
OnPropertyChanged();
}
}
}
}
Plugin Architecture
public interface IAsyncPlugin
{
Task<List<Result>> QueryAsync(Query query, CancellationToken token);
Task InitAsync(PluginInitContext context);
}
// Result creation
new Result
{
Title = "Title",
SubTitle = "Subtitle",
IcoPath = "Images/icon.png",
Score = 100,
Action = context => true // return true to hide window
};
Avalonia Migration Notes
| WPF | Avalonia |
|---|---|
.xaml |
.axaml |
Visibility |
IsVisible |
Collapsed |
Not available |
BoolToVisibilityConverter |
Direct bool binding |
xmlns:microsoft |
xmlns:avaloniaui |
Use #if AVALONIA for conditional compilation.
Testing
[TestFixture]
public class FuzzyMatcherTest
{
[Test]
public void WhenSearching_ThenReturnsExpectedResults()
{
var matcher = new StringMatcher(null);
var result = matcher.FuzzyMatch("chr", "Chrome");
ClassicAssert.IsTrue(result.RawScore > 0);
}
}
Key Files
| File | Purpose |
|---|---|
MainViewModel.cs |
Search window logic |
ResultsViewModel.cs |
Results management |
Settings.cs |
User settings |
PluginManager.cs |
Plugin lifecycle |
StringMatcher.cs |
Fuzzy search |
IPublicAPI.cs |
Plugin API |
Gotchas
- Build order matters - plugins must build before WPF
- Build kills running
Flow.Launcher.exeautomatically - Plugins run in separate app domains
- Settings auto-save via Fody PropertyChanged
- Some tests require Windows Search service (
WSearch) - Avalonia has
<Nullable>enable</Nullable>, WPF does not
Resources
.editorconfig- C#/VB style rulesSettings.XamlStyler- XAML formattingFlow.Launcher.Plugin/README.md- Plugin SDK docs