* fix(pdf-server): translucent highlights, pinch to/from fullscreen, toolbar layout
Highlights rendered opaque: inline `background: <hex>` overrode the CSS
rgba(...,0.35). Now convert def.color via cssColorToRgb and force alpha
0.35. Added a toHaveCSS regression check in pdf-annotations.spec.ts.
Pinch-in while inline (wheel ctrlKey deltaY<0 or two-finger spread
>1.15x) now enters fullscreen. Pinch-out in fullscreen past 0.9x of
fit-scale, when already at/below fit, exits to inline and clears
userHasZoomed so refit sizes the inline view. previewScaleRaw tracks the
unclamped intent so the exit fires even when fit ~= ZOOM_MIN. A
modeTransitionInFlight latch (held 250ms post-toggle) keeps the gesture
tail from re-toggling or immediately zooming the new view.
Fullscreen toolbar: flex-wrap: nowrap and tighter 0.25rem vertical
padding (min-height 40px + safe-top instead of 48px + wrap). Search bar
top/right now follow --safe-top/--safe-right so it sits flush below the
toolbar instead of overlapping it. Base .canvas-container gets
touch-action: pan-x pan-y so the inline pinch is capturable on iOS.
* fix(pdf-server): drop --safe-top from fullscreen toolbar padding
Hosts wrap fullscreen in their own header (title + close), which already
clears the top safe-area. Adding --safe-top to our toolbar padding-top
double-dipped, leaving a visible gap between the host header and our
toolbar. Keep --safe-left/right (host header doesn't cover the sides).
* feat(pdf-server): horizontal swipe changes pages whenever page fits width
Previously gated on scale <= 1.0, which blocked page-nav in fullscreen
where fit-scale is often >100%. Now gate on actual horizontal overflow
(scrollWidth > clientWidth) so swipe works at any fit-to-width scale and
still defers to native panning once you zoom past it.
* fix(pdf-server): refit to inline width after pinch-out exits fullscreen
handleHostContextChanged calls refitScale() before the iframe has actually
shrunk, and the ResizeObserver's inline branch only refits on width
*growth* (to avoid a requestFitToContent shrink-loop). So the
fullscreen->inline shrink never triggered a refit and the page stayed at
the fullscreen scale.
Add a one-shot forceNextResizeRefit flag, set on fullscreen->inline (both
the pinch-out path and handleHostContextChanged), consumed by the
ResizeObserver on the next size change. One-shot keeps the shrink-loop
guard intact for ordinary inline resizes.
* feat(pdf-server): floor fullscreen zoom at fit-to-page
In fullscreen, pinch-out and the zoom-out button now floor at the
fit-to-page scale instead of ZOOM_MIN, so the page never shrinks below
fully-visible (no dead margin around it). previewScaleRaw stays unclamped
so a continued pinch-out past fit still triggers exit-to-inline.
* fix(pdf-server): wheel pinch-out can exit fullscreen again
The fit-floor pinned previewScale at fit, but the wheel handler multiplied
the *clamped* previewScale, so it could never accumulate below fit*0.9 to
trigger exit. Drive the wheel accumulator off previewScaleRaw instead, and
bound previewScaleRaw to [floor*0.7, ZOOM_MAX] so it can cross the 0.9
exit threshold without drifting unboundedly (which would make direction
reversal feel sticky). Touch path was unaffected (absolute ratio).
* fix(pdf-server): always refit to inline width on fullscreen exit
userHasZoomed stayed true after Escape/button/host-× exits, so refitScale()
bailed even though forceNextResizeRefit let the ResizeObserver call it.
Clear userHasZoomed in handleHostContextChanged whenever we land inline —
fullscreen zoom level is meaningless there. Dropped the now-dead
requestFitToContent fallback in the same block.
* refactor(pdf-server): rubber-band pinch-out instead of dual-tracked clamp
The previewScaleRaw side-channel made the gesture feel dead (page pinned
at fit, no feedback) and the wheel accumulator interaction was fragile.
New model: previewScale is the only tracked value. In fullscreen it may
overshoot down to 0.75*fit so the user *sees* the page pull away as they
pinch out. On commit:
- started near fit (<=1.05*fit) AND preview <0.9*fit -> exit to inline
- otherwise clamp committed scale to >=fit (overshoot snaps back)
beginPinch seeds fitScaleAtPinchStart synchronously from when
!userHasZoomed (the common enter-fullscreen-at-fit case) so the first
frame already has the right floor; the async computeFitScale refines it.
* test(pdf-server): update inline-pinch e2e for new fullscreen behavior
'trackpad pinch is ignored outside fullscreen' is no longer the contract.
Replace with two tests: pinch-in inline -> .main.fullscreen appears;
pinch-out inline -> zoom unchanged, no fullscreen. The two
pdf-annotations.spec.ts failures in the previous run were flaky (passed
on retry).