# Phase 5: Blazor Hybrid Host + Theme + Localization **Status:** ⬜ Not Started **Parent plan:** [PLAN.md](./PLAN.md) **Domain:** frontend **Implementer:** Opus + frontend-design skill ## Objective Create the WPF + BlazorWebView host that loads `Marathon.UI` (Razor Class Library), establish the design system / theme using MudBlazor, set up bilingual (RU/EN) localization end-to-end, and wire up DI to compose Application + Infrastructure layers. ## Tasks - [ ] In `src/Marathon.Hosts.WpfBlazor/Marathon.Hosts.WpfBlazor.csproj`: - Set `true`, `false` - SDK: `Microsoft.NET.Sdk.Razor` (so Razor + WPF interop works) - Add packages: - `Microsoft.AspNetCore.Components.WebView.Wpf` - `MudBlazor` - `Microsoft.Extensions.Hosting` - `Serilog.Extensions.Hosting` - `Serilog.Sinks.File` - `Serilog.Sinks.Console` - [ ] In `src/Marathon.UI/Marathon.UI.csproj`: - SDK: `Microsoft.NET.Sdk.Razor` - `net8.0` with WebView for Razor Components - Add `MudBlazor` (so components in this RCL can use MudBlazor) - [ ] Create `Marathon.UI/_Imports.razor` with namespace and component imports (Microsoft.AspNetCore.Components.*, MudBlazor, project namespaces). - [ ] Create `Marathon.UI/wwwroot/index.html` (Blazor host HTML for the WebView). - [ ] Create `Marathon.UI/MainLayout.razor` with MudBlazor `MudLayout` + `MudAppBar` + `MudDrawer` navigation. Include locale switcher (RU/EN) in the AppBar. - [ ] Create `Marathon.UI/Pages/Home.razor` placeholder dashboard. - [ ] Create `Marathon.UI/Pages/Settings.razor` — bound to all `appsettings.json` options (ScrapingOptions, WorkerOptions, StorageOptions, AnomalyOptions, LocalizationOptions). Live save via `IOptionsMonitor` + writing back to `appsettings.Local.json`. - [ ] Establish theme tokens in `Marathon.UI/Theme/MarathonTheme.cs` — distinctive palette per frontend-design guidance, NOT generic AI-default. Include: - Primary, secondary, accent - Surface tones for light + dark mode - Typography stack (RU-friendly font for Cyrillic — e.g., Inter or Manrope which have full Cyrillic coverage) - Spacing scale, radius scale, shadow scale as CSS variables in a `app.css` - [ ] Wire MudBlazor theme via `MudThemeProvider` in `MainLayout.razor`. - [ ] Localization: - Add `Microsoft.Extensions.Localization` to `Marathon.UI` - Create `Marathon.UI/Resources/SharedResource.cs` (marker class for `IStringLocalizer`) - Add `Marathon.UI/Resources/SharedResource.ru.resx` and `SharedResource.en.resx` with all UI strings used in this phase + placeholders for later phases - Configure supported cultures in host: `ru-RU`, `en-US` - Locale switcher persists choice to `appsettings.Local.json` and reloads UI - [ ] In `src/Marathon.Hosts.WpfBlazor/MainWindow.xaml`: - Single `BlazorWebView` filling the window - `HostPage="wwwroot/index.html"` - `RootComponents` add `` - [ ] In `src/Marathon.Hosts.WpfBlazor/App.xaml.cs`: - Build `IHost` via `Host.CreateApplicationBuilder()` - Call `services.AddMarathonInfrastructure(config)` - Call `services.AddMarathonApplication(config)` - Call `services.AddWpfBlazorWebView()` - Add MudBlazor: `services.AddMudServices()` - Configure Serilog (rolling file at `./logs/marathon-.log`, console) - Start the host on `OnStartup`, stop on `OnExit` - [ ] Add `appsettings.json` to `Marathon.Hosts.WpfBlazor/` (move from Phase 3 if placed there) with all sections. Add `appsettings.Development.json` template. - [ ] Tests in `Marathon.UI.Tests` (using bUnit): - Test: `MainLayout` renders without errors - Test: locale switcher changes culture - Test: theme tokens are applied (CSS variables present in DOM) ## Files to Modify/Create - `src/Marathon.UI/_Imports.razor` - `src/Marathon.UI/MainLayout.razor` - `src/Marathon.UI/Pages/Home.razor`, `Pages/Settings.razor` - `src/Marathon.UI/Theme/MarathonTheme.cs`, `Theme/app.css` - `src/Marathon.UI/wwwroot/index.html` - `src/Marathon.UI/Resources/SharedResource.{cs,ru.resx,en.resx}` - `src/Marathon.UI/Components/LocaleSwitcher.razor` - `src/Marathon.Hosts.WpfBlazor/App.xaml`, `App.xaml.cs` - `src/Marathon.Hosts.WpfBlazor/MainWindow.xaml`, `MainWindow.xaml.cs` - `src/Marathon.Hosts.WpfBlazor/appsettings.json`, `appsettings.Development.json` - `src/Marathon.Hosts.WpfBlazor/Properties/AssemblyInfo.cs` - `tests/Marathon.UI.Tests/MainLayoutTests.cs`, `LocaleSwitcherTests.cs` ## Acceptance Criteria - Host project compiles (Big Bang smoke check). - `Marathon.UI` is a clean RCL — usable from any host (verifies portability). - Theme is distinct, not generic — implementer should follow `frontend-design` skill guidance for typography, color, motion, spatial composition. - Locale switcher works (toggles between RU and EN strings on the same page). - Settings page surfaces every configurable parameter from `appsettings.json`. ## Notes - This phase is parallelizable with Phases 2 and 3 (only depends on Phase 1 Domain, but the orchestrator can run all three after Phase 1 completes). - The frontend-design skill content is provided to the agent in `FRONTEND_DESIGN_SKILL` context block. Follow it precisely. - Use Cyrillic-friendly fonts (Inter, Manrope, IBM Plex Sans, JetBrains Mono). - For BlazorWebView in WPF, the project SDK MUST be `Microsoft.NET.Sdk.Razor` and the OutputType set to `WinExe` with WPF enabled. ## Review Checklist - [ ] Compiles - [ ] `Marathon.UI` references no host-specific code (BlazorWebView, WPF) - [ ] Theme not generic — distinctive palette + typography - [ ] All `appsettings.json` keys reachable via the Settings page - [ ] RU + EN both renderable (placeholder strings ok for later phases) - [ ] Accessibility: keyboard navigation in nav drawer, focus indicators ## Handoff to Next Phase