@if (_pdfDocumentContent is not null && _pdfDocumentContent.Length > 0)
{
-
}
@@ -558,9 +539,11 @@
int _currentPage = 1;
int _totalPages = 0;
int _currentZoom = 150;
+ double _viewerZoomLevel = 1.5;
bool _showThumbnails = true;
bool _isLoggingOut = false;
DotNetObjectReference
? _dotNetRef;
+ DxPdfViewer? _pdfViewer;
IReadOnlyList _signatures = [];
EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver.EnvelopeReceiverDto? _envelopeReceiver;
ClaimsPrincipal? _receiverUser;
@@ -766,6 +749,21 @@
await SetZoom(requestedZoom);
}
+ async Task OnViewerZoomLevelChanged(double newZoomLevel)
+ {
+ _viewerZoomLevel = newZoomLevel;
+
+ if (newZoomLevel > 0)
+ {
+ _currentZoom = (int)Math.Round(newZoomLevel * 100, MidpointRounding.AwayFromZero);
+ }
+
+ await JSRuntime.InvokeVoidAsync("pdfViewer.setViewState", _currentPage, _currentZoom);
+ await Task.Delay(150);
+ await RenderSignatureButtonsAsync();
+ await InvokeAsync(StateHasChanged);
+ }
+
async Task NextPage()
{
if (_currentPage >= _totalPages)
@@ -799,7 +797,12 @@
async Task SetZoom(int percentage)
{
_currentZoom = Math.Clamp(percentage, 50, 300);
- await ApplyViewerStateAsync();
+ _viewerZoomLevel = _currentZoom / 100d;
+
+ await InvokeAsync(StateHasChanged);
+ await JSRuntime.InvokeVoidAsync("pdfViewer.setViewState", _currentPage, _currentZoom);
+ await Task.Delay(150);
+ await RenderSignatureButtonsAsync();
}
async Task OnZoomSliderChanged(ChangeEventArgs e)
@@ -824,8 +827,13 @@
async Task FitToWidth()
{
+ _viewerZoomLevel = -2;
_currentZoom = 150;
- await ApplyViewerStateAsync();
+
+ await InvokeAsync(StateHasChanged);
+ await JSRuntime.InvokeVoidAsync("pdfViewer.setViewState", _currentPage, _currentZoom);
+ await Task.Delay(150);
+ await RenderSignatureButtonsAsync();
}
async Task ToggleThumbnails()
@@ -1191,6 +1199,11 @@
if (!_pdfLoaded)
return;
+ if (_viewerZoomLevel > 0)
+ {
+ _viewerZoomLevel = _currentZoom / 100d;
+ }
+
await InvokeAsync(StateHasChanged);
await JSRuntime.InvokeVoidAsync("pdfViewer.setViewState", _currentPage, _currentZoom);
await Task.Delay(150);
diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js
index d48104c3..9a6ff107 100644
--- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js
+++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js
@@ -78,28 +78,6 @@ window.pdfViewer = {
return;
}
- if (!this.wheelHandler) {
- this.wheelHandler = async (e) => {
- if (!(e.ctrlKey || e.metaKey) || !this.dotNetReference) {
- return;
- }
-
- e.preventDefault();
-
- const step = this.qualityOptions.zoomStepPercentage;
- const nextZoom = e.deltaY < 0
- ? Math.min(this.currentZoom + step, 300)
- : Math.max(this.currentZoom - step, 50);
-
- if (nextZoom !== this.currentZoom) {
- this.currentZoom = nextZoom;
- await this.dotNetReference.invokeMethodAsync('OnZoomGestureRequested', nextZoom);
- }
- };
-
- host.addEventListener('wheel', this.wheelHandler, { passive: false });
- }
-
if (!this.resizeObserver && typeof ResizeObserver !== 'undefined') {
this.resizeObserver = new ResizeObserver(() => this.requestOverlayRefresh());
this.resizeObserver.observe(host);
diff --git a/RECEIVER_PDF_VIEWER_CONTEXT.md b/RECEIVER_PDF_VIEWER_CONTEXT.md
index 28c56e57..ea46ee2c 100644
--- a/RECEIVER_PDF_VIEWER_CONTEXT.md
+++ b/RECEIVER_PDF_VIEWER_CONTEXT.md
@@ -939,6 +939,77 @@ That is the safest first vertical slice because:
- it reduces uncertainty in overlay scaling
- it does not yet require full signature flow completion
+### 9. Verified DevExpress API findings and current progress
+
+The following points are now verified from DevExpress documentation for `DxPdfViewer`:
+
+- `ZoomLevel` is a real component parameter
+- positive `ZoomLevel` values are interpreted as percentages
+- `ZoomLevelChanged` is supported and should be used to synchronize viewer zoom with page state
+- `CustomizeToolbar` can clear all built-in toolbar items through `ToolbarModel.AllItems.Clear()`
+- `ActivePageIndex` is read-only information and is not a page-navigation parameter
+
+### 10. Recovery progress update
+
+The first functional recovery step is zoom restoration.
+
+Implemented direction:
+
+- bind custom receiver toolbar zoom controls to `DxPdfViewer.ZoomLevel`
+- synchronize zoom changes back into `_currentZoom` through `ZoomLevelChanged`
+- remove competing custom JS ctrl+wheel zoom logic
+- keep overlay redraw pipeline after confirmed zoom changes
+
+Expected outcome after this step:
+
+- toolbar zoom buttons visibly affect the DevExpress viewer
+- zoom slider visibly affects the DevExpress viewer
+- overlay refresh is triggered from real viewer zoom state instead of guessed zoom state
+
+### 11. Additional findings after first zoom integration attempt
+
+After the first live integration attempt against the installed `DevExpress.Blazor.PdfViewer` package version `25.2.3`, one important runtime behavior was confirmed:
+
+- the `DxPdfViewer.ZoomLevel` value must be treated as a zoom factor for normal positive values in the live UI flow used here
+- in practice, `1.5` corresponds to `150%`
+- using `150` as the bound value causes the DevExpress viewer UI to display `15000%`
+
+This means the receiver page must keep two different zoom representations:
+
+- `_currentZoom` = receiver custom workflow percentage view, for example `150`
+- `_viewerZoomLevel` = DevExpress viewer value, for example `1.5`
+
+### 12. Current UI decision for zoom controls
+
+The custom toolbar zoom section in `pdf-toolbar__zoom-section` is no longer considered desirable.
+
+Current decision:
+
+- remove the custom zoom buttons and slider from the receiver toolbar
+- rely on the built-in DevExpress PDF Viewer zoom UI instead
+- keep `ZoomLevelChanged` synchronization so overlay redraw logic can still react to viewer zoom changes
+
+Reason:
+
+- DevExpress already provides zoom UX
+- duplicate zoom controls create confusing UX and unit mismatch risks
+- the built-in viewer zoom UI is a better source of truth for the current zoom state
+
+### 13. Current UI decision for thumbnails
+
+The custom thumbnail sidebar is still kept for now.
+
+Reason:
+
+- current receiver workflow depends on custom thumbnail shell behavior
+- width persistence and resizable splitter are already implemented in the custom sidebar
+- no verified built-in `DxPdfViewer` thumbnail sidebar integration surface has yet been confirmed from the currently inspected API surface
+
+So the current decision is:
+
+- keep custom thumbnail sidebar for now
+- revisit possible DevExpress-native thumbnail navigation later only if it supports the required receiver workflow behavior
+
---
## Files Most Likely Relevant For Future Work