Update documentation to reflect current architecture
Revised COPILOT_CONTEXT.md to align with the active EnvelopeGenerator architecture and workflows. Key updates: - Updated title and purpose for clarity. - Replaced migration notice with active app structure details. - Documented hosting model, including `Program.cs` setup. - Removed outdated deployment architecture section. - Reorganized route structure for WebAssembly and server pages. - Expanded authentication model for sender/receiver flows. - Added details on server-side data loading and caching. - Updated receiver PDF viewer and signature workflow sections. - Clarified coordinate system conversions and usage. - Marked deprecated projects and legacy files as "Do Not Touch." - Replaced mistakes history with workspace rules. - Updated last modified date to 2026-06-29.
This commit is contained in:
@@ -1,564 +1,351 @@
|
|||||||
# EnvelopeGenerator — AI Context Reference
|
# EnvelopeGenerator — Current Workspace Context
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
Digital document signing system with **unified Blazor Auto (Server+WASM hybrid) frontend** for both Senders and Receivers. Senders create envelopes and place signature fields. Receivers view PDFs, sign documents, export stamped PDFs.
|
Digital document signing system for senders and receivers.
|
||||||
|
|
||||||
**Primary Libraries:** DevExpress + PDF.js (PSPDFKit removed)
|
- Senders authenticate, view envelope lists, and manage envelope workflows.
|
||||||
|
- Receivers authenticate per envelope, open PDFs, create signatures, and apply them in the viewer.
|
||||||
**Receiver Architecture:**
|
- The active UI stack is `Blazor Auto` with server-side and WebAssembly render modes.
|
||||||
- Receiver authentication for `EnvelopeReceiverPage.razor` is now validated server-side.
|
- Primary UI/PDF libraries are `DevExpress` and `PDF.js`.
|
||||||
- Receiver page data is loaded directly via MediatR and distributed cache, not through the page's own API calls.
|
|
||||||
- PDF rendering in `EnvelopeReceiverPage.razor` is PDF.js-based, while `DxPdfViewer` remains the SSR-native viewer target.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Migration Notice
|
## Active Application Structure
|
||||||
|
|
||||||
**EnvelopeGenerator.ReceiverUI ? EnvelopeGenerator.WebUI Migration**
|
### Main Host
|
||||||
|
**Primary active application:** `EnvelopeGenerator.Server`
|
||||||
|
|
||||||
The project has been migrated from pure Blazor WebAssembly (`ReceiverUI`) to **Blazor Auto (Server+WASM hybrid)** architecture (`WebUI`) to resolve DevExpress `DxPdfViewer` compatibility issues.
|
`EnvelopeGenerator.Server` is the current runtime host and contains:
|
||||||
|
- Blazor server host
|
||||||
|
- WebAssembly host integration
|
||||||
|
- API controllers
|
||||||
|
- authentication/authorization setup
|
||||||
|
- Swagger/Scalar setup
|
||||||
|
- YARP reverse proxy configuration
|
||||||
|
- DevExpress server-side services
|
||||||
|
- SQL Server distributed cache setup
|
||||||
|
|
||||||
**Reason:** DevExpress `DxPdfViewer` requires backend server-side rendering services that are NOT available in pure WebAssembly projects.
|
### Client Project
|
||||||
|
**Client UI project:** `EnvelopeGenerator.Server.Client`
|
||||||
|
|
||||||
**New Structure:**
|
This project contains:
|
||||||
- **WebUI** (Server project): Hosts server-side components, YARP proxy, DevExpress backend services
|
- WebAssembly-rendered pages
|
||||||
- **WebUI.Client** (WASM project): Client-side components, business logic, services
|
- client-side services
|
||||||
|
- client models and options
|
||||||
|
- sender and receiver login flows
|
||||||
|
|
||||||
**Migration Details:** See `MIGRATION_CONTEXT.md`
|
### Other Projects
|
||||||
|
- `EnvelopeGenerator.Application` — MediatR/CQRS handlers and business logic
|
||||||
|
- `EnvelopeGenerator.Domain` — domain models, constants, shared abstractions
|
||||||
|
- `EnvelopeGenerator.Infrastructure` — EF Core and infrastructure services
|
||||||
|
- `EnvelopeGenerator.PdfEditor` — PDF-related backend utilities
|
||||||
|
- `EnvelopeGenerator.API` — still exists in the solution, but the current merged app host is `EnvelopeGenerator.Server`
|
||||||
|
|
||||||
|
### Legacy / Do Not Touch
|
||||||
|
- `EnvelopeGenerator.Service`
|
||||||
|
- `EnvelopeGenerator.Form`
|
||||||
|
- `EnvelopeGenerator.BBTests`
|
||||||
|
- `EnvelopeGenerator.CommonServices`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Deployment Architecture
|
## Current Hosting Model
|
||||||
|
|
||||||
**Two Presentation Projects (Both Required):**
|
`EnvelopeGenerator.Server/Program.cs` currently configures:
|
||||||
|
- `AddRazorComponents()` with both interactive server and interactive WebAssembly components
|
||||||
|
- `AddControllers()` and `MapControllers()`
|
||||||
|
- JWT authentication for sender and receiver flows
|
||||||
|
- cookie authentication
|
||||||
|
- authorization policies using `AuthScheme.Sender`, `AuthScheme.Receiver`, `AuthPolicy.Sender`, `AuthPolicy.Receiver`
|
||||||
|
- `AddReverseProxy()` with `yarp.json`
|
||||||
|
- Swagger / OpenAPI / Scalar
|
||||||
|
- distributed SQL Server cache
|
||||||
|
- DevExpress Blazor and DevExpress PDF Viewer server-side services
|
||||||
|
- request localization middleware
|
||||||
|
|
||||||
1. **EnvelopeGenerator.API** (ASP.NET Core Web API)
|
This means the active app is a **merged UI + API host**.
|
||||||
- Runs independently (development & production)
|
|
||||||
- Backend services for document management, authentication, signature endpoints
|
|
||||||
- Serves as API endpoint for WebUI
|
|
||||||
|
|
||||||
2. **EnvelopeGenerator.WebUI** (Blazor Auto - Server+WASM Hybrid)
|
|
||||||
- **Server Project (`EnvelopeGenerator.WebUI`):**
|
|
||||||
- **YARP Reverse Proxy** configured via `yarp.json`
|
|
||||||
- Proxies `/api/*` requests to `API:8088`
|
|
||||||
- Hosts server-side components (`@rendermode InteractiveServer`)
|
|
||||||
- DevExpress server-side services (DxPdfViewer backend)
|
|
||||||
- **Client Project (`EnvelopeGenerator.WebUI.Client`):**
|
|
||||||
- Client-side components (`@rendermode InteractiveWebAssembly`)
|
|
||||||
- Business logic services (AuthService, DocumentService, etc.)
|
|
||||||
- WASM runtime
|
|
||||||
|
|
||||||
**Request Flow:**
|
|
||||||
```
|
|
||||||
Client ? WebUI:XXXX (Blazor Auto)
|
|
||||||
?? Server-side Pages (DxPdfViewer)
|
|
||||||
?? Client-side Pages (WASM)
|
|
||||||
?? YARP Proxy: /api/* ? API:8088
|
|
||||||
```
|
|
||||||
|
|
||||||
**Configuration:** `EnvelopeGenerator.WebUI/yarp.json`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## WebUI Route Structure
|
## Reverse Proxy
|
||||||
|
|
||||||
### Root Route
|
**Config file:** `EnvelopeGenerator.Server/EnvelopeGenerator.Server/yarp.json`
|
||||||
| Route | File | Location | Render Mode |
|
|
||||||
|
Current YARP usage is focused on **AuthHub forwarding**, not a general `/api/* -> EnvelopeGenerator.API` proxy.
|
||||||
|
|
||||||
|
Configured routes forward:
|
||||||
|
- `POST /api/auth` -> AuthHub `/api/auth/sign-flow`
|
||||||
|
- `POST /api/Auth/envelope-receiver/{key}` -> AuthHub `/api/auth/envelope-receiver/{key}?cookie=true`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Active Routes and Files
|
||||||
|
|
||||||
|
### WebAssembly Pages (`EnvelopeGenerator.Server.Client`)
|
||||||
|
| Route | File | Render Mode | Purpose |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `/` | `Index.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` |
|
| `/` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/IndexPage.razor` | WebAssembly | Landing page |
|
||||||
|
| `/sender/login` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/LoginSenderPage.razor` | WebAssembly | Sender login |
|
||||||
|
| `/sender` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/EnvelopeSenderPage.razor` | WebAssembly (`prerender: false`) | Sender dashboard |
|
||||||
|
| `/envelope/login/{EnvelopeKey}` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/LoginReceiverPage.razor` | WebAssembly | Receiver login |
|
||||||
|
|
||||||
### Sender Routes
|
### Server Pages (`EnvelopeGenerator.Server`)
|
||||||
| Route | File | Location | Render Mode |
|
| Route | File | Render Mode | Purpose |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `/sender/login` | `LoginSenderPage.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` |
|
| `/envelope/{EnvelopeKey}` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor` | InteractiveServer | Main receiver PDF viewer and signing page |
|
||||||
| `/sender` | `EnvelopeSenderPage.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` |
|
| `/envelope/DxPdfViewer` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage_DxPdfViewer.razor` | InteractiveServer | DevExpress PDF Viewer test page |
|
||||||
|
| `/envelope/{EnvelopeKey}/DxReportViewer` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage_DxReportViewer.razor` | InteractiveServer | DevExpress report-based PDF rendering |
|
||||||
### Receiver Routes (PDF Viewers)
|
| `/envelope/Embed` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage_embed.razor` | InteractiveServer | Embedded browser PDF view test page |
|
||||||
| Route | File | Location | Render Mode |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `/envelope/login/{EnvelopeKey}` | `LoginReceiverPage.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` |
|
|
||||||
| `/envelope/{EnvelopeKey}` | `EnvelopeReceiverPage.razor` | `WebUI/Components/Pages/` | `@rendermode InteractiveServer` |
|
|
||||||
| `/envelope/DxPdfViewer` | `EnvelopeReceiverPage_DxPdfViewer.razor` | `WebUI/Components/Pages/` | `@rendermode InteractiveServer` |
|
|
||||||
| `/envelope/{EnvelopeKey}/DxReportViewer` | `EnvelopeReceiverPage_DxReportViewer.razor` | `WebUI/Components/Pages/` | `@rendermode InteractiveServer` |
|
|
||||||
| `/envelope/Embed` | `EnvelopeReceiverPage_embed.razor` | `WebUI/Components/Pages/` | `@rendermode InteractiveServer` |
|
|
||||||
|
|
||||||
**Multi-Envelope Support:** Receivers can login to multiple envelopes simultaneously (per-envelope cookie authentication).
|
|
||||||
|
|
||||||
**Render Mode Strategy:**
|
|
||||||
- **Client-side Pages (WASM):** Login, Sender dashboard, Index (no DevExpress backend required)
|
|
||||||
- **Server-side Pages (Server):** PDF viewers (DevExpress DxPdfViewer requires backend)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Architecture Evolution
|
## Current API Location
|
||||||
|
|
||||||
### Old Architecture (Deprecated v1)
|
The active application exposes controllers from:
|
||||||
- **Sender UI:** `EnvelopeGenerator.Web` (Razor Pages + PSPDFKit)
|
`EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers`
|
||||||
- **Receiver UI:** Separate project
|
|
||||||
- **Backend:** `EnvelopeGenerator.API`
|
|
||||||
|
|
||||||
### Intermediate Architecture (Deprecated v2)
|
Current controller set includes:
|
||||||
- **Unified Frontend:** `EnvelopeGenerator.ReceiverUI` (Pure Blazor WASM)
|
- `AnnotationController`
|
||||||
- **Backend:** `EnvelopeGenerator.API`
|
- `AuthController`
|
||||||
- **Issue:** DevExpress `DxPdfViewer` displayed blank screen (no backend services in WASM)
|
- `CacheController`
|
||||||
|
- `ConfigController`
|
||||||
|
- `DocumentController`
|
||||||
|
- `EmailTemplateController`
|
||||||
|
- `EnvelopeController`
|
||||||
|
- `EnvelopeReceiverController`
|
||||||
|
- `EnvelopeTypeController`
|
||||||
|
- `HistoryController`
|
||||||
|
- `LocalizationController`
|
||||||
|
- `ReadOnlyController`
|
||||||
|
- `ReceiverController`
|
||||||
|
- `SignatureController`
|
||||||
|
- `TfaRegistrationController`
|
||||||
|
|
||||||
### Current Architecture (Active)
|
Do not assume API behavior lives only in `EnvelopeGenerator.API`; the active merged host contains controller endpoints directly.
|
||||||
- **Frontend:** `EnvelopeGenerator.WebUI` (Blazor Auto - Server+WASM Hybrid)
|
|
||||||
- **WebUI** (Server): Server-side components, YARP proxy, DevExpress backend
|
|
||||||
- **WebUI.Client** (WASM): Client-side components, services, business logic
|
|
||||||
- **Backend:** `EnvelopeGenerator.API`
|
|
||||||
- **Libraries:** DevExpress + PDF.js
|
|
||||||
- **PSPDFKit:** **REMOVED**
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Solution Structure
|
## Authentication Model
|
||||||
|
|
||||||
| Project | Target | Purpose |
|
### Sender
|
||||||
|---|---|---|
|
Client login page uses `EnvelopeGenerator.Server.Client/Services/AuthService.cs`.
|
||||||
| `EnvelopeGenerator.API` | net8.0 | ASP.NET Core Web API. Backend for **both Senders & Receivers**. Auth, PDF serving, signature endpoints. |
|
|
||||||
| `EnvelopeGenerator.WebUI` | net8.0 | **Blazor Auto Server Project**. YARP proxy, server-side components, DevExpress backend services. |
|
Key sender endpoints:
|
||||||
| `EnvelopeGenerator.WebUI.Client` | net8.0 WASM | **Blazor Auto Client Project**. Client-side components, services, business logic. |
|
- `POST /api/auth?cookie=true` — login
|
||||||
| `EnvelopeGenerator.ReceiverUI` | net8.0 WASM | **DEPRECATED.** Pure Blazor WASM (migrated to WebUI). |
|
- `GET /api/auth/check` — current sender access check
|
||||||
| `EnvelopeGenerator.Web` | net7/8/9 | **DEPRECATED.** Legacy Razor Pages (Sender UI). No longer used. |
|
- `POST /api/auth/logout` — logout
|
||||||
| `EnvelopeGenerator.Application` | multi | MediatR CQRS handlers. Business logic. |
|
|
||||||
| `EnvelopeGenerator.Domain` | multi | Domain models, constants, interfaces. |
|
### Receiver
|
||||||
| `EnvelopeGenerator.Infrastructure` | multi | EF Core repos, DB context. |
|
Receiver authentication is **per envelope**.
|
||||||
| `EnvelopeGenerator.PdfEditor` | multi | iText7 utilities (NOT used in WebUI). |
|
|
||||||
| `EnvelopeGenerator.DependencyInjection` | multi | DI registration helpers. |
|
Key receiver endpoints used by client services:
|
||||||
| **VB.NET projects** (Service/Form/BBTests) | net462 | **Legacy. Do NOT touch.** |
|
- `POST /api/Auth/envelope-receiver/{envelopeKey}` — submit access code
|
||||||
|
- `GET /api/auth/check/envelope/{envelopeKey}` — check access
|
||||||
|
- `POST /api/auth/logout/envelope/{envelopeKey}` — logout receiver for one envelope
|
||||||
|
|
||||||
|
Receiver cookie resolution in server auth uses an envelope-specific cookie name derived from:
|
||||||
|
- `AuthTokenSignFLOWReceiver.{envelopeKey}` pattern
|
||||||
|
|
||||||
|
### Receiver Server-Side Authorization
|
||||||
|
`EnvelopeReceiverPage.razor` does **not** rely on its own API access-check call for page authorization.
|
||||||
|
|
||||||
|
It uses:
|
||||||
|
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverAuthorizationService.cs`
|
||||||
|
|
||||||
|
Behavior:
|
||||||
|
- tries the current `HttpContext.User`
|
||||||
|
- if needed, reads the per-envelope receiver cookie directly
|
||||||
|
- validates the JWT with the receiver auth scheme
|
||||||
|
- verifies the token subject matches the route envelope key
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Localization & Culture Management
|
## Receiver Page Data Loading
|
||||||
|
|
||||||
**Current Architecture:** Blazor WebAssembly (client-side culture management)
|
Main server-side page data service:
|
||||||
|
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverPageDataService.cs`
|
||||||
|
|
||||||
### Implementation Details
|
This service loads directly via MediatR and distributed cache:
|
||||||
|
- document bytes
|
||||||
|
- receiver envelope data
|
||||||
|
- signature placeholders
|
||||||
|
- cached signature data
|
||||||
|
|
||||||
**Culture Storage:**
|
For signature placeholders, the service:
|
||||||
- Culture preference stored in browser's `localStorage` (key: `AppCulture`)
|
- reads document receiver elements
|
||||||
- Managed by `CultureService.cs` (ReceiverUI/Services)
|
- filters them for the authenticated receiver
|
||||||
- Supported cultures: `de-DE`, `en-US`, `fr-FR`
|
- converts coordinates to `UnitOfLength.Point` before UI use
|
||||||
|
|
||||||
**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
|
## Receiver PDF Viewer
|
||||||
|
|
||||||
### Client-Side Pages (WebUI.Client)
|
**Main file:** `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor`
|
||||||
| File | Route | Purpose |
|
|
||||||
|---|---|---|
|
|
||||||
| `WebUI.Client/Pages/Index.razor` | `/` | Application entry point (landing page). |
|
|
||||||
| `WebUI.Client/Pages/EnvelopeSenderPage.razor` | `/sender` | Sender dashboard (envelope list). |
|
|
||||||
| `WebUI.Client/Pages/LoginSenderPage.razor` | `/sender/login` | Sender username/password auth. |
|
|
||||||
| `WebUI.Client/Pages/LoginReceiverPage.razor` | `/envelope/login/{EnvelopeKey}` | Receiver access code auth. |
|
|
||||||
|
|
||||||
### Server-Side Pages (WebUI)
|
Current receiver viewer characteristics:
|
||||||
| File | Route | Purpose |
|
- route: `/envelope/{EnvelopeKey}`
|
||||||
|---|---|---|
|
- render mode: `InteractiveServer`
|
||||||
| `WebUI/Components/Pages/EnvelopeReceiverPage.razor` | `/envelope/{key}` | Receiver PDF viewer & signing page. Uses Interactive Server, server-side auth/data loading, and currently renders with PDF.js overlay logic. |
|
- PDF rendering: `PDF.js`
|
||||||
| `WebUI/Components/Pages/EnvelopeReceiverPage_DxPdfViewer.razor` | `/envelope/DxPdfViewer` | DevExpress PDF Viewer (test page). |
|
- toolbar: page navigation, zoom, thumbnail toggle, signature navigation, signature reset
|
||||||
| `WebUI/Components/Pages/EnvelopeReceiverPage_DxReportViewer.razor` | `/envelope/{key}/DxReportViewer` | DevExpress Report Viewer. |
|
- signature popup: `DxPopup`
|
||||||
| `WebUI/Components/Pages/EnvelopeReceiverPage_embed.razor` | `/envelope/Embed` | Embedded PDF viewer (iframe). |
|
- thumbnail sidebar: resizable and stored in `localStorage`
|
||||||
|
|
||||||
### Services & Assets
|
### JS Assets
|
||||||
| File | Purpose |
|
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js`
|
||||||
|---|---|
|
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/receiver-signature.js`
|
||||||
| `WebUI.Client/Services/AuthService.cs` | Receiver + Sender authentication. |
|
|
||||||
| `WebUI.Client/Services/SignatureCacheService.cs` | Signature caching (Redis/SQL). |
|
### CSS
|
||||||
| `WebUI.Client/Services/DocumentService.cs` | PDF document retrieval. |
|
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/css/envelope-viewer.css`
|
||||||
| `WebUI/Services/EnvelopeReceiverAuthorizationService.cs` | Server-side receiver authorization for `EnvelopeReceiverPage.razor` using per-envelope cookie/JWT validation. |
|
|
||||||
| `WebUI/Services/EnvelopeReceiverPageDataService.cs` | Server-side document/signature/receiver data loading via MediatR and distributed cache. |
|
### PDF.js CDN
|
||||||
| `WebUI/wwwroot/js/pdf-viewer.js` | PDF.js wrapper (zoom, pagination, thumbnails). |
|
- `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js`
|
||||||
| `WebUI/wwwroot/js/receiver-signature.js` | Signature pad (draw/type/image). |
|
- `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf_viewer.min.css`
|
||||||
| `WebUI/wwwroot/css/envelope-viewer.css` | EnvelopeViewer styles. |
|
|
||||||
| `API/Controllers/CacheController.cs` | Signature cache endpoints. |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Coordinate System — CRITICAL
|
## Signature Workflow
|
||||||
|
|
||||||
**Database Format:** INCHES (GdPicture14 native)
|
Receiver signatures are handled as a **viewer overlay workflow**.
|
||||||
**Origin:** Top-left corner
|
|
||||||
**Axes:** X right, Y down
|
|
||||||
|
|
||||||
### Conversion Formulas
|
### Current behavior
|
||||||
|
1. Server-side authorization validates receiver access.
|
||||||
|
2. The page loads document bytes, receiver data, signature placeholders, and cached signature state.
|
||||||
|
3. If no cached signature exists, the signature popup opens automatically.
|
||||||
|
4. Receiver creates signature using one of three tabs:
|
||||||
|
- draw
|
||||||
|
- text
|
||||||
|
- image
|
||||||
|
5. Required metadata:
|
||||||
|
- full name
|
||||||
|
- place
|
||||||
|
6. Optional metadata:
|
||||||
|
- position
|
||||||
|
7. Clicking a signature placeholder applies the signature as a client-side overlay in the PDF viewer.
|
||||||
|
|
||||||
| From INCHES to | Formula | Example |
|
### Important note
|
||||||
|---|---|---|
|
Although `itext` is referenced by the server project, the current receiver page signing flow is **not PDF stamping-based**. The active receiver UI uses client-side overlay behavior in the viewer.
|
||||||
| **DevExpress DX** | `x_DX = x_inches * 100` | 1.5" ? 150 DX |
|
|
||||||
| **PDF Points** | `x_pt = x_inches * 72` | 1.5" ? 108 pt |
|
|
||||||
| **PDF.js Pixels** | Normalize ? scale | `(x_inches / pageWidth) * canvasWidth * scale` |
|
|
||||||
|
|
||||||
**A4 Dimensions:**
|
### Signature DTO
|
||||||
- Width: 8.27" = 595pt = 827 DX
|
`EnvelopeGenerator.Server.Client/Models/SignatureCaptureDto.cs`
|
||||||
- Height: 11.69" = 842pt = 1169 DX
|
|
||||||
|
|
||||||
### Unit Systems
|
|
||||||
|
|
||||||
| System | Unit | Origin | Y-Axis |
|
|
||||||
|---|---|---|---|
|
|
||||||
| **Database (GdPicture14)** | Inches | Top-left | Down |
|
|
||||||
| PDF.js | Pixels | Top-left | Down |
|
|
||||||
| iText7 PDF | Points (1/72") | **Bottom-left** | **Up** (flip required) |
|
|
||||||
| ~~PSPDFKit~~ | ~~Points~~ | ~~Top-left~~ | **REMOVED** |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## EnvelopeReceiver — PDF.js Viewer & Signing
|
|
||||||
|
|
||||||
**Route:** `/envelope/{EnvelopeKey}`
|
|
||||||
**Tech:** PDF.js 3.11.174 + Blazor Server (`@rendermode InteractiveServer`) + server-side auth/data loading + configurable quality
|
|
||||||
**File:** `WebUI/Components/Pages/EnvelopeReceiverPage.razor`
|
|
||||||
|
|
||||||
### Current Server-Side Loading Model
|
|
||||||
|
|
||||||
- Authorization is performed inside the server project via `EnvelopeReceiverAuthorizationService`.
|
|
||||||
- The page no longer relies on `GET /api/auth/check/envelope/{EnvelopeKey}` for its own access check.
|
|
||||||
- Document bytes, receiver data, and signature placeholders are loaded directly through MediatR using `EnvelopeReceiverPageDataService`.
|
|
||||||
- Cached signatures are loaded from distributed cache directly in the server project.
|
|
||||||
|
|
||||||
### Key Features
|
|
||||||
1. HiDPI/Retina support (4x quality)
|
|
||||||
2. Configurable quality (`appsettings.json`)
|
|
||||||
3. Unlimited zoom (50%-300%)
|
|
||||||
4. Ctrl+Wheel global zoom
|
|
||||||
5. Resizable thumbnail sidebar (150-400px, localStorage)
|
|
||||||
6. Responsive (desktop/mobile)
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
**File:** `WebUI/wwwroot/appsettings.json`
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"PdfViewer": {
|
|
||||||
"ThumbnailBaseScale": 0.75,
|
|
||||||
"ThumbnailEnableHiDPI": true,
|
|
||||||
"MainCanvasEnableHiDPI": true,
|
|
||||||
"ZoomStepPercentage": 5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### JavaScript API
|
|
||||||
**File:** `WebUI/wwwroot/js/pdf-viewer.js`
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
window.pdfViewer = {
|
|
||||||
initialize(canvasId, pdfDataUrl, dotNetRef),
|
|
||||||
renderPage(num),
|
|
||||||
renderSignatureButtons(signatures, pageNum, dotNetRef),
|
|
||||||
applySignature(signatureId, dataUrl, fullName, position, place),
|
|
||||||
zoomIn(), zoomOut(), dispose()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Signature Workflow — EnvelopeReceiver
|
|
||||||
|
|
||||||
**IMPORTANT:** iText7 NOT used (GPL license issue). Client-side overlay system only.
|
|
||||||
|
|
||||||
### Workflow Steps
|
|
||||||
|
|
||||||
1. **Page Load:**
|
|
||||||
- Validate receiver access server-side using the per-envelope auth cookie
|
|
||||||
- Load document, receiver, and signature data directly through MediatR
|
|
||||||
- Check distributed cache for cached signature
|
|
||||||
- If cached ? skip popup, load signature
|
|
||||||
- If not ? show automatic popup (mandatory)
|
|
||||||
|
|
||||||
2. **Signature Popup (DxPopup):**
|
|
||||||
- **Cannot close** (no X, no ESC, no outside-click)
|
|
||||||
- **3 Tabs:** Draw (canvas) / Text (font select) / Image (upload)
|
|
||||||
- **Required:** Full name, Place
|
|
||||||
- **Optional:** Position
|
|
||||||
- **Save ?** Store in `_capturedSignature`, cache via API
|
|
||||||
|
|
||||||
3. **Signature Buttons:**
|
|
||||||
- Render purple "Unterschreiben" buttons at signature field positions
|
|
||||||
- Coordinates: INCHES ? POINTS ? Pixels (scaled)
|
|
||||||
- File: `pdf-viewer.js` ? `renderSignatureButtons()`
|
|
||||||
|
|
||||||
4. **Apply Signature (Click "Unterschreiben"):**
|
|
||||||
- JS: Remove button, create HTML overlay
|
|
||||||
- Format: Image + separator + text (Name, Position, Place, Date)
|
|
||||||
- **NOT stamped on PDF bytes** (visual overlay only)
|
|
||||||
|
|
||||||
5. **Re-rendering:**
|
|
||||||
- Zoom/Page change ? recalculate button positions
|
|
||||||
- Session state: `_capturedSignature` (lost on refresh)
|
|
||||||
|
|
||||||
### Authentication Notes
|
|
||||||
|
|
||||||
- Receiver cookies are stored per envelope: `AuthTokenSignFLOWReceiver.{envelopeKey}`.
|
|
||||||
- `EnvelopeReceiverPage.razor` uses server-side receiver authorization logic instead of calling its own auth check API endpoint.
|
|
||||||
- The server-side auth flow must remain compatible with `AuthScheme.Receiver` and `AuthPolicy.Receiver`.
|
|
||||||
|
|
||||||
### Data Model
|
|
||||||
**File:** `WebUI.Client/Models/SignatureCaptureDto.cs`
|
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public sealed record SignatureCaptureDto {
|
public sealed record SignatureCaptureDto {
|
||||||
public required string DataUrl { get; init; } // base64 PNG
|
public required string DataUrl { get; init; }
|
||||||
public required string FullName { get; init; }
|
public required string FullName { get; init; }
|
||||||
public string Position { get; init; } = ""; // Optional
|
public string Position { get; init; } = "";
|
||||||
public required string Place { get; init; }
|
public required string Place { get; init; }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Signature Caching
|
## Signature Cache
|
||||||
|
|
||||||
**Purpose:** Persist signature across page refreshes (distributed cache: Redis/SQL)
|
### Active cache model
|
||||||
|
The current receiver page cache flow is handled directly in the server project through:
|
||||||
|
- `EnvelopeReceiverPageDataService`
|
||||||
|
- `IDistributedCache`
|
||||||
|
- SQL Server distributed cache configuration from `Program.cs`
|
||||||
|
|
||||||
### API Endpoints
|
### Cache key format
|
||||||
**Controller:** `API/Controllers/CacheController.cs`
|
Current server-side key prefix:
|
||||||
|
- `envelope-generator.receiver-ui.signature:{receiverSignature}`
|
||||||
|
|
||||||
- `POST /api/Cache/SignatureCapture/{envelopeKey}` — Save
|
This is different from an envelope-key-only cache convention.
|
||||||
- `GET /api/Cache/SignatureCapture/{envelopeKey}` — Load
|
|
||||||
- `DELETE /api/Cache/SignatureCapture/{envelopeKey}` — Delete
|
|
||||||
|
|
||||||
**Cache Key Format:**
|
### Config
|
||||||
```
|
`EnvelopeGenerator.Server/EnvelopeGenerator.Server/Options/CacheOptions.cs`
|
||||||
signature:91751687-8ae6-4777-bf5f-b8846085e62e:{envelopeKey}
|
- section name: `Cache`
|
||||||
```
|
- option: `SignatureCacheExpiration`
|
||||||
|
|
||||||
**Configuration:** `appsettings.json`
|
### Related controller
|
||||||
```json
|
A cache API controller also exists in:
|
||||||
{
|
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers/CacheController.cs`
|
||||||
"Cache": {
|
|
||||||
"SignatureCacheExpiration": null // or "02:00:00" for 2h
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Service
|
|
||||||
**File:** `WebUI.Client/Services/SignatureCacheService.cs`
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
public class SignatureCacheService {
|
|
||||||
Task SaveSignatureAsync(string envelopeKey, SignatureCaptureDto signature);
|
|
||||||
Task<SignatureCaptureDto?> GetSignatureAsync(string envelopeKey);
|
|
||||||
Task DeleteSignatureAsync(string envelopeKey);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Error Handling:** Fire-and-forget saves, graceful degradation on load failure.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Sender Login
|
## Sender Dashboard
|
||||||
|
|
||||||
**Route:** `/sender/login`
|
**Main file:** `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/EnvelopeSenderPage.razor`
|
||||||
**File:** `WebUI.Client/Pages/LoginSenderPage.razor`
|
|
||||||
**Tech:** Bootstrap 5 + DevExpress Blazing Berry theme
|
|
||||||
|
|
||||||
### AuthService Extension
|
Current behavior:
|
||||||
**File:** `WebUI.Client/Services/AuthService.cs`
|
- checks sender access through `AuthService.CheckSenderAccessAsync()`
|
||||||
|
- redirects to `/sender/login` when unauthorized
|
||||||
|
- loads envelope list through client `EnvelopeService`
|
||||||
|
- separates envelopes into active/completed tabs
|
||||||
|
- uses `DevExpress DxGrid`
|
||||||
|
|
||||||
```csharp
|
The sender page is active, but create/edit/delete actions are still marked with TODO behavior in the UI page.
|
||||||
public enum SenderLoginResult { Success, InvalidCredentials, Error }
|
|
||||||
|
|
||||||
public async Task<SenderLoginResult> LoginSenderAsync(string username, string password) {
|
|
||||||
var response = await http.PostAsJsonAsync(
|
|
||||||
$"{_api.BaseUrl}/api/auth?cookie=true",
|
|
||||||
new { username, password });
|
|
||||||
|
|
||||||
return response.StatusCode switch {
|
|
||||||
HttpStatusCode.OK => SenderLoginResult.Success,
|
|
||||||
HttpStatusCode.Unauthorized => SenderLoginResult.InvalidCredentials,
|
|
||||||
_ => SenderLoginResult.Error
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### API Integration
|
|
||||||
**Endpoint:** `POST /api/auth?cookie=true`
|
|
||||||
|
|
||||||
**Request:**
|
|
||||||
```json
|
|
||||||
{ "username": "TekH", "password": "***" }
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response:**
|
|
||||||
- `200 OK` ? Cookie set, redirect to `/sender`
|
|
||||||
- `401 Unauthorized` ? Show error: "Ungültige Anmeldedaten"
|
|
||||||
- Other ? Show error: "Serverfehler"
|
|
||||||
|
|
||||||
**Cookie:** HTTP-only, Secure (HTTPS), SameSite=Strict
|
|
||||||
|
|
||||||
### UI Flow
|
|
||||||
1. User enters username + password
|
|
||||||
2. Click "Anmelden" or press Enter
|
|
||||||
3. Call `AuthService.LoginSenderAsync()`
|
|
||||||
4. Success ? `Navigation.NavigateTo("/sender", forceLoad: true)`
|
|
||||||
5. Error ? Display alert
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Receiver Login
|
## Localization
|
||||||
|
|
||||||
**Route:** `/envelope/login/{EnvelopeKey}`
|
Current server host localization setup in `Program.cs`:
|
||||||
**File:** `WebUI.Client/Pages/LoginReceiverPage.razor`
|
- supported cultures: `de-DE`, `en-US`
|
||||||
|
- request localization middleware is enabled
|
||||||
|
- `QueryStringRequestCultureProvider` is added
|
||||||
|
- cookie-based localization services are registered via `AddCookieBasedLocalizer()`
|
||||||
|
|
||||||
**Multi-Envelope Support:** Cookies are stored per-envelope (e.g., `AuthTokenSignFLOWReceiver.{envelopeKey}`), allowing simultaneous authentication for multiple envelopes in the same browser session.
|
Do not assume the old ReceiverUI-only `localStorage` culture approach is the current source of truth for the active host.
|
||||||
|
|
||||||
### AuthService Method
|
|
||||||
```csharp
|
|
||||||
public enum EnvelopeLoginResult { Success, InvalidCode, NotFound, Error }
|
|
||||||
|
|
||||||
public async Task<EnvelopeLoginResult> LoginEnvelopeReceiverAsync(string key, string accessCode) {
|
|
||||||
var form = new MultipartFormDataContent();
|
|
||||||
form.Add(new StringContent(accessCode), "AccessCode");
|
|
||||||
|
|
||||||
var response = await http.PostAsync(
|
|
||||||
$"{_api.BaseUrl}/api/Auth/envelope-receiver/{Uri.EscapeDataString(key)}", form);
|
|
||||||
|
|
||||||
return response.StatusCode switch {
|
|
||||||
HttpStatusCode.OK => EnvelopeLoginResult.Success,
|
|
||||||
HttpStatusCode.Unauthorized => EnvelopeLoginResult.InvalidCode,
|
|
||||||
HttpStatusCode.NotFound => EnvelopeLoginResult.NotFound,
|
|
||||||
_ => EnvelopeLoginResult.Error
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Success:** Redirect to `/envelope/{key}`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## NuGet Packages (WebUI.Client)
|
## Coordinate System
|
||||||
|
|
||||||
| Package | Version | Purpose |
|
### Source data
|
||||||
|---|---|---|
|
Database signature coordinates are still based on:
|
||||||
| `DevExpress.Blazor.*` | 25.2.3 | UI components (grids, popups, etc.) |
|
- **unit:** inches
|
||||||
| `SkiaSharp.*` | 3.119.1 | WASM rendering |
|
- **origin:** top-left
|
||||||
| ~~`itext`~~ | ~~8.0.5~~ | **NOT USED** (GPL license) |
|
- **axes:** X right, Y down
|
||||||
|
|
||||||
**External CDN:**
|
### Relevant conversions
|
||||||
- PDF.js 3.11.174: `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js`
|
- inches -> PDF points: `x_pt = x_inches * 72`
|
||||||
|
- inches -> DevExpress DX units: `x_dx = x_inches * 100`
|
||||||
|
|
||||||
|
### Current receiver page behavior
|
||||||
|
The server page data service converts signature placeholders to **points** before sending them into the viewer workflow.
|
||||||
|
|
||||||
|
### Unit systems to keep in mind
|
||||||
|
| System | Unit | Origin | Y-axis |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Database | Inches | Top-left | Down |
|
||||||
|
| PDF.js display | Pixels | Top-left | Down |
|
||||||
|
| PDF points | Points | Depends on PDF model | Depends on consumer |
|
||||||
|
| DevExpress DX | 1/100 inch style coordinates | Top-left-oriented usage in this app | Down-oriented usage |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Mistakes History — Do NOT Repeat
|
## Key Services and Files
|
||||||
|
|
||||||
| Mistake | Why Wrong |
|
### Client services
|
||||||
|---|---|
|
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AuthService.cs`
|
||||||
| Using iText7 in EnvelopeReceiver | GPL license issue. Use overlay system instead. |
|
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/EnvelopeService.cs`
|
||||||
| Using PSPDFKit | Removed from architecture. Use PDF.js + DevExpress. |
|
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/DocumentService.cs`
|
||||||
| Hardcoded quality values in PDF.js | Use `appsettings.json` for configurability. |
|
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/SignatureCacheService.cs`
|
||||||
| Complex toolbar layouts | User wants simplicity. Keep horizontal layout. |
|
|
||||||
| Over-designed UI (gradients/badges) | User prefers simple text labels. |
|
### Server services
|
||||||
| Ignoring "revert" instructions | Revert HTML structure, not just CSS. |
|
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeAuthService.cs`
|
||||||
| `BottomMarginBand` for signatures | Repeats on every page. Use DetailBand. |
|
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/IEnvelopeAuthService.cs`
|
||||||
| `imageY = (page-1) * 1169 + ann.Y` | Inflates DetailBand. Calculate per-page. |
|
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverAuthorizationService.cs`
|
||||||
|
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverPageDataService.cs`
|
||||||
|
|
||||||
|
### Server config and host files
|
||||||
|
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs`
|
||||||
|
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/yarp.json`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Development Notes
|
## Working Rules for This Workspace
|
||||||
|
|
||||||
### Deprecated Projects
|
- Treat `EnvelopeGenerator.Server` as the active main application host.
|
||||||
**DO NOT USE:**
|
- Treat `EnvelopeGenerator.Server.Client` as the active client UI project.
|
||||||
- `EnvelopeGenerator.ReceiverUI` (Pure Blazor WASM) — Migrated to WebUI (DevExpress compatibility issue)
|
- Prefer current `Server` / `Server.Client` paths over old `WebUI` / `ReceiverUI` references.
|
||||||
- `EnvelopeGenerator.Web` (Razor Pages) — Replaced by unified WebUI
|
- Do not use `EnvelopeGenerator.Web` or `EnvelopeGenerator.ReceiverUI` as the primary implementation target unless explicitly asked.
|
||||||
- PSPDFKit — Removed, use PDF.js + DevExpress instead
|
- Do not modify the legacy VB.NET projects unless explicitly requested.
|
||||||
|
- For receiver PDF/signature work, prefer the current `PDF.js`-based flow in `EnvelopeReceiverPage.razor`.
|
||||||
### Legacy Projects (VB.NET)
|
- For DevExpress PDF viewer issues, remember server-side services are registered in `EnvelopeGenerator.Server`.
|
||||||
**DO NOT TOUCH:** `EnvelopeGenerator.Service`, `EnvelopeGenerator.Form`, `EnvelopeGenerator.BBTests`
|
|
||||||
|
|
||||||
### Signature Coordinate Evidence
|
|
||||||
**File:** `EnvelopeGenerator.Form/frmFieldEditor.vb` (VB.NET)
|
|
||||||
|
|
||||||
```vb
|
|
||||||
Private Const SIGNATURE_WIDTH As Single = 1.77 ' inches
|
|
||||||
Private Const SIGNATURE_HEIGHT As Single = 1.96 ' inches
|
|
||||||
|
|
||||||
Sub LoadAnnotation(pElement As Signature, ...)
|
|
||||||
oAnnotation.Left = CSng(pElement.X) ' Direct INCHES assignment
|
|
||||||
oAnnotation.Top = CSng(pElement.Y)
|
|
||||||
End Sub
|
|
||||||
```
|
|
||||||
|
|
||||||
Proves database uses INCHES natively.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Quick Reference
|
**Last Updated:** 2026-06-29
|
||||||
|
|
||||||
### When working with coordinates:
|
|
||||||
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:
|
|
||||||
1. Check `Mistakes History` first
|
|
||||||
2. Prefer simplicity over complexity
|
|
||||||
3. Use `appsettings.json` for configuration
|
|
||||||
4. Keep consistent with existing design (Bootstrap 5 + Blazing Berry)
|
|
||||||
5. **Unified frontend:** WebUI serves both Senders and Receivers
|
|
||||||
6. **Render mode:** Client-side (WASM) for login/dashboard, Server-side for PDF viewers
|
|
||||||
|
|
||||||
### When debugging:
|
|
||||||
1. **Coordinates:** Always check unit system (inches/points/pixels)
|
|
||||||
2. **Authentication:** Check cookie name/domain/SameSite
|
|
||||||
3. **Cache:** Check Redis/SQL connection + key format
|
|
||||||
4. **Frontend confusion:** Only use WebUI (ReceiverUI/Web are deprecated)
|
|
||||||
5. **Blank DxPdfViewer:** Ensure page has `@rendermode InteractiveServer`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Last Updated:** 2025-01-27 (ReceiverUI ? WebUI migration complete)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user