diff --git a/EnvelopeGenerator.sln b/EnvelopeGenerator.sln index b672495a..5507c274 100644 --- a/EnvelopeGenerator.sln +++ b/EnvelopeGenerator.sln @@ -23,6 +23,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{134D4164-B29 ProjectSection(SolutionItems) = preProject COPILOT_CONTEXT.md = COPILOT_CONTEXT.md FORM_APPLICATION_CONTEXT.md = FORM_APPLICATION_CONTEXT.md + RECEIVER_PDF_VIEWER_CONTEXT.md = RECEIVER_PDF_VIEWER_CONTEXT.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0CBC2432-A561-4440-89BC-671B66A24146}" diff --git a/RECEIVER_PDF_VIEWER_CONTEXT.md b/RECEIVER_PDF_VIEWER_CONTEXT.md new file mode 100644 index 00000000..1bbeffbf --- /dev/null +++ b/RECEIVER_PDF_VIEWER_CONTEXT.md @@ -0,0 +1,581 @@ +# EnvelopeGenerator — Receiver PDF Viewer Context + +## Purpose +This document summarizes the active receiver-side PDF viewing and signing experience so that other agents can understand the current implementation quickly without re-reading all related files. + +This summary is based on the active host and current implementation in: +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor` +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js` +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/receiver-signature.js` +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/css/envelope-viewer.css` + +--- + +## Active Page + +**Primary receiver page:** +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor` + +**Route:** +- `/envelope/{EnvelopeKey}` + +**Render mode:** +- `InteractiveServer` + +This page is the active receiver PDF viewing and signing UI. + +--- + +## Core Architecture + +The receiver experience is built from three layers: + +### 1. Blazor page layer +`EnvelopeReceiverPage.razor` is responsible for: +- authorization flow +- loading receiver-specific document data +- loading signature placeholders +- loading and saving cached signature data +- UI state management +- toolbar interactions +- popup interactions +- JS interop orchestration + +### 2. PDF rendering layer +The active implementation currently uses `PDF.js` for: +- rendering the active PDF page +- rendering thumbnails +- handling zoom and page navigation state + +Referenced assets in the current implementation: +- `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js` +- `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf_viewer.min.css` + +### Planned target rendering layer +The main migration goal is to replace `PDF.js` in `EnvelopeReceiverPage.razor` with `DxPdfViewer` while preserving the complete receiver signing experience documented in this file. + +This means `DxPdfViewer` must become the main document rendering surface without regressing: +- single active page viewing behavior +- page navigation behavior +- zoom behavior +- thumbnail sidebar behavior +- receiver-specific signature placeholder overlays +- applied signature overlays +- signature navigation across pages +- overlay repositioning and resizing after zoom/page changes + +### 3. Custom enhancement layer +Extra behavior is implemented through custom JavaScript and CSS: +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js` +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/receiver-signature.js` +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/css/envelope-viewer.css` + +These files extend the base PDF.js experience with receiver-specific features. + +--- + +## Main Functional Capabilities + +## 1. Single-page PDF viewing in the main viewer +The page shows one active PDF page at a time in the main canvas. + +Main viewer elements: +- `pdf-canvas` +- `pdf-text-layer` +- `pdf-signature-layer` + +This means the page is not using a continuous full-document scroll layout in the main area. Instead, it behaves like a single active page viewer with controlled navigation. + +--- + +## 2. Page navigation +The page supports multiple navigation methods: +- previous page button +- next page button +- direct page number input +- thumbnail click navigation +- signature navigation that may jump to another page automatically + +Relevant Blazor methods include: +- `PreviousPage()` +- `NextPage()` +- `OnPageInputChanged(...)` +- `GoToPageFromThumbnail(...)` +- `OnPageChangedBySignatureNav(...)` + +--- + +## 3. Zoom support +The page supports zooming in and out for the active PDF page. + +Available zoom behavior: +- zoom in button +- zoom out button +- zoom slider +- programmatic scale setting +- fit-to-width helper exists in code + +Relevant methods: +- `ZoomIn()` +- `ZoomOut()` +- `SetZoom(int percentage)` +- `OnZoomSliderChanged(...)` +- `FitToWidth()` +- `OnZoomChanged(double scale)` + +Current zoom constraints in the page: +- minimum: `50%` +- maximum: `300%` + +Important behavior: +- when zoom changes, signature placeholders and applied signature overlays are re-rendered/repositioned +- position and visual size are expected to stay synchronized with the current PDF scale + +--- + +## 4. Thumbnail sidebar +The page has a thumbnail sidebar for document page previews. + +Supported sidebar behavior: +- show/hide toggle +- click thumbnail to navigate to page +- highlight active page +- resizable width +- width persistence in `localStorage` + +Relevant state: +- `_showThumbnails` +- `_thumbnailWidth` +- `_isResizing` + +Relevant methods: +- `ToggleThumbnails()` +- `RenderThumbnailsAsync()` +- `OnSplitterMouseDown(...)` +- `OnSplitterMouseMove(...)` +- `OnSplitterMouseUp()` + +Persistence key: +- `envelopeViewer_thumbnailWidth` + +Important implementation detail: +- thumbnail rendering is done sequentially with delay to avoid overloading the browser + +--- + +## 5. Signature placeholder buttons on top of the PDF +The page places clickable signature buttons over the PDF for receiver-specific signature fields. + +Behavior: +- placeholders are loaded server-side +- placeholders are rendered on the client as overlay elements +- placeholders are shown for the current page +- clicking a placeholder applies the captured signature to that location + +Relevant Blazor method: +- `RenderSignatureButtonsAsync()` + +Relevant JS interop call: +- `pdfViewer.renderSignatureButtons` + +Important note: +- this is an overlay workflow, not direct PDF stamping +- placeholders belong to the current authenticated receiver + +--- + +## 6. Applying a real signature after clicking a placeholder +Once a receiver has a captured signature, clicking a signature placeholder applies the real signature overlay to the PDF viewer. + +Applied signature data includes: +- signature image data URL +- signer full name +- signer position +- place + +Relevant method: +- `OnSignatureButtonClick(int signatureId)` + +Relevant JS interop call: +- `pdfViewer.applySignature` + +Important note: +- the active implementation visually places the signature in the viewer layer +- the page is not currently doing final PDF binary stamping in this UI flow + +--- + +## 7. Signature navigation across all signature fields +The toolbar supports fast navigation between signature fields. + +Supported behavior: +- go to previous signature +- go to next signature +- maintain current signature index +- show signed / unsigned / total counts +- automatically move between pages if the next signature is on another page + +Relevant methods: +- `GoToPreviousSignature()` +- `GoToNextSignature()` +- `OnSignatureNavChanged()` +- `OnPageChangedBySignatureNav(int newPage)` +- `UpdateSignatureCounterAsync()` + +Relevant JS interop calls: +- `pdfViewer.goToPreviousSignature` +- `pdfViewer.goToNextSignature` +- `pdfViewer.getSignatureNavState` + +Counter state maintained in Blazor: +- `_totalSignatures` +- `_signedSignatures` +- `_unsignedSignatures` +- `_currentSignatureIndex` + +--- + +## 8. Automatic signature/button repositioning and resizing on zoom +A critical current feature is that zoom changes should keep overlay elements visually aligned with the document. + +This applies to: +- signature placeholder buttons +- applied signature overlays + +Expected behavior: +- position updates when zoom changes +- size updates when zoom changes +- re-render happens after page change and zoom change + +This behavior is triggered from methods such as: +- `OnZoomChanged(...)` +- `ZoomIn()` +- `ZoomOut()` +- `OnZoomSliderChanged(...)` +- `NextPage()` +- `PreviousPage()` +- `GoToPageFromThumbnail(...)` +- `OnPageChangedBySignatureNav(...)` + +In practice, the page repeatedly calls: +- `RenderSignatureButtonsAsync()` + +This is one of the key behaviors other agents must preserve. + +--- + +## 9. Signature capture popup +Signature creation is handled with a `DxPopup`. + +Popup capabilities: +- draw signature +- create typed signature +- upload signature image +- capture required signer metadata +- validate required fields before save + +Tabs: +- `draw` +- `text` +- `image` + +Relevant constants: +- `SignatureTabDraw` +- `SignatureTabText` +- `SignatureTabImage` + +Relevant canvas/input IDs: +- `envelope-signature-pad` +- `envelope-typed-signature-pad` +- `envelope-signature-image-input` +- `envelope-image-signature-pad` + +--- + +## 10. Signature capture modes +The current UI supports three signature creation modes. + +### Draw mode +Receiver signs directly on a canvas. + +Relevant JS usage: +- `receiverSignature.initialize` +- `receiverSignature.clear` +- `receiverSignature.getDataUrl` +- `receiverSignature.loadExistingSignature` + +### Text mode +Receiver enters a signature as text and chooses a font. + +Relevant JS usage: +- `receiverSignature.initializeTyped` +- `receiverSignature.renderTypedSignature` +- `receiverSignature.clearTyped` +- `receiverSignature.getTypedDataUrl` + +### Image mode +Receiver uploads an image of their signature. + +Relevant JS usage: +- `receiverSignature.initializeImage` +- `receiverSignature.clearImage` +- `receiverSignature.getImageDataUrl` + +--- + +## 11. Signature metadata requirements +The popup captures extra metadata besides the signature image. + +### Required +- full name +- place + +### Optional +- position + +Relevant bound fields: +- `_signerFullName` +- `_signaturePlace` +- `_signerPosition` + +Validation behavior in `SaveSignatureAsync()`: +- full name must not be empty +- place must not be empty +- signature image/data must not be empty + +--- + +## 12. Cached signature reuse +The page attempts to load a previously cached signature for the receiver. + +Behavior: +- if cache exists, the popup does not open automatically +- if cache does not exist, the popup opens on initial load +- saving a signature stores it back through the page data service + +Relevant service usage: +- `PageDataService.GetCachedSignatureAsync(...)` +- `PageDataService.SaveCachedSignatureAsync(...)` + +Related state: +- `_capturedSignature` +- `_signaturePopupVisible` + +Important note: +- the active cache handling is server-side through distributed cache +- the receiver signature is not only in browser state + +--- + +## 13. Signature change locking after signing starts +The current page prevents changing the captured signature after at least one signature has already been applied. + +Behavior: +- signature change button becomes disabled when `_signedSignatures > 0` +- title explains the signature is locked +- reset requires restarting the signing session + +Relevant methods: +- `GetSignatureButtonTitle()` +- `HandleSignatureChangeClick()` +- `RestartSigning()` + +Important implication: +- once actual field signing begins, the selected/captured signature is treated as fixed for that session unless the page is reset + +--- + +## 14. Reset/restart signing flow +The page includes a reset/restart behavior. + +Current behavior: +- page reload is used to reset signing UI state +- this clears current in-view applied signatures and session UI state + +Relevant method: +- `RestartSigning()` + +Implementation detail: +- current reset behavior is based on `Navigation.NavigateTo(Navigation.Uri, forceLoad: true)` + +--- + +## 15. PDF quality and rendering options +The page sends rendering options from .NET to JavaScript before viewer initialization. + +Relevant options include: +- thumbnail base scale +- thumbnail HiDPI support +- thumbnail max DPR +- main canvas HiDPI support +- main canvas max DPR +- smooth zoom enablement +- zoom transition duration +- rendering opacity +- zoom step percentage + +These come from: +- `IOptions` + +Relevant JS call: +- `pdfViewer.setQualityOptions(...)` + +This means rendering quality and viewer performance are configurable from server-side options. + +--- + +## 16. Local storage usage +The page currently uses `localStorage` at least for UI preferences. + +Known usage: +- thumbnail sidebar width persistence + +Known key: +- `envelopeViewer_thumbnailWidth` + +This is UI preference storage, not the main signature cache mechanism. + +--- + +## 17. Receiver authorization dependency +The page is not a public PDF viewer. It depends on receiver-specific authorization. + +Before loading the document: +- `ReceiverAuthorizationService.AuthorizeAsync(EnvelopeKey)` is executed +- unauthorized users are redirected to `/envelope/login/{EnvelopeKey}` + +Implication for other agents: +- viewer behavior is tied to authenticated receiver context +- placeholder loading and cached signature loading are receiver-specific + +--- + +## 18. Receiver-specific data loading +The page loads its data through `EnvelopeReceiverPageDataService`. + +Main server-side data loaded: +- document bytes +- signature placeholders +- receiver envelope data +- cached signature + +Important domain behavior: +- signature placeholders are filtered for the authenticated receiver +- placeholder coordinates are converted to points before UI use + +This matters for any future changes involving coordinate systems or overlay placement. + +--- + +## Important Non-Goals / Current Constraints + +### Not acceptable as a migration outcome +The migration is **not** successful if it results in: +- losing any receiver-side behavior documented in this file +- replacing the current workflow with a simplified read-only PDF viewer +- removing signature placeholder overlays or applied signature overlays +- removing cross-page signature navigation +- breaking zoom-time overlay synchronization +- falling back to legacy `ReceiverUI` as the main implementation target +- changing the flow into direct PDF binary stamping-only behavior in the main receiver UI + +### Current active model +The current implementation **is** based on: +- `EnvelopeGenerator.Server` +- `EnvelopeReceiverPage.razor` +- `PDF.js` +- custom overlay and JS interop behavior + +### Target model +The intended target model is: +- `EnvelopeGenerator.Server` +- `EnvelopeReceiverPage.razor` +- `DxPdfViewer` as the main PDF rendering component +- equivalent custom behavior for overlays, navigation, zoom synchronization, and receiver signing interactions +- preservation of the current receiver-specific authorization and page data loading flow + +--- + +## Key Behaviors Other Agents Must Preserve + +If another agent modifies this area, these behaviors are important to keep intact: + +1. Receiver authorization before document access +2. Single active page PDF viewing +3. Page navigation and thumbnail navigation +4. Zoom in/out with correct overlay synchronization +5. Signature placeholder rendering on the correct page +6. Clicking a placeholder applies the captured signature overlay +7. Fast previous/next signature navigation across pages +8. Automatic overlay position and size updates after zoom/page changes +9. Signature popup with draw/text/image modes +10. Required metadata validation for full name and place +11. Cached signature reuse +12. Signature lock after signing has started +13. Reset/restart signing behavior +14. Thumbnail width persistence + +## Migration Requirement + +Any migration from `PDF.js` to `DxPdfViewer` in `EnvelopeReceiverPage.razor` must be treated as a rendering engine swap, not a workflow redesign. + +The expected outcome is: +- `DxPdfViewer` replaces `PDF.js` as the main viewer technology +- the receiver authorization model remains unchanged +- the existing signature capture popup model remains unchanged +- signature placeholder rendering remains receiver-specific +- applied signature visuals remain aligned with the rendered document +- page navigation, zoom, thumbnails, and signature navigation continue to behave equivalently +- no documented feature in this file is dropped during the migration + +--- + +## Suggested Mental Model + +A useful way to think about the page is: + +- `EnvelopeReceiverPage.razor` = orchestration and state +- current rendering engine = `PDF.js` +- target rendering engine = `DxPdfViewer` +- current viewer behavior layer = `pdf-viewer.js` for overlays, navigation, thumbnails, resize logic +- `receiver-signature.js` = signature capture/creation logic +- server page data service = receiver-specific document and signature source + +--- + +## Files Most Likely Relevant For Future Work + +### Main page +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor` + +### JavaScript +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js` +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/receiver-signature.js` + +### CSS +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/css/envelope-viewer.css` + +### Data/auth services +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverAuthorizationService.cs` +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverPageDataService.cs` + +--- + +## Summary +The active receiver document experience is currently a `PDF.js`-based, server-orchestrated, overlay-signing workflow in `EnvelopeGenerator.Server`. + +It supports: +- single-page PDF display +- page navigation +- zoom +- thumbnails +- receiver-specific signature placeholders +- actual signature overlay placement +- fast signature navigation across pages +- overlay rescaling/repositioning during zoom +- signature capture via draw/text/image +- cached signature reuse + +The current migration goal is to move this experience to `DxPdfViewer` in `EnvelopeReceiverPage.razor` without losing any of the capabilities listed above. + +Any future changes in this area should be made with the assumption that this is a custom receiver signing experience whose rendering engine may change, but whose functional behavior must remain intact; it is not a basic document embed and not a direct PDF stamping pipeline.