Archived
- add uv configuration for the backend - update frontend to make auth work - add new auth endpoints - add bookmars feat - add reader feat
96 lines
2.5 KiB
TypeScript
96 lines
2.5 KiB
TypeScript
import type { EpubRendition } from "./epubRendition";
|
|
import { normalizeHighlightColor } from "@/constants/bookmarkHighlightColors";
|
|
|
|
export interface BookmarkHighlight {
|
|
id: string;
|
|
epub_cfi: string;
|
|
highlight_color?: string | null;
|
|
}
|
|
|
|
type EpubAnnotations = {
|
|
highlight?: (
|
|
cfiRange: string,
|
|
data: Record<string, unknown>,
|
|
callback?: () => void,
|
|
id?: string,
|
|
styles?: Record<string, string>,
|
|
) => void;
|
|
remove?: (cfiRange: string, type: string) => void;
|
|
};
|
|
|
|
export type EpubRenditionWithHighlights = EpubRendition & {
|
|
annotations?: EpubAnnotations;
|
|
on?: (event: string, handler: () => void) => void;
|
|
off?: (event: string, handler: () => void) => void;
|
|
};
|
|
|
|
function annotationId(bookmarkId: string): string {
|
|
return `bookmark-hl-${bookmarkId}`;
|
|
}
|
|
|
|
export function applyBookmarkHighlight(
|
|
rendition: EpubRenditionWithHighlights,
|
|
bookmark: BookmarkHighlight,
|
|
): void {
|
|
const cfi = (bookmark.epub_cfi || "").trim();
|
|
if (!cfi || !rendition.annotations?.highlight) return;
|
|
|
|
const color = normalizeHighlightColor(bookmark.highlight_color);
|
|
try {
|
|
rendition.annotations.highlight(
|
|
cfi,
|
|
{},
|
|
undefined,
|
|
annotationId(bookmark.id),
|
|
{
|
|
fill: color,
|
|
"fill-opacity": "0.45",
|
|
"mix-blend-mode": "multiply",
|
|
},
|
|
);
|
|
} catch {
|
|
/* CFI may not be in the current spine slice */
|
|
}
|
|
}
|
|
|
|
export function removeBookmarkHighlight(
|
|
rendition: EpubRenditionWithHighlights,
|
|
bookmark: BookmarkHighlight,
|
|
): void {
|
|
const cfi = (bookmark.epub_cfi || "").trim();
|
|
if (!cfi || !rendition.annotations?.remove) return;
|
|
try {
|
|
rendition.annotations.remove(cfi, "highlight");
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
}
|
|
|
|
export function syncBookmarkHighlights(
|
|
rendition: EpubRenditionWithHighlights | null,
|
|
bookmarks: BookmarkHighlight[],
|
|
applied: Map<string, string>,
|
|
): void {
|
|
if (!rendition?.annotations?.highlight) return;
|
|
|
|
const nextById = new Map(bookmarks.map((b) => [b.id, b]));
|
|
|
|
for (const [id, cfi] of applied) {
|
|
if (!nextById.has(id)) {
|
|
removeBookmarkHighlight(rendition, { id, epub_cfi: cfi });
|
|
applied.delete(id);
|
|
}
|
|
}
|
|
|
|
for (const bookmark of bookmarks) {
|
|
const prevCfi = applied.get(bookmark.id);
|
|
if (prevCfi && prevCfi !== bookmark.epub_cfi) {
|
|
removeBookmarkHighlight(rendition, { id: bookmark.id, epub_cfi: prevCfi });
|
|
applied.delete(bookmark.id);
|
|
}
|
|
if (applied.has(bookmark.id)) continue;
|
|
applyBookmarkHighlight(rendition, bookmark);
|
|
applied.set(bookmark.id, bookmark.epub_cfi);
|
|
}
|
|
}
|