fix: /Link to note opens a live note picker instead of a dead [[ - #83
Merged
Conversation
Picking `Link to note` rewrote the slash trigger into `[[` and stopped there, so the note picker never reopened — the user was left with a stranded `[[dep` in their prose and no popup. The empty-query guard in `noteLinkSource` is only half of it. The apply dispatch carries a selection but no `input.type` user event, and `@codemirror/autocomplete`'s `getUpdateType` maps that to `UpdateType.Reset`, forcing every source Inactive — so the source was never re-queried even with a query seeded. Only an explicit `startCompletion` (or a real keystroke, which is why the bug read as flaky) can bring it back. Both handoffs now route through `openNotePicker`, which dispatches the trigger and re-arms completion. The `explicit` flag it sets is what lets a query-less `[[` list the whole catalog without weakening the guard that keeps stray brackets from popping the list open mid-sentence. The Link handoff is additionally marked link-only through a state field holding the trigger's offset, so its picker offers existing notes only. Link is a peer hyperlink, never a nesting create (#80): with no match, `Create "…"` would otherwise sit pre-selected one Enter from filing the "linked" note as a sub-note. A zero-match link picker shows a non-actionable row instead of closing, so backspacing narrows straight back to real notes. Also excludes the active note from link candidates — self-linking is never useful. `note-links.dom.test.ts` drives the real `autocompletion()` plugin, because a source-level test cannot see a popup that dies in CodeMirror's transaction handling. 9 of its 10 cases fail against the old code; the tenth is the hand-typed `[[` control. Closes #82 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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 #82.
The bug
/→ Link to note was a dead end. It rewrote the slash trigger into[[(optionally seeded with the typed query) and stopped there — the picker never
reopened, leaving a stranded
[[depin the user's prose with no popup and nodiscoverable way forward.
Root cause
The obvious suspect is the empty-query guard in
noteLinkSource:That is only half of it, and fixing it alone would have changed nothing.
The apply dispatch used
userEvent: "input"and set a selection.@codemirror/autocomplete@6.20.3getUpdateTypereads that as neitherinput.typenorinput.complete—"input"is not a parent event of"input.type"; the dot-prefix relation runs the other way — falls through totr.selection ? UpdateType.Reset, andActiveSource.updateforces every sourceInactive. An inactive source is never re-queried, so
[[depwas just as deadas bare
[[. Only an explicitstartCompletion— or a real keystroke, which iswhy the bug read as flaky rather than deterministic — could bring the popup back.
The fix
Both handoffs (
Link to note, and bareNew note, which had the same deadtrigger) route through one
openNotePickerhelper that dispatches the triggerand then calls
startCompletion. Theexplicitflag that sets is also whatlets a query-less
[[list the whole catalog without weakening the guardthat keeps a stray bracket pair from popping the list open mid-sentence.
The Link handoff is additionally marked link-only via a
StateFieldholdingthe trigger's document offset. Link is a peer hyperlink, never a nesting create
(#80) — with no match,
Create "…"would otherwise sit pre-selected oneEnter from filing the "linked" note as a sub-note of the one being
edited (#81). The offset (rather than a boolean) scopes the flag to the one
trigger it was set for, and it clears as soon as an edit overlaps the brackets
themselves, so deleting them and typing
[[by hand restores pick-or-create.A zero-match link-only picker shows a non-actionable
No note matches "…"rowrather than closing — an empty result closes the popup, and a closed popup over a
live
[[is exactly the dead trigger this is fixing. Keeping the source activemeans backspacing narrows straight back to real notes.
Also excludes the active note from link candidates: self-linking is never useful.
Before / after
/→ Link to note[[, popup closed[[, picker open on the full catalog/dep→ Link to note[[dep, popup closed[[dep, picker open, filtered to "dep"/dep→ Link to note, no matchCreate "dep"pre-selected → sub-noteNo note matches "dep", nothing created/→ New note (no title)[[, popup closed[[, picker open, Create appears once titled[[depTests
New
lib/editor/note-links.dom.test.tsdrives the realautocompletion()plugin in a mounted
EditorView— a source-level test cannot see this bug class,since the popup dies inside CodeMirror's transaction handling, not in what the
source returns. 9 of its 10 cases fail against the pre-fix code; the tenth is
the hand-typed
[[control, which passes both ways by design.Covered: picker opens on bare and seeded handoff; narrowing across a space;
no
Createin a link-only picker andcreateNotenever called; recovery from anon-matching query; the picked note inserting
[Title](/n/{id});New notestill offering Create; and link-only not leaking onto a hand-typed
[[.Verification
pnpm vitest run— 355 passed / 34 files, including the True sub-notes: parent_id tree in sidebar (Zed project-panel feel) #80 create≠link matrix untouchedpnpm exec tsc --noEmit— cleanpnpm build— cleaneslinton the touched paths — 8 problems before, 8 after (all pre-existing); no new debtNotes
[[remains a transient trigger; storage is still plain[Title](/n/{id})Markdown, so the CRDT, publish, and
note_revisionspaths are untouched. Thestate field is local editor state and never reaches Yjs.
agentnoteCompletionsits in the shared extension array used with and withoutyCollab, so the CRDT path gets the same behavior by construction.startCompletion); the RCA's dependence ongetUpdateTypeinternals cannot silently reintroduce the bug on an upgrade,and the DOM tests would catch it if it tried.
🤖 Generated with Claude Code