From 6fe99d0cd0b1e868178b2e5d91d0a11eedd5e048 Mon Sep 17 00:00:00 2001 From: TekH Date: Thu, 18 Jun 2026 12:50:45 +0200 Subject: [PATCH] docs: add culture management migration warnings for Server/Auto Prepare for Blazor Server/Auto migration by documenting current WASM-specific culture initialization approach and providing detailed migration checklist in COPILOT_CONTEXT.md. --- COPILOT_CONTEXT.md | 105 +++++++++++++++++++----- EnvelopeGenerator.ReceiverUI/Program.cs | 20 ++++- 2 files changed, 104 insertions(+), 21 deletions(-) diff --git a/COPILOT_CONTEXT.md b/COPILOT_CONTEXT.md index babb02d6..a683bdd1 100644 --- a/COPILOT_CONTEXT.md +++ b/COPILOT_CONTEXT.md @@ -1,4 +1,4 @@ -# EnvelopeGenerator — AI Context Reference +# EnvelopeGenerator β€” AI Context Reference ## Purpose Digital document signing system with **unified Blazor WASM frontend** for both Senders and Receivers. Senders create envelopes and place signature fields. Receivers view PDFs, sign documents, export stamped PDFs. @@ -65,8 +65,8 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM) - **Backend:** `EnvelopeGenerator.API` ### Current Architecture -- **Unified Frontend:** `EnvelopeGenerator.ReceiverUI` (Blazor WASM) — **Both Senders & Receivers** -- **Backend:** `EnvelopeGenerator.API` — **Both Senders & Receivers** +- **Unified Frontend:** `EnvelopeGenerator.ReceiverUI` (Blazor WASM) β€” **Both Senders & Receivers** +- **Backend:** `EnvelopeGenerator.API` β€” **Both Senders & Receivers** - **Libraries:** DevExpress + PDF.js - **PSPDFKit:** **REMOVED** @@ -88,15 +88,80 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM) --- +## Localization & Culture Management + +**Current Architecture:** Blazor WebAssembly (client-side culture management) + +### Implementation Details + +**Culture Storage:** +- Culture preference stored in browser's `localStorage` (key: `AppCulture`) +- Managed by `CultureService.cs` (ReceiverUI/Services) +- Supported cultures: `de-DE`, `en-US`, `fr-FR` + +**Culture Initialization:** +- **Location:** `Program.cs` (lines 53-57) +- Sets `CultureInfo.DefaultThreadCurrentCulture/UICulture` **before** app runs +- **WASM-Safe:** Each user has isolated browser instance + +**Language Selector:** +- **Component:** `LanguageSelector.razor` (ReceiverUI/Shared) +- Displays flag icon + language name +- Changes culture via `CultureService.SetCultureAsync()` +- Navigates with `forceLoad: false` (smooth transition, no page reload) + +### ⚠️ MIGRATION WARNING: Blazor Server/Auto + +**Current approach is WASM-specific and will break in Server/Auto render modes!** + +**Why it breaks:** +- `Program.cs:53-57` sets **global** `DefaultThreadCurrentCulture` +- In Server/Auto, one app instance serves **all users** +- User A selects German β†’ User B sees German too (shared state) +- Thread-safety issues and culture conflicts + +**Migration Checklist (when moving to Server/Auto):** + +1. **Remove global culture initialization** from `Program.cs` (lines 53-57) + - See detailed warning comment in the code + +2. **Add RequestLocalizationMiddleware** (Server-side approach): + ```csharp + app.UseRequestLocalization(options => { + options.SupportedCultures = new[] { "de-DE", "en-US", "fr-FR" }; + options.SupportedUICultures = options.SupportedCultures; + options.RequestCultureProviders.Insert(0, new CookieRequestCultureProvider()); + }); + ``` + +3. **OR** Use **per-circuit culture** (Blazor Server approach): + - Store culture in circuit-scoped service + - Use `CascadingParameter` to distribute to components + - See: https://learn.microsoft.com/aspnet/core/blazor/globalization-localization + +4. **Update `LanguageSelector.razor`:** + - Remove manual `CultureInfo.DefaultThreadCurrentCulture` assignment + - Use middleware/circuit culture provider instead + +5. **Update `CultureService.cs`:** + - Integrate with Server-side culture provider + - May need to store in cookies instead of localStorage + +**References:** +- Microsoft Docs: [Blazor Globalization/Localization](https://learn.microsoft.com/aspnet/core/blazor/globalization-localization) +- Current implementation: `Program.cs`, `CultureService.cs`, `LanguageSelector.razor` + +--- + ## Key Files & Routes | File | Route/Purpose | |---|---| -| `ReceiverUI/Pages/Index.razor` | `/` — Application entry point (landing page). | -| `ReceiverUI/Pages/EnvelopeSenderPage.razor` | `/sender` — Sender dashboard (envelope list). | -| `ReceiverUI/Pages/EnvelopeReceiverPage.razor` | `/envelope/{key}` — Receiver PDF viewer & signing. | -| `ReceiverUI/Pages/LoginSenderPage.razor` | `/sender/login` — Sender username/password auth. | -| `ReceiverUI/Pages/LoginReceiverPage.razor` | `/envelope/login/{EnvelopeKey}` — Receiver access code auth. | +| `ReceiverUI/Pages/Index.razor` | `/` β€” Application entry point (landing page). | +| `ReceiverUI/Pages/EnvelopeSenderPage.razor` | `/sender` β€” Sender dashboard (envelope list). | +| `ReceiverUI/Pages/EnvelopeReceiverPage.razor` | `/envelope/{key}` β€” Receiver PDF viewer & signing. | +| `ReceiverUI/Pages/LoginSenderPage.razor` | `/sender/login` β€” Sender username/password auth. | +| `ReceiverUI/Pages/LoginReceiverPage.razor` | `/envelope/login/{EnvelopeKey}` β€” Receiver access code auth. | | `ReceiverUI/wwwroot/js/pdf-viewer.js` | PDF.js wrapper (zoom, pagination, thumbnails). | | `ReceiverUI/wwwroot/js/receiver-signature.js` | Signature pad (draw/type/image). | | `ReceiverUI/wwwroot/css/envelope-viewer.css` | EnvelopeViewer styles. | @@ -106,7 +171,7 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM) --- -## Coordinate System — CRITICAL +## Coordinate System β€” CRITICAL **Database Format:** INCHES (GdPicture14 native) **Origin:** Top-left corner @@ -135,7 +200,7 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM) --- -## EnvelopeReceiver — PDF.js Viewer & Signing +## EnvelopeReceiver β€” PDF.js Viewer & Signing **Route:** `/envelope/{EnvelopeKey}` **Tech:** PDF.js 3.11.174 + Blazor WASM + configurable quality @@ -178,7 +243,7 @@ window.pdfViewer = { --- -## Signature Workflow — EnvelopeReceiver +## Signature Workflow β€” EnvelopeReceiver **IMPORTANT:** iText7 NOT used (GPL license issue). Client-side overlay system only. @@ -231,9 +296,9 @@ public sealed record SignatureCaptureDto { ### API Endpoints **Controller:** `API/Controllers/CacheController.cs` -- `POST /api/Cache/SignatureCapture/{envelopeKey}` — Save -- `GET /api/Cache/SignatureCapture/{envelopeKey}` — Load -- `DELETE /api/Cache/SignatureCapture/{envelopeKey}` — Delete +- `POST /api/Cache/SignatureCapture/{envelopeKey}` β€” Save +- `GET /api/Cache/SignatureCapture/{envelopeKey}` β€” Load +- `DELETE /api/Cache/SignatureCapture/{envelopeKey}` β€” Delete **Cache Key Format:** ``` @@ -299,7 +364,7 @@ public async Task LoginSenderAsync(string username, string pa **Response:** - `200 OK` ? Cookie set, redirect to `/sender` -- `401 Unauthorized` ? Show error: "Ungόltige Anmeldedaten" +- `401 Unauthorized` ? Show error: "UngΓΌltige Anmeldedaten" - Other ? Show error: "Serverfehler" **Cookie:** HTTP-only, Secure (HTTPS), SameSite=Strict @@ -357,7 +422,7 @@ public async Task LoginEnvelopeReceiverAsync(string key, st --- -## Mistakes History — Do NOT Repeat +## Mistakes History β€” Do NOT Repeat | Mistake | Why Wrong | |---|---| @@ -376,8 +441,8 @@ public async Task LoginEnvelopeReceiverAsync(string key, st ### Deprecated Projects **DO NOT USE:** -- `EnvelopeGenerator.Web` (Razor Pages) — Replaced by unified ReceiverUI -- PSPDFKit — Removed, use PDF.js + DevExpress instead +- `EnvelopeGenerator.Web` (Razor Pages) β€” Replaced by unified ReceiverUI +- PSPDFKit β€” Removed, use PDF.js + DevExpress instead ### Legacy Projects (VB.NET) **DO NOT TOUCH:** `EnvelopeGenerator.Service`, `EnvelopeGenerator.Form`, `EnvelopeGenerator.BBTests` @@ -402,8 +467,8 @@ Proves database uses INCHES natively. ## Quick Reference ### When working with coordinates: -1. **Database ? UI:** INCHES Χ 72 = PDF Points -2. **UI ? Display:** Points Χ scale = Pixels +1. **Database ? UI:** INCHES Γ— 72 = PDF Points +2. **UI ? Display:** Points Γ— scale = Pixels 3. **iText7 stamping:** Flip Y-axis (top-down ? bottom-up) ### When adding features: diff --git a/EnvelopeGenerator.ReceiverUI/Program.cs b/EnvelopeGenerator.ReceiverUI/Program.cs index 0ef22b94..19ac86f9 100644 --- a/EnvelopeGenerator.ReceiverUI/Program.cs +++ b/EnvelopeGenerator.ReceiverUI/Program.cs @@ -50,7 +50,25 @@ ReportStorageWebExtension.RegisterExtensionGlobal(new InMemoryReportStorageWebEx var host = builder.Build(); -// Initialize culture BEFORE running the app +// ⚠️ IMPORTANT: BLAZOR WASM-SPECIFIC CULTURE INITIALIZATION +// This approach sets DefaultThreadCurrentCulture globally, which is SAFE for WebAssembly +// because each user runs their own isolated app instance in their browser. +// +// ⚠️ TODO: REMOVE/REFACTOR WHEN MIGRATING TO BLAZOR SERVER/AUTO +// In Server/Auto render modes, this is DANGEROUS because: +// - Server runs a single shared instance for all users +// - Setting global culture affects ALL connected users simultaneously +// - Race conditions and culture conflicts will occur +// +// Migration Guide: +// - Option 1: Use RequestLocalizationMiddleware for per-request culture +// - Option 2: Use CascadingParameter with per-circuit culture state +// - See: https://learn.microsoft.com/aspnet/core/blazor/globalization-localization +// +// Related files to update on migration: +// - LanguageSelector.razor (remove manual culture setting) +// - App.razor (may need CascadingValue for culture) +// - Startup/Program.cs (add middleware) var cultureService = host.Services.GetRequiredService(); var culture = await cultureService.InitializeCultureAsync(); CultureInfo.DefaultThreadCurrentCulture = culture;