fix(reader): make device-synced highlights editable on the web
Device-synced highlights paint with a synthesized range CFI (renderCfi, built by toRangeCfi from the stored point CFIs and selection text) while the stored locator stays a point CFI. Three follow-ons from that split: - show-annotation (click-to-edit) matched the clicked value against the stored point cfi only, so clicking a device-created highlight never opened the edit popover — it listed in the drawer but was uneditable. Match either the stored cfi or the renderCfi the overlay was added by. - deleteHighlightById removed the overlay with the stored point cfi, which never matched the painted value; the highlight box lingered until reload. Delete with the value it was added by. - saveHighlightChanges re-added the overlay without removing the old value; an edit that changes the synthesized range (note/text edits change the UTF-16 length it derives from) would ghost the old paint beside the new one. Remove the previous overlay value first when the edit changed it. Web-created highlights are unaffected: their stored CFI is already a native range, so renderCfi === cfi for them.
This commit is contained in:
@@ -826,7 +826,13 @@ document.addEventListener("alpine:init", () => {
|
|||||||
});
|
});
|
||||||
this.view.addEventListener("show-annotation", (e: any) => {
|
this.view.addEventListener("show-annotation", (e: any) => {
|
||||||
const { value, index, range } = e.detail;
|
const { value, index, range } = e.detail;
|
||||||
const h = this.highlightItems.find((x) => x.cfi === value);
|
// Device-synced highlights are painted with a synthesized range
|
||||||
|
// CFI (renderCfi) while the stored locator stays a point CFI, so a
|
||||||
|
// click reports the render value — match either or editing
|
||||||
|
// device highlights is impossible.
|
||||||
|
const h = this.highlightItems.find(
|
||||||
|
(x) => x.cfi === value || x.renderCfi === value,
|
||||||
|
);
|
||||||
if (!h) return;
|
if (!h) return;
|
||||||
const doc = this.renderer
|
const doc = this.renderer
|
||||||
?.getContents?.()
|
?.getContents?.()
|
||||||
@@ -1343,7 +1349,23 @@ document.addEventListener("alpine:init", () => {
|
|||||||
if (!resp.ok) return;
|
if (!resp.ok) return;
|
||||||
const row = await resp.json();
|
const row = await resp.json();
|
||||||
const idx = this.highlightItems.findIndex((h) => h.id === p.id);
|
const idx = this.highlightItems.findIndex((h) => h.id === p.id);
|
||||||
|
// The overlay is keyed by the value it was added with; an edit can
|
||||||
|
// change it (note/text edits change the synthesized range), so
|
||||||
|
// remove the old paint before re-adding or it ghosts.
|
||||||
|
const oldValue =
|
||||||
|
idx !== -1
|
||||||
|
? this.highlightItems[idx].renderCfi ||
|
||||||
|
this.highlightItems[idx].cfi
|
||||||
|
: "";
|
||||||
if (idx !== -1) this.highlightItems[idx] = this.mapHighlightRow(row);
|
if (idx !== -1) this.highlightItems[idx] = this.mapHighlightRow(row);
|
||||||
|
const newValue =
|
||||||
|
idx !== -1
|
||||||
|
? this.highlightItems[idx].renderCfi ||
|
||||||
|
this.highlightItems[idx].cfi
|
||||||
|
: "";
|
||||||
|
if (p.pdfPage < 0 && oldValue && oldValue !== newValue) {
|
||||||
|
this.view?.deleteAnnotation({ value: oldValue });
|
||||||
|
}
|
||||||
// Re-add so the overlay redraws with the new color.
|
// Re-add so the overlay redraws with the new color.
|
||||||
if (p.pdfPage >= 0) {
|
if (p.pdfPage >= 0) {
|
||||||
this.renderer?.addRectAnnotation?.({
|
this.renderer?.addRectAnnotation?.({
|
||||||
@@ -1379,7 +1401,11 @@ document.addEventListener("alpine:init", () => {
|
|||||||
if (hl?.pdfPage >= 0) {
|
if (hl?.pdfPage >= 0) {
|
||||||
this.renderer?.removeRectAnnotation?.(id);
|
this.renderer?.removeRectAnnotation?.(id);
|
||||||
} else if (hl?.cfi) {
|
} else if (hl?.cfi) {
|
||||||
this.view?.deleteAnnotation({ value: hl.cfi });
|
// Delete with the value the overlay was added by: device-synced
|
||||||
|
// highlights paint a synthesized range, not the stored point CFI.
|
||||||
|
this.view?.deleteAnnotation({
|
||||||
|
value: hl.renderCfi || hl.cfi,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
this.hideSelectionPopover();
|
this.hideSelectionPopover();
|
||||||
} catch (_e) {
|
} catch (_e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user