Summary
When a user selects text in the web EPUB reader, the text_selected pipeline does not produce locators optimal for highlight/annotation persistence and cross-device sync.
Today the injectable sends only the selected string and viewport coordinates. The navigator then builds a minimal selection.locator with href, type, and text.highlight only. Consumers that store Readium-style annotation locators (matching kotlin-toolkit / Readium LCP annotation shape) end up with sparse data: no text.before / text.after, no locations.position / locations.totalProgression, and no character offset (target).
Motivation
Annotation locators are used to:
- Re-anchor highlights after layout or font changes (TextQuoteSelector via
rangeFromLocator in injectables — needs before / highlight / after)
- Sync annotations between web and native readers (kotlin-toolkit already emits full locators)
- Disambiguate duplicate phrases on the same page (
target + text context)
Examples
|
onPointUp(event: PointerEvent) { |
|
const selection = this.wnd.getSelection(); |
|
if (!!selection && selection.toString()?.length > 0) { |
|
const domRectList = selection.getRangeAt(0)?.getClientRects(); |
|
// Sanity check to avoid sending empty selections |
|
if (!domRectList || domRectList.length === 0) { |
|
return; |
|
} |
|
const rect = domRectList[0]; |
|
const textSelection = { |
|
text: selection.toString(), |
|
x: rect.x, |
|
y: rect.y, |
|
width: rect.width, |
|
height: rect.height, |
|
targetFrameSrc: this.wnd?.location?.href |
|
} |
|
this.comms.send("text_selected", textSelection as BasicTextSelection); |
|
selection.locator = new Locator({ href, type: link!.type || "application/xhtml+xml", text: new LocatorText({ highlight: selection.text }) }); |
The code that generates the selected text event needs expanding with something like:
const range = selection.getRangeAt(0);
const root = document.body;
const beforeRange = range.cloneRange();
beforeRange.selectNodeContents(root);
beforeRange.setEnd(range.startContainer, range.startOffset);
const afterRange = range.cloneRange();
afterRange.selectNodeContents(root);
afterRange.setStart(range.endContainer, range.endOffset);
const CONTEXT_CHARS = 160;
const before = beforeRange.toString().replace(/\s+/g, " ").trim().slice(-CONTEXT_CHARS);
const after = afterRange.toString().replace(/\s+/g, " ").trim().slice(0, CONTEXT_CHARS);
import { TextRange } from "../vendor/hypothesis/anchoring/text-range.ts";
const target = TextRange.fromRange(range).relativeTo(root).start.offset;
Also update: types/src/modules/Peripherals.d.ts — add optional before, after, target on BasicTextSelection.
Current (EPUB):
selection.locator = new Locator({
href,
type: link!.type || "application/xhtml+xml",
text: new LocatorText({ highlight: selection.text }),
});
After the changes:
-
Build full LocatorText from injectable payload:
new LocatorText({
before: selection.before,
highlight: selection.text,
after: selection.after,
})
-
Merge this.currentLocation.locations (position, progression, totalProgression) into the new locator.
-
Attach @readium/shared — Locator.target
- kotlin-toolkit and several annotation stores use
locator.target as a character offset in the resource. The web Locator class does not define or serialize this field today.
- Consider adding optional
target?: number to Locator (serialize/deserialize) so annotation consumers do not rely on Object.assign extensions.
Summary
When a user selects text in the web EPUB reader, the
text_selectedpipeline does not produce locators optimal for highlight/annotation persistence and cross-device sync.Today the injectable sends only the selected string and viewport coordinates. The navigator then builds a minimal
selection.locatorwithhref,type, andtext.highlightonly. Consumers that store Readium-style annotation locators (matching kotlin-toolkit / Readium LCP annotation shape) end up with sparse data: notext.before/text.after, nolocations.position/locations.totalProgression, and no character offset (target).Motivation
Annotation locators are used to:
rangeFromLocatorin injectables — needsbefore/highlight/after)target+ text context)Examples
ts-toolkit/navigator-html-injectables/src/modules/Peripherals.ts
Lines 404 to 421 in 9e763e3
ts-toolkit/navigator/src/epub/EpubNavigator.ts
Line 468 in 9e763e3
The code that generates the selected text event needs expanding with something like:
Also update:
types/src/modules/Peripherals.d.ts— add optionalbefore,after,targetonBasicTextSelection.Current (EPUB):
After the changes:
Build full
LocatorTextfrom injectable payload:Merge
this.currentLocation.locations(position,progression,totalProgression) into the new locator.Attach
@readium/shared—Locator.targetlocator.targetas a character offset in the resource. The webLocatorclass does not define or serialize this field today.target?: numbertoLocator(serialize/deserialize) so annotation consumers do not rely onObject.assignextensions.