Found while doing live acceptance for #252, on Hatchdoor 2.6.1. It is not that bug and not caused by its fix: escaped and unescaped wikilinks are affected identically.
What happens
Click inside a rendered link to start editing that block. The block flips to its Markdown source and the caret lands in the wrong place, roughly where the click was on screen rather than at the character you clicked.
Reproduce
Note body, one paragraph:
See [[Some Note|alias]] TAILWORD here.
It renders as See alias TAILWORD here.. Click between TAIL and WORD, which is rendered offset 19. The caret should land at source offset 50, inside TAILWORD. It lands at 34, one character into alias, inside the link's target.
Measured in the live editor against just dev-start, with a real mouse click:
| link form |
source |
caret lands |
should be |
[[Some Note|alias]] |
60 chars |
34 |
50 |
[[Some Note\\|alias]] |
61 chars |
35 |
51 |
The one-character difference between the two rows is just the extra escape character, which is what shows the two forms behave alike.
Cause
caretSourceOffset in frontend/src/components/note-page/EditableBlock.tsx (around line 45) does:
const position = doc.caretPositionFromPoint?.(clientX, clientY);
if (position) {
return sourceOffsetForRenderedOffset(source, position.offset);
}
caretPositionFromPoint returns an offset within the text node under the pointer, but sourceOffsetForRenderedOffset documents and expects an offset within the whole rendered block. In the example the click is inside the TAILWORD here. text node, so position.offset is 5, not 19, and the map faithfully converts the wrong number. The same applies to the WebKit caretRangeFromPoint branch just below it.
sourceOffsetForRenderedOffset itself is correct: given 19 it returns 50.
Scope
Any block whose rendered output splits into more than one node is affected, so links, bold, italic and inline code, not only wikilinks. A block of plain prose is a single text node, where the node-relative and block-relative offsets coincide, which is why this has gone unnoticed.
Suggested fix
Walk from the block root to the returned node and add up the text length in front of it, then pass that block-relative offset to sourceOffsetForRenderedOffset. Worth a test per inline construct, since the current unit tests exercise the map with correct input and so never catch the caller.
Found while doing live acceptance for #252, on Hatchdoor 2.6.1. It is not that bug and not caused by its fix: escaped and unescaped wikilinks are affected identically.
What happens
Click inside a rendered link to start editing that block. The block flips to its Markdown source and the caret lands in the wrong place, roughly where the click was on screen rather than at the character you clicked.
Reproduce
Note body, one paragraph:
It renders as
See alias TAILWORD here.. Click betweenTAILandWORD, which is rendered offset 19. The caret should land at source offset 50, insideTAILWORD. It lands at 34, one character intoalias, inside the link's target.Measured in the live editor against
just dev-start, with a real mouse click:[[Some Note|alias]][[Some Note\\|alias]]The one-character difference between the two rows is just the extra escape character, which is what shows the two forms behave alike.
Cause
caretSourceOffsetinfrontend/src/components/note-page/EditableBlock.tsx(around line 45) does:caretPositionFromPointreturns an offset within the text node under the pointer, butsourceOffsetForRenderedOffsetdocuments and expects an offset within the whole rendered block. In the example the click is inside theTAILWORD here.text node, soposition.offsetis 5, not 19, and the map faithfully converts the wrong number. The same applies to the WebKitcaretRangeFromPointbranch just below it.sourceOffsetForRenderedOffsetitself is correct: given 19 it returns 50.Scope
Any block whose rendered output splits into more than one node is affected, so links, bold, italic and inline code, not only wikilinks. A block of plain prose is a single text node, where the node-relative and block-relative offsets coincide, which is why this has gone unnoticed.
Suggested fix
Walk from the block root to the returned node and add up the text length in front of it, then pass that block-relative offset to
sourceOffsetForRenderedOffset. Worth a test per inline construct, since the current unit tests exercise the map with correct input and so never catch the caller.