65ff5961f2e97d16d2ae431a857fbe399eb37abc
L41 from the 2026-05-21 plan.
Shipped (4 sub-tasks):
- **Dialog width**: already fixed in an earlier session
(max-w-[1400px] w-[95vw] on the DialogContent).
- **Draft persistence to localStorage**: scoped per
interest+documentType (`pn-crm.upload-for-signing.draft.v1:<id>:<type>`),
versioned for future shape evolution. Persists step / title /
recipients / fields / invitationMessage with a 500ms debounce so
rapid edits (typing the custom note, dragging a field) don't
hammer storage. The PDF File object itself is NOT persisted
(large blobs + browser quota); on reopen the rep re-picks the
file but every other piece of state survives. Pristine "no
progress yet" state actively clears any stale draft. Header
surfaces a "Draft saved" indicator + Discard button when a
draft exists. Successful submission clears the draft so the
shadow doesn't outlive the doc.
- **PDF preview error handling + zoom**: `onLoadError` now sets
`pdfLoadError` and replaces the spinner with a useful failure
block (error message + re-pick guidance) so reps don't see an
infinite loading state on a broken file. Toolbar gains zoom
controls (50–200% in 25% steps); field coordinates stay in %
of page dimensions so placements scale automatically with the
canvas.
- **Field-placement keyboard shortcuts**: window-level keydown
handler responds to Delete / Backspace (remove selected field),
arrow keys (nudge 0.5% per press, Shift + arrow = 5% per press).
Ignored when focus is in a real input / textarea / contenteditable
so the shortcuts never steal typing.
Verified: tsc clean, vitest 1454/1454.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Description
No description provided
Languages
TypeScript
98.7%
HTML
1%
CSS
0.1%
Shell
0.1%