Overhaul frontend rewrite documentation

This commit is contained in:
2026-05-04 20:04:40 +02:00
parent 8d08b857ab
commit a7f6163c4b
2 changed files with 249 additions and 138 deletions

View File

@@ -1,10 +1,12 @@
# RpgRoller
RpgRoller is an ASP.NET Core + Blazor Server app for lightweight tabletop campaign play, character sheets, and dice workflows.
RpgRoller is an ASP.NET Core and Blazor Server app for lightweight tabletop campaign play, character sheets, and dice workflows.
- `RpgRoller/`: web app, API endpoints, domain model, EF Core persistence, Blazor components, and static assets
- `RpgRoller.Tests/`: xUnit coverage for API behavior, services, hosting, payload budgets, and persistence/migration paths
- `RpgRoller.Tests/`: xUnit coverage for API behavior, services, hosting, payload budgets, and persistence and migration paths
- `RpgRoller.sln`: solution used by local development and repo scripts
- `POSTMORTEM.md`: architecture analysis of the May 2026 Firefox and RoboForm failure in the authenticated workspace
- `TASKS.md`: the current execution plan for the approved frontend routing rewrite
Test layout:
@@ -16,41 +18,44 @@ Test layout:
Backend:
- `RpgRoller/Program.cs`: app bootstrap, JSON options, compression, API/component mapping, and optional `PathBase`
- `RpgRoller/Program.cs`: app bootstrap, JSON options, compression, API and component mapping, and optional `PathBase`
- `RpgRoller/Hosting/`: service registration, startup initialization, SQLite path resolution, and schema upgrades
- `RpgRoller/Api/`: minimal API endpoint groups, request mappings, cookie/session helpers, and result mapping
- `RpgRoller/Api/`: minimal API endpoint groups, request mappings, cookie and session helpers, and result mapping
- `RpgRoller/Services/`: gameplay and account workflows behind `IGameService`
- `RpgRoller/Services/GameService.cs`: facade over composed domain services
- `RpgRoller/Services/GameAuthService.cs`: registration, login, logout, session lookup, and `GetMe`
- `RpgRoller/Services/GameCampaignService.cs`: campaign creation, listing, roster reads, campaign options, and deletion
- `RpgRoller/Services/GameCharacterService.cs`: character creation, updates, activation, deletion, transfer, and owner-scoped reads
- `RpgRoller/Services/GameSkillService.cs`: skill-group CRUD, skill CRUD, sheet shaping, and ruleset validation
- `RpgRoller/Services/GameRollService.cs`: skill/custom rolls, compact log pages, roll detail, and campaign state snapshots
- `RpgRoller/Services/GameRollService.cs`: skill and custom rolls, compact log pages, roll detail, and campaign state snapshots
- `RpgRoller/Services/GameUserAdministrationService.cs`: username reads, admin user listing, role updates, and account deletion
- `RpgRoller/Services/GameStateStore.cs`, `GameStateCloneFactory.cs`, and `GamePersistenceService.cs`: in-memory runtime state, campaign-state version tracking, and SQLite load/save boundaries
- `RpgRoller/Services/GameAuthorization.cs`, `GameContextResolver.cs`, and `GameDtoMapper.cs`: shared authorization, session/campaign resolution, and backend read-model mapping
- `RpgRoller/Services/GameStateStore.cs`, `GameStateCloneFactory.cs`, and `GamePersistenceService.cs`: in-memory runtime state, campaign-state version tracking, and SQLite load and save boundaries
- `RpgRoller/Services/GameAuthorization.cs`, `GameContextResolver.cs`, and `GameDtoMapper.cs`: shared authorization, session and campaign resolution, and backend read-model mapping
- `RpgRoller/Services/RollEngine.cs`, `StandardRollEngine.cs`, `D6RollEngine.cs`, `RolemasterRollEngine.cs`, `RollBreakdownFormatter.cs`, and `CampaignLogSummaryBuilder.cs`: ruleset-specific dice execution, breakdown formatting, and compact campaign-log summaries
- `RpgRoller/Services/SkillDefinitionValidator.cs`, `RoleSerializer.cs`, `RollVisibilityParser.cs`, and `CustomRollOptionsResolver.cs`: shared rules and parsing helpers
Frontend:
- `RpgRoller/Components/`: Blazor app shell, routes, layout, page components, and query/client helpers
- `RpgRoller/Components/Pages/Home.razor`: gateway that switches between loading, auth, and authenticated workspace views, and force-reloads after login so the authenticated play workspace is built from the fresh session cookie
- `RpgRoller/Components/Pages/Home.razor.cs`: gateway/session orchestration for `Home`
- `RpgRoller/Components/Pages/Workspace.razor`: authenticated workspace UI
- `RpgRoller/Components/Pages/Workspace.razor.cs`: workspace composition root, coordinator wiring, lifecycle, and JS-invokable entry points
- `RpgRoller/Components/App.razor`: current HTML shell and the request-time branch that decides whether `/` serves the static auth page or the interactive app
- `RpgRoller/Components/Routes.razor`: Blazor router and layout hookup
- `RpgRoller/Components/Layout/MainLayout.razor`: default layout
- `RpgRoller/Components/Pages/Home.razor`: current root route component for `/`; it only renders `Workspace`
- `RpgRoller/Components/Pages/Home.razor.cs`: logout navigation helper that force-loads `/` and carries auth status query text
- `RpgRoller/Components/Pages/Workspace.razor`: authenticated workspace UI with play, campaign management, admin, toasts, and modals
- `RpgRoller/Components/Pages/Workspace.razor.cs`: workspace composition root, lifecycle, coordinator wiring, JS-invokable entry points, and menu item construction
- `RpgRoller/Components/Pages/WorkspaceState.cs`: workspace UI state plus pure computed and formatting projections used directly by the Razor view
- `RpgRoller/Components/Pages/WorkspaceSessionCoordinator.cs`, `WorkspaceCampaignCoordinator.cs`, `WorkspaceCampaignScopeCoordinator.cs`, `WorkspacePlayCoordinator.cs`, `WorkspaceAdminCoordinator.cs`, `WorkspaceLiveStateController.cs`, `WorkspaceFeedbackService.cs`, and `WorkspaceToast.cs`: session/bootstrap, campaign scope, play/log, admin, live update, and toast concerns used by `Workspace`
- `RpgRoller/Components/Pages/HomeControls/`: workspace and auth child components, forms, header, panels, and modal controls
- `RpgRoller/Components/Pages/WorkspaceSessionCoordinator.cs`, `WorkspaceCampaignCoordinator.cs`, `WorkspaceCampaignScopeCoordinator.cs`, `WorkspacePlayCoordinator.cs`, `WorkspaceAdminCoordinator.cs`, `WorkspaceLiveStateController.cs`, `WorkspaceFeedbackService.cs`, and `WorkspaceToast.cs`: session bootstrap, campaign scope, play and log, admin, live update, and toast concerns used by `Workspace`
- `RpgRoller/Components/Pages/HomeControls/StaticAuthPage.razor`: plain HTML login and registration page used when `/` is requested without a valid session
- `RpgRoller/Components/Pages/HomeControls/`: workspace child components, forms, header, panels, and modal controls
- `RpgRoller/Components/RpgRollerApiClient.cs`: browser API client for write actions
- `RpgRoller/Components/WorkspaceQueryService.cs`: server-side read model access for workspace data
- `RpgRoller/wwwroot/js/rpgroller-api.js`: browser interop for session storage, SSE wiring, and DOM helpers
- `RpgRoller/Components/WorkspaceQueryService.cs`: browser-facing read client for workspace data
- `RpgRoller/wwwroot/js/rpgroller-api.js`: browser interop for auth forms, session storage, SSE wiring, and DOM helpers
- `RpgRoller/wwwroot/styles.css`: app styling and responsive layout
Current repo note:
- `TASKS.md` records the completed decomposition work and the final execution notes for this refactor.
- This README describes the code as it exists today. It does not treat blueprint items in `TASKS.md` as finished unless they are already present in the repo.
- `POSTMORTEM.md` documents why the current authenticated workspace architecture is fragile and why the next major frontend change is a route-first rewrite of the authenticated shell.
- `TASKS.md` is the authoritative execution plan for that rewrite and must be kept current while the work proceeds.
## Runtime and Persistence
@@ -66,10 +71,10 @@ Current repo note:
- Supported campaign rulesets: D6 System, D&D 5e, and Rolemaster
- Account registration, login, session-based auth, and role-aware authorization
- Admin tools for user listing, role updates, account deletion, and direct SQLite database download
- Campaign creation, roster reads, participant-scoped visibility, and owner/admin deletion
- Character creation, activation, owner transfer, campaign reassignment or unlinking, and owner/admin deletion
- Campaign creation, roster reads, participant-scoped visibility, and owner and admin deletion
- Character creation, activation, owner transfer, campaign reassignment or unlinking, and owner and admin deletion
- Skill groups with reusable defaults plus skill and skill-group create, edit, reassign, and delete flows
- Owner-scoped play workspace that lists only the current user's characters while preserving GM/admin management capabilities
- Owner-scoped play workspace that lists only the current user's characters while preserving GM and admin management capabilities
- Campaign log paging, lazy-loaded roll detail, compact summaries, and live state refresh through SSE
- Custom roll submission from the play screen without creating a persisted skill
- Instant skill filtering in the character panel
@@ -87,6 +92,37 @@ Rolemaster support:
- Open-ended high chaining and low-end subtraction with ordered die metadata in roll detail
- Compact log badges and summaries for open-ended, retry, and fumble-related events, including `Retry +5` and `Retry +10`
## Current Frontend Architecture
The current frontend is in an intermediate state that was created while mitigating the Firefox and RoboForm failure documented in `POSTMORTEM.md`.
Today, `/` is dual-purpose:
- when the request has no valid session cookie, `RpgRoller/Components/App.razor` renders `StaticAuthPage.razor` as plain HTML and `RpgRoller/wwwroot/js/rpgroller-api.js` handles login and registration through `fetch`
- when the request has a valid session cookie, `App.razor` renders the interactive Blazor app and `Home.razor` loads the authenticated `Workspace`
Inside the authenticated app, the hamburger menu does not navigate to different URLs. Instead, `WorkspaceSessionCoordinator.cs` stores a `screen` preference in `sessionStorage`, and `Workspace.razor` conditionally swaps between play, campaign management, and admin screens inside one large component tree.
This architecture works functionally but remains structurally fragile because:
- the root shell still branches on request-time `HttpContext`
- the authenticated workspace still performs staged startup in `OnAfterRenderAsync`
- the app coordinates state across Blazor component state, browser `sessionStorage`, `fetch`, and SSE during early startup
- the route URL does not represent the authenticated screen the user is actually viewing
## Approved Rewrite Direction
The approved remediation direction is a route-first authenticated shell:
- `/` becomes an auth-aware entry point that redirects to `/login` or `/play`
- `/login` hosts the anonymous auth experience
- `/play`, `/campaigns`, and `/admin` become real authenticated routes
- the hamburger menu becomes route navigation instead of in-memory screen switching
- SSE and heavy play bootstrap stay scoped to `/play`
- the large `Workspace` component is split so each route owns a smaller, more stable subtree
This rewrite is not complete yet. Follow `TASKS.md` for the execution plan.
## Local Development
Prerequisites:
@@ -110,6 +146,7 @@ Run locally:
dotnet run --project RpgRoller/RpgRoller.csproj
```
2. Open `http://localhost:5000` or the URL printed in the console.
3. Expect the current app to show either the static auth page at `/` or the authenticated workspace at `/`, depending on whether a valid session cookie already exists.
Playwright helpers:
@@ -145,14 +182,14 @@ SQLite migration rule:
## Frontend Runtime
- The UI runs as Blazor Server with interactive components.
- The UI currently runs as Blazor Server with interactive components for the authenticated workspace and a plain HTML plus JavaScript auth page for anonymous users at `/`.
- Static assets are linked through Blazor's `@Assets[...]` pipeline for fingerprinted cache-busting URLs.
- Workspace reads are resolved server-side through `WorkspaceQueryService`; browser interop stays limited to browser-only concerns.
- Workspace reads are resolved through API requests in `WorkspaceQueryService`; browser interop stays focused on auth forms, session storage, SSE wiring, and small DOM helpers.
- Live workspace refresh compares separate roster, per-character sheet, and log versions so unrelated changes do not trigger full reloads.
- Campaign log data is loaded in bounded slices: campaign summaries, one selected roster, one selected character sheet, and a 25-row incremental log window from `/api/campaigns/{campaignId}/log/page`.
- Log rows return compact summary data first and lazy-load full detail from `/api/rolls/{rollId}` when expanded.
- Newly appended local rolls auto-expand in the play workspace and reuse the roll response as the initial detail payload.
- Custom roll submission uses the selected character context; D6 uses baseline wild-die/fumble behavior, while D&D 5e and Rolemaster use the submitted expression directly.
- Custom roll submission uses the selected character context; D6 uses baseline wild-die and fumble behavior, while D&D 5e and Rolemaster use the submitted expression directly.
- API JSON contracts use the source-generated `RpgRollerJsonSerializerContext`.
- HTTP JSON responses are gzip-compressed when the client advertises support.
- The OpenAPI contract source lives at `openapi/RpgRoller.json`.