forked from akai/readest
A highlight and its note are now a single BookNote. Adding a note attaches it to the highlight at that CFI (or creates one with the current global style) instead of creating a second record, and a unified record renders as both a highlight overlay and a note bubble. - onDrawAnnotation chooses the draw kind from the overlay value prefix (cfi -> highlight, NOTE_PREFIX -> bubble) instead of annotation.note, so a record with both a style and a note draws both. Fixes notes synced from KOReader losing their highlight (#4511). - handleSaveNote updates the existing annotation at the CFI rather than pushing a new record (#3870); re-styling preserves the note. - unifyAnnotations migration (book config schema v1 -> v2, run in deserializeConfig) collapses existing split highlight+note records into one survivor and tombstones the redundant record (deletedAt) so the merge syncs to the cloud and KOReader. - Sidebar: a note's quoted highlight text uses the theme foreground so it stays legible on the highlight background. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -229,3 +229,50 @@ export function buildTTSSentenceHighlight(
|
||||
...params,
|
||||
};
|
||||
}
|
||||
|
||||
export type AnnotationDrawKind = 'bubble' | 'highlight' | 'underline' | 'squiggly' | 'none';
|
||||
|
||||
/**
|
||||
* Decide what an overlay should draw for an annotation. The bubble vs.
|
||||
* highlight choice keys off the overlay's `value` prefix — NOT `annotation.note`
|
||||
* — so a single unified record draws BOTH its highlight overlay (value = cfi)
|
||||
* and its note bubble (value = `${NOTE_PREFIX}${cfi}`).
|
||||
*/
|
||||
export function decideAnnotationDraw(
|
||||
value: string | undefined,
|
||||
style: HighlightStyle | undefined,
|
||||
): AnnotationDrawKind {
|
||||
if (value?.startsWith(NOTE_PREFIX)) return 'bubble';
|
||||
if (style === 'highlight') return 'highlight';
|
||||
if (style === 'underline' || style === 'squiggly') return style;
|
||||
return 'none';
|
||||
}
|
||||
|
||||
/**
|
||||
* Index of the live (`!deletedAt`) annotation record at `cfi`, or -1. Used when
|
||||
* adding a note so it attaches to the existing highlight instead of creating a
|
||||
* second record at the same position.
|
||||
*/
|
||||
export function findAnnotationAtCfi(booknotes: BookNote[], cfi: string): number {
|
||||
return booknotes.findIndex(
|
||||
(note) => note.type === 'annotation' && note.cfi === cfi && !note.deletedAt,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge a freshly-built restyle (`restyled`, carrying the new style/color) onto
|
||||
* an `existing` annotation, preserving the parts a restyle must not lose: the
|
||||
* record id, its note text, the selected text, the original creation time, and
|
||||
* the `global` flag. Without preserving `note`, recoloring a unified annotation
|
||||
* would wipe the note.
|
||||
*/
|
||||
export function mergeRestyledAnnotation(existing: BookNote, restyled: BookNote): BookNote {
|
||||
return {
|
||||
...restyled,
|
||||
id: existing.id,
|
||||
createdAt: existing.createdAt,
|
||||
note: existing.note,
|
||||
text: existing.text ?? restyled.text,
|
||||
global: existing.global || restyled.global,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user