Skip to content

[Refactor] Migrate to Pure.DI - #459

Open
DearVa wants to merge 1 commit into
mainfrom
pure.di
Open

[Refactor] Migrate to Pure.DI#459
DearVa wants to merge 1 commit into
mainfrom
pure.di

Conversation

@DearVa

@DearVa DearVa commented Jul 7, 2026

Copy link
Copy Markdown
Member

Description

This PR migrates reflection-based Microsoft.Extensions.DependencyInjection to hybrid Pure.DI. This makes DI more AOT compatible, compile-time diagnosable and (maybe) better performance.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactoring (no functional changes, no api changes)
  • Documentation update
  • CI/CD or Build changes

Checklist

  • My code follows the code style of this project
  • I have performed a self-review of my own code
  • I have added XML documentation to any relat

Summary by CodeRabbit

  • New Features

    • Added centralized app startup and service wiring across platforms for more consistent initialization.
    • Introduced platform-specific support for Linux, macOS, and Windows, including better integration with chat, networking, updates, and UI services.
    • Improved cloud connectivity setup, including request handling and client configuration.
  • Bug Fixes

    • Added environment validation for Linux startup to fail fast when required display settings are missing.
    • Refined design-time app startup to work more reliably in development environments.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces Pure.DI-based dependency injection composition classes for Core, Cloud, Linux, Mac, and Windows modules, replacing the previous ServiceLocator.Build startup flow with a SetProvider(IServiceProvider) model. It adds centralized NuGet package management props files, Pure.DI build targets, and updates platform-specific Program.cs, csproj files, and MCP handler visibility accordingly.

Changes

Central package management setup

Layer / File(s) Summary
Central package version props files
Directory.Packages.props, 3rd/Directory.Packages.props, patches/Directory.Packages.props, src/Directory.Packages.props, tests/Directory.Packages.props, Everywhere.slnx, src/Build.Pure.DI.MS.targets
Adds per-directory Directory.Packages.props files enabling central package management and pinning Pure.DI/Pure.DI.MS versions, removes the root-level flag, registers new props files in the solution, and adds an MSBuild targets file wiring Pure.DI generated sources.

Pure.DI composition migration

