Skip to content

Commit a591792

Browse files
committed
fix(core): scan the selection for the link URL instead of probing a boundary
The `from + 1` probe fixed the left-edge case (`marks()` excludes a link at its left boundary) but is still fragile: browsers disagree by a position on where a selection over a link starts, so a single-position lookup can land outside the mark either way. For a non-empty selection, scan the selected range for the first link mark instead; an empty selection keeps the plain position lookup.
1 parent 039fb60 commit a591792

2 files changed

Lines changed: 24 additions & 9 deletions

File tree

‎packages/core/src/editor/managers/StyleManager.ts‎

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,13 +183,26 @@ export class StyleManager<
183183
*/
184184
public getSelectedLinkUrl() {
185185
return this.editor.transact((tr) => {
186-
// `from + 1` for the same boundary reason as `editLink` below: at the
187-
// left edge of a link (e.g. when the whole link is selected), the mark
188-
// lookup at `from` itself misses the mark and the link's URL would
189-
// incorrectly read as absent.
190-
return this.getLinkMarkAtPos(
191-
Math.min(tr.selection.from + 1, tr.doc.content.size),
192-
)?.href;
186+
const { from, to, empty } = tr.selection;
187+
if (empty) {
188+
return this.getLinkMarkAtPos(from)?.href;
189+
}
190+
// For a non-empty selection, probing a single boundary position is
191+
// fragile twice over: `marks()` excludes a link at its left edge, and
192+
// browsers disagree by a position on where a selection over a link
193+
// starts. Scan the selected range for the first link mark instead.
194+
let href: string | undefined;
195+
tr.doc.nodesBetween(from, to, (node) => {
196+
if (href !== undefined) {
197+
return false;
198+
}
199+
const linkMark = node.marks.find((mark) => mark.type.name === "link");
200+
if (linkMark) {
201+
href = linkMark.attrs.href;
202+
}
203+
return href === undefined;
204+
});
205+
return href;
193206
});
194207
}
195208

‎tests/src/end-to-end/mobile/mobileToolbar.test.tsx‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,10 @@ describe("Mobile formatting toolbar", () => {
141141
});
142142

143143
// Reopening the popover with the whole link selected must pre-fill its
144-
// URL: `getSelectedLinkUrl` reads the mark just inside the selection
145-
// start, since a lookup exactly at the link's left boundary misses it.
144+
// URL: `getSelectedLinkUrl` scans the selected range for the link mark,
145+
// since a probe at a single boundary position misses it — `marks()`
146+
// excludes a link at its left edge, and browsers disagree by a position
147+
// on where a selection over a link starts.
146148
await userEvent.keyboard("{Shift>}{Home}{/Shift}");
147149
await userEvent.click(
148150
await waitForSelector(

0 commit comments

Comments
 (0)