Render clickable Supernote internal links in SupernoteView - #128
Merged
Conversation
Closes #127. Buckets each page's ILink entries (from the link parsing merged in #121) and overlays a clickable, invisibly-positioned region for each LINKRECT, scaled/repositioned by the same mechanism that already keeps the pdf.js text layer aligned through zoom. Click handling resolves same-file links via PAGEID against the currently loaded pages, and cross-file links by basename against the vault (mirroring VaultWriter.resolvePageAnchor's export-time resolution from PR #122), reusing the existing #page=N ephemeral-state anchor for the cross-file page jump rather than inventing a new one. parseLinkRect/bucketLinksByPage live in a new obsidian-free module (src/linkOverlay.ts) so they're unit-testable, including a real-fixture test that verifies LINKRECT's coordinate space against actual parsed data rather than trusting it from a doc comment. Also fixes the vitest/eslint/tsc plumbing needed for a test to read a real .note file from disk (the first test in this repo to need any Node builtins).
Testing against a real note with two internal links showed no links rendering at all. Root cause was two bugs stacked: - The LINKTYPE === '1' filter dropped both real links (their LINKTYPE was '0') — the doc comment claiming '1 = internal note link' doesn't hold up against real data. - bucketLinksByPage used ILink.OBJPAGE for the source page, which looked reasonable but is wrong: verified by drawing each link's LINKRECT on every rendered page and measuring ink coverage under it, OBJPAGE (4 and 1) didn't match either link's actual page (array index 0 and 3) under any indexing convention. It tracks something else -- looks like the page's own template label, which had drifted from its current array position. The original sn.links Record key's first-4-characters convention (1-indexed page, taken from PR #122) was actually right; reverting to it. It looked broken against the submodule's own link-tag fixture only because all three of its links happen to share one page and one key prefix -- confirmed by re-checking that fixture's exact expected bucketing rather than just "some valid page index".
Collaborator
Author
|
Pushed a fix for a real bug found while testing against an actual note with two internal links: nothing rendered at all. Root cause was two stacked mistakes in `bucketLinksByPage`:
Reverted to the original `sn.links` key-prefix convention (1-indexed page in the key's first 4 characters, from PR #122) — that turned out to be correct all along. It only looked broken against the submodule's own link-tag test fixture because all three of that fixture's links happen to share one page and one key prefix, which I mistook for evidence the key was meaningless. Re-verified with an exact expected-bucketing assertion against that fixture, not just "some valid page index" like before. |
linkOverlay.test.ts is the first test to import supernote-typescript at runtime; the test job never checked out or built the submodule (only lint/build/test-submodule did), so it failed on CI with "Cannot find package 'supernote-typescript'" even though it passed locally. Mirrors the lint job's existing submodule checkout + build steps.
Writes up what building the clickable link overlay found: LINKTYPE and OBJPAGE don't behave the way their doc comments claim, verified against real notes by rendering pages and measuring where each LINKRECT's ink actually falls. Cross-references the upstream issue (philips/supernote-typescript#32) filed with the same findings.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #127.
Summary
Renders each page's Supernote internal links (
ILink/sn.links, parsed since #121's submodule bump) as a clickable overlay inSupernoteView:LINKRECTregions are positioned/scaled using the exact same mechanism (drawPageImage()) that already keeps the pdf.js text layer aligned through zoom — a new.supernote-links-layersits alongside the existing.textLayer, sized identically, carried along by the same CSS transform during a zoom drag.goToPage(), resolvingPAGEIDagainst the currently-loaded file's own pages.VaultWriter.resolvePageAnchor()'s export-time link resolution from Export Supernote keywords as tags and links as Wikilinks #122 (so a link resolves to the same note whether you're viewing it live or exporting it), then open it via a new leaf using the existing#page=Nephemeral-state anchor — the same mechanism a regular[[note#page=N]]link already uses — rather than inventing a new cross-view API.Noticerather than failing silently or (Obsidian's defaultopenLinkTextbehavior) creating an empty note.parseLinkRect/bucketLinksByPagelive in a new Obsidian-free module (src/linkOverlay.ts) so they're unit-testable —main.tsextends several Obsidian base classes and has never had test coverage as a result. Includes a test that loads the submodule's own real link-tag fixture (nomad-3.26.40-link-tag-3p.note) and checksLINKRECTdecodes to values within[0, pageWidth] x [0, pageHeight], concretely verifying the coordinate-space assumption the overlay math depends on instead of trusting it from a doc comment.This is the first test in the repo that reads a real file from disk, which needed some plumbing:
vitest.config.tsnow aliasessupernote-typescriptto the submodule's compiledlib/(mirroringtsconfig.json/esbuild.config.mjs), and a smallsrc/node-shims.d.tsgivesfs/path/import.meta.dirnamenarrow ambient types without pulling in all of@types/node— doing that project-wide collides with the DOM lib'sWindow.setTimeouttyping thatdeviceFetch.ts/main.tsalready rely on (window.setTimeout's return type goes ambiguous between DOM'snumberand Node'sNodeJS.Timeoutonce both are in scope).Scope decisions
SupernoteEmbed(the simpler, separate embed renderer) is out of scope — Clickable links in SupernoteView? #127 asks specifically aboutSupernoteView.Keymap.isModEventin this codebase; v1 always reuses the current-most-recent leaf.Test plan
npx vitest run— 32 passed (including the 10 newlinkOverlay.test.tscases)npx tsc --noEmit --skipLibCheck— cleannpx eslint src/main.ts src/linkOverlay.ts src/linkOverlay.test.ts src/node-shims.d.ts— cleannpm run build— clean