Skip to content

feat: preview and open the footnote a reference names - #417

Merged
Azganoth merged 5 commits into
mainfrom
feature/footnote-definition-preview
Sep 6, 2026
Merged

feat: preview and open the footnote a reference names#417
Azganoth merged 5 commits into
mainfrom
feature/footnote-definition-preview

Conversation

@Azganoth

@Azganoth Azganoth commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Summary

A footnote reference exposed its editable [^label] source but said nothing about the footnote it named, so reading one meant finding the definition elsewhere in the file and then finding the way back. A reference now previews its definition and navigates to it.

The issue held three interaction decisions open before Ready. They are answered here as: the preview holds no control and takes no focus, so it stays a semantic tooltip rather than the popover the issue would otherwise have required; pointer and keyboard reach the preview by their own gestures — hover after a delay, and Mod+Alt+P on the reference the caret reads; and navigation is a separate gesture rather than a control inside the preview.

  • Navigation takes the modifier click, which is the rule a link already follows, so the plain click keeps opening the reference's own source projection and the existing gesture is not taken away. The keyboard reaches the same move through the Edit menu's Jump group, with no shortcut of its own, as Jump to selection has none.
  • The label a caret reads is taken from the projected source rather than from the node the projection opened on, so a label being edited resolves as it is typed. The reference node stays the fallback for a selection that reaches one before a projection stands in its place.
  • The pointer anchors to the rendered reference element and the keyboard anchors to the measured selection, because a reference the caret is reading has been replaced by its projected source and has no element left to anchor to. The measured anchor is the one the context popup already uses for its keyboard opens.
  • Base UI names a popup through its trigger's aria-describedby, which an anchored, caret-driven preview has none of, so the role and the live region are set on the content itself.
  • The preview closes on mousedown. The pointer stays on the reference through a modifier click, so without that the preview stayed open over the definition the click had just navigated to. A modifier pressed on its own keeps it, because holding one is how a pointer navigates from the preview it is reading.
  • The preview reports the definition's body as one line of text, cut short past a character limit, and never touches the document. The caret lands at the start of the definition's body rather than in its label, so navigating does not open the label's rename edit.

A reference no definition answers to is only reachable from inside a session. A file never opens holding one: remark reads a label without a definition as literal text rather than a reference, so the state arises by relabelling a reference to a name no definition answers to, or by deleting the definition it named. That is what the missing-definition state covers, and it is why the tests build it that way rather than by opening a file that spells it.

Related Issue

Closes #127

Verification

Focused tests drive the editor mount the projection and plugin tests use.

  • A label resolves to the definition the document holds, from the reference node and from an active projection, including a label resolving as it is edited; references sharing a label resolve to the one definition.
  • The preview opens on the pointer after its delay and on Mod+Alt+P from the caret, carrying the same definition either way, and reports an explicit missing-definition state where the label answers to none.
  • The preview closes on the pointer leaving, on Escape, and on a click, and survives a modifier held on its own.
  • The pointer preview anchors to the reference element and the keyboard preview to the measured selection.
  • The rendered preview is a tooltip carrying no focusable content, and renders nothing until one is requested.
  • A modifier click and the command both land the caret in the matching definition's body; a plain click is left to the source projection; neither the Markdown, the dirty state, nor the undo history moves.
  • The command is unavailable where the caret reads no reference and where the reference's definition is gone.

Manually verified in Chrome against a temporary Vite route mounting the editor: the pointer preview opens after its delay anchored above the reference with the definition's text, and a modifier click lands the caret in the matching definition's body.

pnpm check:frontend passes, and the coverage floor holds. The change is frontend-only.

Not verified: the on-screen position of the keyboard preview, and whether the live region announces it. Chrome stopped delivering synthetic input to the ProseMirror surface partway through the manual pass, so the caret could not be placed in a reference there; the anchor it uses is asserted in the tests and is the one the context popup already ships. The desktop E2E suite, which CI runs as its own job, and backend checks, since nothing under src-tauri/ changed.

Notes

TooltipContent gains an anchor passthrough to its positioner, mirroring what PopoverContent already exposes. It is a passthrough on an existing primitive rather than a new one.

findFootnoteDefinitions moves out of the definition-label plugin into the utility both it and the new resolution share, so the document scan has one owner.

The label a caret reads is taken from the projected source rather than the
node the session opened on, so a label being edited resolves as it is typed.
The reference node stays the fallback for a selection that reaches one before
a projection stands in its place.
Navigation takes the modifier click, which is the rule a link already follows,
so the plain click keeps opening the reference's own source. The keyboard
reaches the same move through the Edit menu's Jump group rather than a
shortcut of its own, as Jump to selection does.

A reference no definition answers to is only reachable from inside a session:
remark reads a label without a definition as literal text, so the state comes
from relabelling a reference or deleting the definition it named.
The preview carries no control and takes no focus, so it stays a semantic
tooltip rather than a popover. Base UI names a popup through its trigger's
aria-describedby, which an anchored, caret-driven preview has none of, so the
role and the live region are set on the content itself.

The pointer anchors to the rendered reference element; the keyboard anchors to
the measured selection, because a reference the caret is reading has been
replaced by its projected source and no longer has an element of its own.
The pointer stays on the reference through a modifier click, so without this
the preview stayed open over the definition the click had just navigated to.
A modifier pressed on its own keeps it, because holding one is how a pointer
navigates from the preview it is reading.
@Azganoth Azganoth added the Feature New feature or request label Sep 6, 2026
@Azganoth Azganoth self-assigned this Sep 6, 2026
The preview is anchored to the reference the label is written on, so the line
repeated what the document was already showing under it.
@Azganoth
Azganoth enabled auto-merge (squash) September 6, 2026 07:06
@Azganoth
Azganoth merged commit 769ce6c into main Sep 6, 2026
3 checks passed
@Azganoth
Azganoth deleted the feature/footnote-definition-preview branch September 6, 2026 07:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Preview footnote definitions from references

1 participant