Skip to content

The app

AvalloyApp is the base Application. It owns the bootstrap so the app class is a list of answers to hooks rather than a page of wiring.

What it does on startup

The splash path is opt-in. With no CreateSplashWindow override the launch is synchronous.

The hooks

OverrideWhat it answers
ConfigureServices(IServiceCollection)Your services and commands. Every CommandRegistry entry is also registered as a singleton so the dispatcher can resolve it cheaply.
CreateMainWindow()The main window. Published on ShellHost for dialog parenting.
BuildKeymap()Command name to gestures. Mod+ means Cmd on macOS and Ctrl elsewhere.
ResolveThemeController()The theme engine. Applied at startup and re-applied on every OS accent or light/dark change.
GetThemeAppearance()The IAppearanceModel the engine reads, typically your embedded AppearancePrefs.
ResolveAccentPublisher()Optional. Publishes the accent as {Prefix}AccentBrush resources for XAML to bind.
ConfigureLogging(ILoggingBuilder)Add providers. The in-app buffer, console, debug and rolling file are already there.
ConfigureAppConfiguration(IConfigurationBuilder)Extra configuration sources.
DockIconAvaresUriThe macOS Dock and Cmd-Tab icon, so it shows during dotnet run too.
AppDataFolderNameWhere per-app state lives under the platform data folder.
AppIconPackSources, DefaultIconPackIdYour bundled icon packs; the default is lucide.
CreateSplashWindow(), PrepareLaunchAsync(progress), LaunchLanded(progress)The deferred launch.
KeepRunningWhenClosedClose-to-background: the window hides and relaunching the app re-shows it.
HandleDispatcherExceptionsCatch unhandled exceptions on the UI thread instead of crashing.
SentryEnabled, ConfigureSentrySentry is off until you say otherwise.

Two members are for the app to call: RequestQuit() when quitting must go through the close-to-background guard, and ReapplyTheme() after a preference change.

ShellAvalloyApp

ShellAvalloyApp is AvalloyApp with the batteries in. It registers the themed DialogService and maps a ResolveThemeService() hook onto the engine-agnostic seam, so an app on Core's own preset cascade overrides one method and returns its ThemeService. An app on the native engine, or one bringing another engine, derives from AvalloyApp and answers ResolveThemeController itself.

One instance

SingleInstanceGuard makes the first process the primary and holds an exclusive lock file for its lifetime. A later process fails to acquire, signals the primary over a named pipe to come to the foreground, and exits. The OS releases the lock on exit, crash included, so there is never a stale one. Both the lock and the pipe are namespaced per user.

AppRelaunch is the quit-and-come-back for settings that only take effect at boot. It cannot simply spawn its replacement, because the new process would find the lock still held. So a helper outlives the app, waits for the lock to release, and only then launches.

The login-shell PATH

A GUI app launched from the Dock, Spotlight or a desktop launcher inherits the minimal system PATH, because the login shell's rc files never run. Any bare-name tool the app spawns later works from a terminal and comes up not found from the packaged app. LoginShellPath runs the user's real login shell once at startup, under a four-second box through CliRunner, and merges its PATH into the process environment. Every later child inherits it. On by default through ResolveLoginShellPath; a no-op on Windows.

Logging

AlloyLogging is the static accessor for code that is not DI-resolved. Four providers are wired by default: LogBufferProvider first, so the in-app log viewer sees startup logging; console and debug; and FileLoggerProvider, a rolling file written synchronously and flushed per entry, because a background queue would drop exactly the entry before a crash. Avalonia's own internal log, the binding errors it otherwise swallows, is routed in through AvaloniaLogSink, and System.Diagnostics.Trace through the trace bridge.

Released under the MIT License.