Opening a PDF cost four clicks across two screens: the dock icon, a dropdown
with a single entry in it, an editor with an empty drop zone, and finally the
Open button that produced a file browser. Three of those were the program
asking the user to confirm what they had already said by clicking a PDF icon.
The dock item now raises the file browser itself. The dialog is native and
raised from the add-on's Node side, because a file:// page cannot open one
without a user gesture of its own and a freshly-opened tab has none to spend.
The chosen file goes through the same scratch handoff a right-clicked link
already used, so the editor opens with the document in it rather than with an
invitation to find one. Cancelling still lands in the empty editor, which is
where drag-and-drop works and is probably still where someone who changed
their mind about the file wants to be.
Second click removed in the chrome: a toolbar menu holding exactly one item is
not a menu, it is a button wearing a dropdown, so it now dispatches directly.
Falls through to the popup if the renderer has no addonMenuSelect.
Not yet exercised end to end — TheseusNavigator/node_modules is empty while
another session reinstalls it, so the CDP suites cannot boot Electron. Both
files parse; run scratchpad/verify-pdf-editor/drive.mjs once the tree is back.
Everything the editor put on a page was final. A text stamp could not be
corrected without deleting it and typing it again, nothing could be resized,
and the only way to remove a mark was a Delete key nobody had been told
about — the selection drew a dashed box and offered no action at all. Placing
a stamp also left its tool armed, so the next click stamped a second copy.
Marks are now editable objects. Selecting one gives it grab handles and a
small bar pinned above it: delete and duplicate for anything, and for text an
edit button, a size stepper and bold and italic. Double-clicking text reopens
it for rewriting in place rather than adding a second one. Placing a text
stamp or a signature drops straight back to the select tool with the new mark
live, which is both what people expect and what puts it immediately within
reach of a nudge.
Resizing is one function over every mark type rather than a special case per
kind: a handle drag produces a new bounding box, and the mark is mapped from
its old box into that one. Text scales by font size instead of stretching its
glyphs, signatures keep their aspect on a corner, and lines offer their two
endpoints instead of a box that would let you stretch them in ways you never
aimed at. A whole gesture lands on the undo stack as one step.
Selecting a thin mark used to mean clicking its outline exactly — about one
screen pixel. Each stroked mark now carries an invisible fat copy of itself
purely to catch the pointer.
New marks to go with it: underline and strike-through, which share the
highlight's text-selection geometry and differ only in where the rule sits; a
plain line; and a fill toggle for rectangles and ellipses. Bold and italic
mean three more Helvetica variants embedded at save time, since a PDF treats
them as separate fonts rather than as a style.
Double-click is detected from the pointer stream rather than from a dblclick
listener, because selecting a mark calls preventDefault() on the pointerdown
and that suppresses the compatibility mouse events the browser would have
synthesised the dblclick from.
- pdf-editor 0.1.1 → 0.1.2: manifest.icon is a data:image/svg+xml red PDF
document badge (dock renders it as <img>). Same on toolbar-menu.icon.
Item-level icons dropped — those go through the native OS menu that
doesn't render data URIs.
- translate 0.1.0 → 0.1.1: default LibreTranslate mirror was
translate.argosopentech.com, which is now a dead domain — panel just
said "Failed" on every request. Switched default to
translate.disroot.org (currently up), added a datalist of known
mirrors, and a one-shot migration off the dead default so existing
installs recover on next load.
- docx-editor 0.1.0 → 0.1.1: manifest.icon is a data:image/svg+xml blue
DOC document badge, index.js no longer overrides it with 📝, and the
panel header uses the same inline SVG. Same rationale as pdf-editor —
every extension was rendering as either 📄 or 📝, so PDF and Word
were visually identical to the Notepad.
The profile folder was Electron's default from the product name
("Theseus Navigator") and add-ons lived in addons\ under it. Now:
%APPDATA%\Theseus\extensions\ installed extensions
%APPDATA%\Theseus\extensions-data\ per-extension storage + scratch
%APPDATA%\Theseus\extensions-backups\ replaced copies
%APPDATA%\Theseus\extensions-staged\ staged updates
Both moves are one-time migrations on the first start that finds the old
layout: the profile folder is renamed (same volume, instant) or copied
when a rename is refused, with the old folder left in place in that case;
the four sub-folders are renamed before the extension host first reads
them. Nothing is deleted. THESEUS_USER_DATA still overrides everything.
The host now hands each extension its data folder as api.dataDir; the
Screenshot and PDF editor add-ons used to rebuild the old path from their
own folder for scratch files (so they recreated addons-data\ after the
move) and now use the field, with versions bumped so the bundles reseed.
A PDF that needs a signature, a highlight or a page removed currently sends
the user out to a desktop application or, worse, to a web service that wants
the document uploaded first. Both are poor answers for a browser whose point
is that nothing has to leave the machine. This is a full-tab editor that opens
a PDF, marks it up, fills its forms and saves a new copy, entirely locally.
Two engines, vendored rather than installed, because an add-on ships as a
self-contained folder over the signed update channel and nothing runs a
package manager on the way: pdf.js reads and renders, pdf-lib writes. They
share no state. Everything in between lives in PDF user space — points,
origin bottom-left — which is the one coordinate vocabulary both speak, so a
mark survives zooming, rotating and reordering with no conversion table and
save-time needs to know nothing about how a page happened to be displayed.
The page strip is built from pdf.js's PDFPageView components rather than its
PDFViewer, which renders pages in the file's own order and cannot hide,
reorder or individually rotate one — three of the features here. Text layers
are ours and stay attached for every page, drawn or not, because Theseus's
find bar is Chromium's findInPage over the live DOM and a torn-down text layer
is a page Ctrl+F cannot see. Canvases are virtualised; a letter page at 100%
is 3.4 MB of bitmap.
Redaction is the part worth being careful about. A black box over text hides
nothing — the text stays in the content stream and comes straight out of a
copy-paste — so the editor says so in a modal before the tool can be used,
and on save rebuilds each redacted page as an image, which genuinely removes
it. Pages that were not redacted are untouched. Form widgets and links are
kept, since they were never the leak.
Saving never writes over the original: every save reloads the source bytes and
replays the session onto a fresh copy, so a botched save cannot poison the
next one.
Out of scope for this first version: editing the text that is already in the
document, and writing XFA forms back (pdf-lib cannot, so those are fill-and-
print only, and the editor says so on open).