feat: uv config other feats

- add uv configuration for the backend
- update frontend to make auth work
- add new auth endpoints
- add bookmars feat
- add reader feat
This commit is contained in:
2026-06-03 22:06:01 -05:00
parent 730c748f5f
commit 6b4c0c43f8
137 changed files with 20319 additions and 2340 deletions
+95
View File
@@ -0,0 +1,95 @@
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);
}
}