From ce066bb4cad43a8127366d43120978496831d8d9 Mon Sep 17 00:00:00 2001 From: Local Dev Date: Sun, 27 Sep 2026 15:09:17 +0200 Subject: [PATCH] feat(pdf-editor): one select, one text tool, and a signature you can ink and turn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Select does what selecting does in every editor people already know. Double-click bare page and a caret opens there; double-click the document's own words and they open for replacement; what is selected copies with Ctrl+C, pastes with Ctrl+V and goes with Del. Nothing was taken away — a drag on empty page still gathers an area, and a drag that starts on words still selects words to copy. Type text and Edit text were two buttons for one question the click already answers. They are one Text tool: land on the document's own words and it offers to replace them, land anywhere else and it starts new text. The words light up under the cursor so which is which is visible before clicking, not after. The toolbar says what it is for. Select, Text and Sign are labelled and set apart; the drawing kit and the markup kit are their own groups. Sign gets a pen icon over a signature rather than a squiggle that could have been anything. Signatures take ink — black, blue, red, green — chosen while drawing and kept with the signature, because people sign in a particular colour and it belongs to the signature, not to whichever swatch was armed. And they turn: a grip above the box, free rotation, Shift to snap to 15°, for the signing line that is not square to the page. Two faults the tests found, both invisible by eye: The rotate grip was drawn in the right place and could not be grabbed — the selection bar floats directly above a mark, which is exactly where the grip sits, and it swallowed every click. The bar now stands clear of it. Undo would not undo a first rotation. Restoring a mark with Object.assign copies the keys the original HAD, so a property the drag introduced survived the restore; the journal then recorded the rotated state as the state to go back to. Restoring now forgets keys the original never had, which fixes every future property with the same shape. Also: building a document from pictures or joins refuses to start a second one on top of the first, and says so rather than failing quietly. --- bundled-addons/pdf-editor/addon.json | 2 +- bundled-addons/pdf-editor/editor.css | 23 +++- bundled-addons/pdf-editor/editor.html | 70 +++++++---- bundled-addons/pdf-editor/editor.js | 95 +++++++++++++- bundled-addons/pdf-editor/lib/overlay.js | 15 +++ bundled-addons/pdf-editor/lib/shape.js | 37 +++++- bundled-addons/pdf-editor/lib/signature.js | 13 +- bundled-addons/pdf-editor/lib/tools.js | 137 +++++++++++++++++---- 8 files changed, 329 insertions(+), 63 deletions(-) diff --git a/bundled-addons/pdf-editor/addon.json b/bundled-addons/pdf-editor/addon.json index 8aa58882..321cc4f8 100644 --- a/bundled-addons/pdf-editor/addon.json +++ b/bundled-addons/pdf-editor/addon.json @@ -1,7 +1,7 @@ { "id": "pdf-editor", "name": "PDF Editor", - "version": "0.6.0", + "version": "0.7.0", "description": "Open a PDF in a full tab: read it, mark it up with highlight, underline, strike-through, pen, shapes, arrows, text and a drawn signature, then move, resize, restyle or delete anything you placed. Reorder, rotate or drop pages, fill in form fields, redact for real, and save a new copy. Build a PDF from pictures, one page each, or join several PDFs into one. Marks are written into the saved file as real PDF content. The original is never touched.", "author": "Silent Mode", "icon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTYgMmg4bDYgNnYxMmEyIDIgMCAwIDEtMiAySDZhMiAyIDAgMCAxLTItMlY0YTIgMiAwIDAgMSAyLTJ6IiBmaWxsPSIjZGMyNjI2Ii8+PHBhdGggZD0iTTE0IDJ2Nmg2eiIgZmlsbD0iIzk5MWIxYiIvPjx0ZXh0IHg9IjEyIiB5PSIxNyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9InN5c3RlbS11aSwtYXBwbGUtc3lzdGVtLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iNS41IiBmb250LXdlaWdodD0iODAwIiBmaWxsPSIjZmZmIj5QREY8L3RleHQ+PC9zdmc+", diff --git a/bundled-addons/pdf-editor/editor.css b/bundled-addons/pdf-editor/editor.css index 4a974c9f..b0813224 100644 --- a/bundled-addons/pdf-editor/editor.css +++ b/bundled-addons/pdf-editor/editor.css @@ -160,6 +160,16 @@ body[data-dragover="1"] .dz-actions { visibility: hidden; } .linkish { background: none; border: none; padding: 0; font: inherit; cursor: pointer; color: var(--acid); text-decoration: underline; } +/* ---- the three main tools ------------------------------------------- */ +/* Point at it, write on it, sign it. Wider, labelled, and set apart from the + drawing kit, because they are the things most people open a PDF to do and + they used to be four identical squares in a row of thirteen. */ +.tgroup.primary { gap: 4px; } +.tool.big { width: auto; padding: 0 10px; gap: 6px; } +.tool.big .tlabel { font-size: 12px; line-height: 1; } +.tool.big svg { width: 15px; height: 15px; } +@media (max-width: 900px) { .tool.big .tlabel { display: none; } .tool.big { width: 30px; padding: 0; } } + /* ---- typing on the page --------------------------------------------- */ /* Positioned by lib/inline.js against the page's own coordinates, so it sits where the text will sit. No background: the page is the background, which @@ -187,6 +197,11 @@ body[data-dragover="1"] .dz-actions { visibility: hidden; } icon, and a word does not fit in one. */ .sigrow .sigacts .btn { flex: none; width: auto; height: 26px; white-space: nowrap; padding: 0 10px; font-size: 11.5px; line-height: 1; } +/* Ink for the pad. Dark by default: a signature is not a highlight. */ +.sigink { display: flex; gap: 6px; margin: 0 0 8px; } +.sigink .swatch { width: 22px; height: 22px; border-radius: 50%; + border: 2px solid var(--line); cursor: pointer; padding: 0; } +.sigink .swatch.active { border-color: var(--acid); } /* ---- modals --------------------------------------------------------- */ .scrim { position: fixed; inset: 0; background: rgba(4,7,12,.62); z-index: 50; @@ -336,9 +351,11 @@ body[data-mode="select"] .smOverlay .mark .hit { pointer-events: stroke; } live so a click can land on a run; the form layer steps aside so a widget does not swallow it. The hover outline is the only cue that the invisible text layer is a target at all. */ -body[data-mode="pickrun"] .annotationLayer { pointer-events: none !important; } -body[data-mode="pickrun"] .pdfViewer .page { cursor: text; } -body[data-mode="pickrun"] .textLayer span:hover { +body[data-mode="text"] .annotationLayer { pointer-events: none !important; } +body[data-mode="text"] .pdfViewer .page { cursor: text; } +/* The document's own words light up under the cursor, so it is obvious that + clicking them replaces them and clicking anywhere else starts new text. */ +body[data-mode="text"] .textLayer span:hover { outline: 2px solid rgb(from var(--acid) r g b / .9); outline-offset: 1px; background: rgb(from var(--acid) r g b / .16); diff --git a/bundled-addons/pdf-editor/editor.html b/bundled-addons/pdf-editor/editor.html index 21907b02..9d567702 100644 --- a/bundled-addons/pdf-editor/editor.html +++ b/bundled-addons/pdf-editor/editor.html @@ -35,10 +35,46 @@ controls can right-anchor without the tools crowding the left edge. -->
-
- + + +
+ +
+ + + + + +
+ +
@@ -48,30 +84,6 @@ - - - - - - - - @@ -225,6 +237,12 @@