Layer / File(s) Summary
ServiceLocator provider-setting refactor
src/Everywhere.Core/Common/ServiceLocator.cs, src/Everywhere.Core/App.axaml.cs
ServiceLocator.Build is replaced with SetProvider(IServiceProvider); keyed resolution now throws NotSupportedException; design-mode initialization builds a ServiceProvider directly.
Cross-cutting application services and provider factories
src/Everywhere.Core/DependencyInjection/ApplicationServiceCollection.cs, .../ApplicationServiceProviderFactories.cs
New ApplicationServiceCollection configures logging, HTTP clients, EF Core contexts, and alias registrations; ApplicationServiceProviderFactories resolves DialogManager/ToastHost via active IReactiveHost.
Core module composition
src/Everywhere.Core/DependencyInjection/*.cs, src/Everywhere.Core/Everywhere.Core.csproj
CoreComposition partial classes register Avalonia UI, chat, plugins, storage, settings, network, interop, strategy engine, and external boundary services; csproj adds Pure.DI package references.
Cloud module composition
src/Everywhere.Cloud/DependencyInjection/*.cs, src/Everywhere.Cloud/Everywhere.Cloud.csproj
CloudComposition/CloudServiceCollection register cloud client services, HTTP handlers, and initializer aliases; csproj adds Pure.DI dependencies.
MCP delegating handlers made top-level
src/Everywhere.Core/Chat/Plugins/Mcp/McpServiceExtension.cs
ContentLengthBufferingHandler and McpSessionExpiryHandler are moved from private nested classes to top-level public sealed classes.
Linux platform composition and bootstrap
src/Everywhere.Linux/DependencyInjection/LinuxComposition.cs, Program.cs, Interop/X11WindowBackend.cs, Everywhere.Linux.csproj
LinuxComposition wires Linux-specific services; Program.cs builds the provider via CreateServiceProvider(); X11WindowBackend validates DISPLAY/XDG_SESSION_TYPE; csproj adds Pure.DI and Cloud project reference.
Mac platform composition and bootstrap
src/Everywhere.Mac/DependencyInjection/MacComposition.cs, Program.cs, Everywhere.Mac.csproj
MacComposition wires macOS-specific services; Program.cs builds the provider via CreateServiceProvider(); csproj updates libonigwrap bundling, signing conditions, and Pure.DI references.
Windows platform composition and bootstrap
src/Everywhere.Windows/DependencyInjection/WindowsComposition.cs, Program.cs, Everywhere.Windows.csproj
WindowsComposition wires Windows-specific services; Program.cs builds the provider via CreateServiceProvider(); csproj adds NoWarn/Pure.DI package references.

Estimated code review effort: 4 (Complex) | ~75 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ProgramMain as Program.Main/MainAsync
  participant CreateServiceProvider
  participant CoreComposition
  participant CloudComposition
  participant PlatformComposition as Platform Composition (Linux/Mac/Windows)
  participant ServiceLocator

  ProgramMain->>CreateServiceProvider: invoke
  CreateServiceProvider->>CoreComposition: CreateBuilder(services)
  CreateServiceProvider->>CloudComposition: CreateBuilder(services)
  CreateServiceProvider->>PlatformComposition: CreateBuilder(services)
  CreateServiceProvider->>CreateServiceProvider: ApplicationServiceCollection.Configure/ConfigureAliases
  CreateServiceProvider->>CreateServiceProvider: services.BuildServiceProvider()
  CreateServiceProvider->>CoreComposition: assign built ServiceProvider
  CreateServiceProvider->>CloudComposition: assign built ServiceProvider
  CreateServiceProvider->>PlatformComposition: assign built ServiceProvider
  CreateServiceProvider-->>ProgramMain: return ServiceProvider
  ProgramMain->>ServiceLocator: SetProvider(serviceProvider)
  ProgramMain->>ProgramMain: BuildAvaloniaApp(serviceProvider)
Loading

Possibly related PRs

  • Sylinko/Everywhere#450: Both PRs modify Directory.Packages.props for centralized NuGet package version management, adjusting ManagePackageVersionsCentrally and package version entries in the same file.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: migrating dependency injection to Pure.DI.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pure.di

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
src/Everywhere.Core/Common/ServiceLocator.cs (1)

15-21: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Simplify Resolve: the key == null check and the trailing throw are unreachable dead code.

After line 18 (if (key != null) throw ...), key is guaranteed to be null, so the if (key == null) guard is always true and the final throw on line 20 can never execute.

♻️ Proposed simplification
     public static object Resolve(Type type, object? key = null)
     {
         if (_serviceProvider == null) throw new InvalidOperationException($"{nameof(ServiceLocator)} is not built.");
         if (key != null) throw new NotSupportedException("Keyed service resolution is not supported by the source-generated provider.");
-        if (key == null) return _serviceProvider.GetRequiredService(type);
-        throw new InvalidOperationException("Unreachable service resolution branch.");
+        return _serviceProvider.GetRequiredService(type);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Everywhere.Core/Common/ServiceLocator.cs` around lines 15 - 21, Simplify
ServiceLocator.Resolve by removing the unreachable null check and dead trailing
throw: after the key validation branch, key is guaranteed to be null, so return
the required service from _serviceProvider directly. Keep the existing
_serviceProvider null guard and the NotSupportedException for keyed resolution,
but eliminate the redundant if (key == null) branch and the final “Unreachable
service resolution branch” exception.
src/Everywhere.Core/DependencyInjection/CoreComposition.cs (1)

29-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

#pragma warning disable CA1416 scope is broader than necessary.

The disable covers the entire class, but only the #if WINDOWS-guarded RestartAsAdministratorControl root (Line 63) needs it. Consider scoping the pragma to just that line/root, or re-enabling (#pragma warning restore CA1416) after it, to avoid silently suppressing platform-compatibility diagnostics for the rest of the file.

Also applies to: 38-38, 58-58

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Everywhere.Core/DependencyInjection/CoreComposition.cs` at line 29, The
CA1416 suppression in CoreComposition is too broad and is masking
platform-compatibility warnings beyond the Windows-only root. Move the `#pragma
warning disable CA1416` so it only surrounds the `RestartAsAdministratorControl`
registration guarded by `#if WINDOWS`, and add `#pragma warning restore CA1416`
immediately after that root (or otherwise narrowly scope it) so the rest of the
`CoreComposition` class keeps normal diagnostics.
src/Everywhere.Cloud/DependencyInjection/CloudComposition.cs (1)

12-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated Hint() boilerplate across every composition class.

This exact .Hint(...) chain (lines 14-24) is repeated verbatim in LinuxComposition (and presumably CoreComposition/MacComposition/WindowsComposition). Pure.DI supports .DependsOn(Base) to share a base setup across compositions — extracting this into a common base DI.Setup(...) shared via DependsOn would remove the duplication and keep the hint list in sync across all five modules going forward.

♻️ Sketch of shared base setup
// e.g. in a shared static class
private static void SetupBase() =>
    DI.Setup("Base")
        .Hint(Hint.OnCannotResolve, "On")
        .Hint(Hint.OnCannotResolvePartial, "Off")
        .Hint(Hint.OnNewRoot, "On")
        .Hint(Hint.OnNewRootPartial, "Off")
        .Hint(Hint.OnCannotResolveContractTypeNameWildcard, "Microsoft.Extensions.*")
        .Hint(Hint.OnCannotResolveContractTypeNameWildcard, "Everywhere.*");

// in CloudComposition
private static void SetupCloudServices() =>
    DI.Setup().DependsOn("Base")
        .Bind<OAuthCloudClient>()...
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Everywhere.Cloud/DependencyInjection/CloudComposition.cs` around lines 12
- 24, The `SetupCloudServices` method in `CloudComposition` repeats the same
`Hint(...)` boilerplate used by other composition classes; extract the shared
Pure.DI setup into a common base configuration and reuse it via `DependsOn` so
the hint chain is defined once. Update `DI.Setup()` usage in
`SetupCloudServices` to depend on the shared base setup, and move the duplicated
`Hint` list into a shared helper/static setup used by `LinuxComposition`,
`CoreComposition`, `MacComposition`, and `WindowsComposition` as well.
src/Everywhere.Mac/Program.cs (1)

34-56: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Optional: the composition build/assign sequence is duplicated across platform Program.cs files.

The create-compositions → CreateBuilder → build → assign ServiceProvider sequence is repeated verbatim in Mac/Windows (and Linux). Forgetting to assign ServiceProvider on any composition would surface only as a runtime failure. Consider a small shared helper that takes the platform composition and centralizes the build + back-assignment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Everywhere.Mac/Program.cs` around lines 34 - 56, The composition setup in
CreateServiceProvider is duplicated across platform Program.cs files and can be
centralized to avoid missing the ServiceProvider back-assignment. Extract the
create-compositions → CreateBuilder → BuildServiceProvider → assign
ServiceProvider flow into a small shared helper, then have CoreComposition,
CloudComposition, and MacComposition use that helper so the build and assignment
happen consistently in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Everywhere.Core/DependencyInjection/AvaloniaServices.cs`:
- Around line 24-38: The SettingsPage registration in AvaloniaServices is
inconsistent with the other IMainViewNavigationItem page bindings because it is
missing As(Singleton). Update the SettingsPage binding to match the other
navigation item pages so the same instance is used both in the navigation
collection and when resolved as a root view, and verify the binding chain around
Bind<SettingsPage>() stays consistent with the other page registrations.

In `@src/Everywhere.Core/DependencyInjection/CoreComposition.cs`:
- Line 120: `SettingsPage` is bound inconsistently between direct root export
and the nav-item collection, which can create different instances depending on
how it is resolved. Update the `SettingsPage` registration in `AvaloniaServices`
to match the other navigation pages by using the same singleton lifetime as
`HomePage`, `CustomAssistantPage`, `PromptPage`, `ChatPluginPage`, `SkillPage`,
and `WebSearchEnginePage`, or add a clear comment in
`CoreComposition`/`AvaloniaServices` if the separate lifetime is intentional.
Reference the `Root<SettingsPage>` export and the
`Root<IEnumerable<IMainViewNavigationItem>>` aggregation when verifying the fix.

---

Nitpick comments:
In `@src/Everywhere.Cloud/DependencyInjection/CloudComposition.cs`:
- Around line 12-24: The `SetupCloudServices` method in `CloudComposition`
repeats the same `Hint(...)` boilerplate used by other composition classes;
extract the shared Pure.DI setup into a common base configuration and reuse it
via `DependsOn` so the hint chain is defined once. Update `DI.Setup()` usage in
`SetupCloudServices` to depend on the shared base setup, and move the duplicated
`Hint` list into a shared helper/static setup used by `LinuxComposition`,
`CoreComposition`, `MacComposition`, and `WindowsComposition` as well.

In `@src/Everywhere.Core/Common/ServiceLocator.cs`:
- Around line 15-21: Simplify ServiceLocator.Resolve by removing the unreachable
null check and dead trailing throw: after the key validation branch, key is
guaranteed to be null, so return the required service from _serviceProvider
directly. Keep the existing _serviceProvider null guard and the
NotSupportedException for keyed resolution, but eliminate the redundant if (key
== null) branch and the final “Unreachable service resolution branch” exception.

In `@src/Everywhere.Core/DependencyInjection/CoreComposition.cs`:
- Line 29: The CA1416 suppression in CoreComposition is too broad and is masking
platform-compatibility warnings beyond the Windows-only root. Move the `#pragma
warning disable CA1416` so it only surrounds the `RestartAsAdministratorControl`
registration guarded by `#if WINDOWS`, and add `#pragma warning restore CA1416`
immediately after that root (or otherwise narrowly scope it) so the rest of the
`CoreComposition` class keeps normal diagnostics.

In `@src/Everywhere.Mac/Program.cs`:
- Around line 34-56: The composition setup in CreateServiceProvider is
duplicated across platform Program.cs files and can be centralized to avoid
missing the ServiceProvider back-assignment. Extract the create-compositions →
CreateBuilder → BuildServiceProvider → assign ServiceProvider flow into a small
shared helper, then have CoreComposition, CloudComposition, and MacComposition
use that helper so the build and assignment happen consistently in one place.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ed9743f0-f67c-4a13-a5eb-869f454ab4fa

📥 Commits

Reviewing files that changed from the base of the PR and between 545fb98 and e121e9d.

📒 Files selected for processing (36)
  • 3rd/Directory.Packages.props
  • Directory.Packages.props
  • Everywhere.slnx
  • patches/Directory.Packages.props
  • src/Build.Pure.DI.MS.targets
  • src/Directory.Packages.props
  • src/Everywhere.Cloud/DependencyInjection/CloudComposition.cs
  • src/Everywhere.Cloud/DependencyInjection/CloudServiceCollection.cs
  • src/Everywhere.Cloud/Everywhere.Cloud.csproj
  • src/Everywhere.Core/App.axaml.cs
  • src/Everywhere.Core/Chat/Plugins/Mcp/McpServiceExtension.cs
  • src/Everywhere.Core/Common/ServiceLocator.cs
  • src/Everywhere.Core/DependencyInjection/ApplicationServiceCollection.cs
  • src/Everywhere.Core/DependencyInjection/ApplicationServiceProviderFactories.cs
  • src/Everywhere.Core/DependencyInjection/AvaloniaServices.cs
  • src/Everywhere.Core/DependencyInjection/ChatPluginServices.cs
  • src/Everywhere.Core/DependencyInjection/ChatServices.cs
  • src/Everywhere.Core/DependencyInjection/CoreComposition.cs
  • src/Everywhere.Core/DependencyInjection/ExternalBoundaryServices.cs
  • src/Everywhere.Core/DependencyInjection/InteropServices.cs
  • src/Everywhere.Core/DependencyInjection/NetworkServices.cs
  • src/Everywhere.Core/DependencyInjection/SettingsServices.cs
  • src/Everywhere.Core/DependencyInjection/StorageServices.cs
  • src/Everywhere.Core/DependencyInjection/StrategyEngineServices.cs
  • src/Everywhere.Core/Everywhere.Core.csproj
  • src/Everywhere.Linux/DependencyInjection/LinuxComposition.cs
  • src/Everywhere.Linux/Everywhere.Linux.csproj
  • src/Everywhere.Linux/Interop/X11WindowBackend.cs
  • src/Everywhere.Linux/Program.cs
  • src/Everywhere.Mac/DependencyInjection/MacComposition.cs
  • src/Everywhere.Mac/Everywhere.Mac.csproj
  • src/Everywhere.Mac/Program.cs
  • src/Everywhere.Windows/DependencyInjection/WindowsComposition.cs
  • src/Everywhere.Windows/Everywhere.Windows.csproj
  • src/Everywhere.Windows/Program.cs
  • tests/Directory.Packages.props
💤 Files with no reviewable changes (1)
  • Directory.Packages.props

Comment on lines +24 to +38
.Bind<HomePageViewModel>().As(Singleton).To<HomePageViewModel>()
.Bind<HomePage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<HomePage>()
.Bind<CustomAssistantPageViewModel>().As(Singleton).To<CustomAssistantPageViewModel>()
.Bind<CustomAssistantPage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<CustomAssistantPage>()
.Bind<PromptPageViewModel>().As(Singleton).To<PromptPageViewModel>()
.Bind<PromptPage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<PromptPage>()
.Bind<PromptEditorViewModel>().To<PromptEditorViewModel>()
.Bind<PromptEditorPage>().To<PromptEditorPage>()
.Bind<ChatPluginPageViewModel>().As(Singleton).To<ChatPluginPageViewModel>()
.Bind<ChatPluginPage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<ChatPluginPage>()
.Bind<SkillPageViewModel>().As(Singleton).To<SkillPageViewModel>()
.Bind<SkillPage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<SkillPage>()
.Bind<WebSearchEnginePageViewModel>().As(Singleton).To<WebSearchEnginePageViewModel>()
.Bind<WebSearchEnginePage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<WebSearchEnginePage>()
.Bind<SettingsPage>().Bind<IMainViewNavigationItem>(Tag.Unique).To<SettingsPage>()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

SettingsPage missing .As(Singleton).

Every other nav-item page binding in this block uses .As(Singleton) (Lines 24-37), but SettingsPage (Line 38) is bound as transient. Since SettingsPage is also exported as a standalone root in CoreComposition.cs (Line 120), this could yield a different instance than the one appearing in the IMainViewNavigationItem collection.

🔧 Proposed fix
-            .Bind<SettingsPage>().Bind<IMainViewNavigationItem>(Tag.Unique).To<SettingsPage>()
+            .Bind<SettingsPage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<SettingsPage>()
📝 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.

Suggested change
.Bind<HomePageViewModel>().As(Singleton).To<HomePageViewModel>()
.Bind<HomePage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<HomePage>()
.Bind<CustomAssistantPageViewModel>().As(Singleton).To<CustomAssistantPageViewModel>()
.Bind<CustomAssistantPage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<CustomAssistantPage>()
.Bind<PromptPageViewModel>().As(Singleton).To<PromptPageViewModel>()
.Bind<PromptPage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<PromptPage>()
.Bind<PromptEditorViewModel>().To<PromptEditorViewModel>()
.Bind<PromptEditorPage>().To<PromptEditorPage>()
.Bind<ChatPluginPageViewModel>().As(Singleton).To<ChatPluginPageViewModel>()
.Bind<ChatPluginPage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<ChatPluginPage>()
.Bind<SkillPageViewModel>().As(Singleton).To<SkillPageViewModel>()
.Bind<SkillPage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<SkillPage>()
.Bind<WebSearchEnginePageViewModel>().As(Singleton).To<WebSearchEnginePageViewModel>()
.Bind<WebSearchEnginePage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<WebSearchEnginePage>()
.Bind<SettingsPage>().Bind<IMainViewNavigationItem>(Tag.Unique).To<SettingsPage>()
.Bind<HomePageViewModel>().As(Singleton).To<HomePageViewModel>()
.Bind<HomePage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<HomePage>()
.Bind<CustomAssistantPageViewModel>().As(Singleton).To<CustomAssistantPageViewModel>()
.Bind<CustomAssistantPage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<CustomAssistantPage>()
.Bind<PromptPageViewModel>().As(Singleton).To<PromptPageViewModel>()
.Bind<PromptPage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<PromptPage>()
.Bind<PromptEditorViewModel>().To<PromptEditorViewModel>()
.Bind<PromptEditorPage>().To<PromptEditorPage>()
.Bind<ChatPluginPageViewModel>().As(Singleton).To<ChatPluginPageViewModel>()
.Bind<ChatPluginPage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<ChatPluginPage>()
.Bind<SkillPageViewModel>().As(Singleton).To<SkillPageViewModel>()
.Bind<SkillPage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<SkillPage>()
.Bind<WebSearchEnginePageViewModel>().As(Singleton).To<WebSearchEnginePageViewModel>()
.Bind<WebSearchEnginePage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<WebSearchEnginePage>()
.Bind<SettingsPage>().Bind<IMainViewNavigationItem>(Tag.Unique).As(Singleton).To<SettingsPage>()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Everywhere.Core/DependencyInjection/AvaloniaServices.cs` around lines 24
- 38, The SettingsPage registration in AvaloniaServices is inconsistent with the
other IMainViewNavigationItem page bindings because it is missing As(Singleton).
Update the SettingsPage binding to match the other navigation item pages so the
same instance is used both in the navigation collection and when resolved as a
root view, and verify the binding chain around Bind<SettingsPage>() stays
consistent with the other page registrations.

.Root<SkillPage>(kind: ExportedRoot)
.Root<WebSearchEnginePageViewModel>(kind: ExportedRoot)
.Root<WebSearchEnginePage>(kind: ExportedRoot)
.Root<SettingsPage>(kind: ExportedRoot)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Verify SettingsPage lifetime consistency with other nav items.

Root<SettingsPage> is exported here, and SettingsPage is also aggregated into Root<IEnumerable<IMainViewNavigationItem>>. In AvaloniaServices.cs (Line 38), SettingsPage is the only nav-item binding missing .As(Singleton) — every sibling page (HomePage, CustomAssistantPage, PromptPage, ChatPluginPage, SkillPage, WebSearchEnginePage) is Singleton. If this is intentional it's worth a comment; otherwise resolving SettingsPage directly vs. through the nav-item collection will produce two different instances.

Also applies to: 158-158

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Everywhere.Core/DependencyInjection/CoreComposition.cs` at line 120,
`SettingsPage` is bound inconsistently between direct root export and the
nav-item collection, which can create different instances depending on how it is
resolved. Update the `SettingsPage` registration in `AvaloniaServices` to match
the other navigation pages by using the same singleton lifetime as `HomePage`,
`CustomAssistantPage`, `PromptPage`, `ChatPluginPage`, `SkillPage`, and
`WebSearchEnginePage`, or add a clear comment in
`CoreComposition`/`AvaloniaServices` if the separate lifetime is intentional.
Reference the `Root<SettingsPage>` export and the
`Root<IEnumerable<IMainViewNavigationItem>>` aggregation when verifying the fix.

@DearVa

DearVa commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

Given the complexity of the DI in this project and its heavy reliance on MSDI, this PR will be put on hold.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant