Files
Le Tan f3ed4111a4 Upgrade pdf.js to v6 and add per-file PDF comments
Vendor pdf.js v6.2.108 (legacy dist) in place of v3.11.174 and add a
comment/highlight feature on top of it. The two land together because the
comment overlay is built on the v6 viewer and shares its template, bridge
and view window; neither half builds without the other.

=== pdf.js v6 ===

v4+ is ESM-only, so the old setHtml() page no longer works: it has an
opaque data: origin, and a module fetched from there to a file:// URL is
cross-origin and blocked by Chromium. The viewer, its assets and the
document bytes are now served from one real origin over a custom
`vxpdf://` QWebEngineUrlScheme (Syntax::Host, SecureScheme | CorsEnabled |
FetchApiAllowed), validated by a standalone spike before any of it was
written.

The page is served at the exact path the stock web/viewer.html occupies,
which is load-bearing: every pdf.js default option (./images/,
../build/pdf.worker.mjs, ../web/cmaps/, locale/locale.json) resolves
relative to the document URL and needs no override.

The legacy dist targets Chrome 125+, so the built-in viewer is now gated
to Qt 6.9+ (Chromium 130) instead of Qt 6.0; below that VNote hands PDFs
to the system reader exactly as it did on Qt 5.

`editor.pdf_viewer.viewerResource` is persisted verbatim per user and
WebResource::init() takes it wholesale, so the v3 script list would
survive the upgrade and load three files that no longer exist. Reset it
in doVersionSpecificOverride for any version below 4.6.0.

Version bumped to 4.6.0 via scripts/update_version.py, which also forces
re-extraction of the bundled web/ tree.

=== Comments ===

Comments live in a per-file comments.json sidecar: inside the file's UUID
assets folder for a bundled notebook, beside the file otherwise. The
store travels with the file.

The schema is deliberately not PDF-specific -- only Comment::m_anchor
carries file-type knowledge -- and is forward compatible: an anchor type
this build does not implement is preserved verbatim rather than dropped,
so an older PDF-only build cannot eat a newer build's comments.

Anchors are stored in PDF page space, so zoom, rotation and resize only
re-project. Highlight colors are semantic tokens resolved by ThemeService
and injected as CSS custom properties, never literals.

Writes go through one per-file FIFO: saves coalesce, and the sibling
rename/move/delete operations are queued on that same FIFO rather than
run inline, which removes a race where a rename that ran before a pending
worker left the sidecar orphaned under the old name. Bundled writes take
NotebookIoGate. comments.json is written with a plain QSaveFile, so no
vxcore file event is emitted; CommentService::storeDirty feeds the fact
into SyncService's existing auto-sync debounce.

A failed save keeps the file dirty and retryable, and a malformed store
goes read-only instead of presenting as empty -- otherwise the next edit
would overwrite a file the user could still recover by hand.

Highlights are created from the page context menu (select text ->
Highlight -> color). Alt+drag is a shortcut only; a feature reachable
solely by a modifier key nobody is told about is not reachable at all.
2026-08-25 23:52:00 +08:00
..