feat(reader): add settings system with per-book delta and defaults

This commit is contained in:
neon
2026-01-16 02:00:38 -06:00
parent 86ff97bc82
commit 244d38afe8
53 changed files with 9554 additions and 5677 deletions
+36 -32
View File
@@ -1,52 +1,56 @@
export interface Permission {
id: number
name: string
description?: string
isSystem: boolean
id: number;
name: string;
description?: string;
isSystem: boolean;
}
export interface Role {
id: number
name: string
description?: string
isSuperuser: boolean
isSystem: boolean
permissions: Permission[]
id: number;
name: string;
description?: string;
isSuperuser: boolean;
isSystem: boolean;
permissions: Permission[];
}
export interface UserSettings {
syncReaderPreferences?: boolean;
}
export interface AuthUser {
id: number
username: string
name: string
email?: string
active: boolean
isDefaultPassword: boolean
settings: Record<string, unknown>
avatarUrl?: string | null
provisioningMethod: 'local' | 'oidc'
roles: Role[]
permissions: string[] // flat list; '*' means superuser
id: number;
username: string;
name: string;
email?: string;
active: boolean;
isDefaultPassword: boolean;
settings: UserSettings;
avatarUrl?: string | null;
provisioningMethod: "local" | "oidc";
roles: Role[];
permissions: string[]; // flat list; '*' means superuser
}
export interface OidcPublicConfig {
enabled: boolean
providerName: string
issuerUri: string
clientId: string
scopes: string
enabled: boolean;
providerName: string;
issuerUri: string;
clientId: string;
scopes: string;
}
export interface AuthResponse {
accessToken: string
user: AuthUser
accessToken: string;
user: AuthUser;
}
export interface RefreshResponse {
accessToken: string
accessToken: string;
}
export interface Session {
id: number
createdAt: string
expiresAt: string
id: number;
createdAt: string;
expiresAt: string;
}
+3 -2
View File
@@ -1,2 +1,3 @@
export * from './auth';
export * from './library';
export * from "./auth";
export * from "./library";
export * from "./reader-settings";
+2 -2
View File
@@ -1,5 +1,5 @@
export type OrganizationMode = 'auto' | 'book_per_file' | 'book_per_folder';
export type AccessLevel = 'viewer' | 'editor' | 'owner';
export type OrganizationMode = "auto" | "book_per_file" | "book_per_folder";
export type AccessLevel = "viewer" | "editor" | "owner";
export interface LibraryFolder {
id: number;
+96
View File
@@ -0,0 +1,96 @@
export type ReaderFormatGroup = 'epub' | 'pdf' | 'cbx'
export const FORMAT_TO_GROUP: Record<string, ReaderFormatGroup> = {
epub: 'epub',
mobi: 'epub',
azw3: 'epub',
azw: 'epub',
fb2: 'epub',
txt: 'epub',
pdf: 'pdf',
cbz: 'cbx',
cbr: 'cbx',
cb7: 'cbx',
}
export function getFormatGroup(format: string): ReaderFormatGroup {
return FORMAT_TO_GROUP[format.toLowerCase()] ?? 'epub'
}
export interface EpubReaderSettings {
themeName: string // matches one of the reader's built-in theme names
isDark: boolean
fontFamily: string | null // null = use the book's embedded font
fontSize: number // 10-32
lineHeight: number // 0.8-3.0
maxColumnCount: number // 1-10
gap: number // 0-0.5 (column gap as fraction)
maxInlineSize: number // 400-1600 (max content width in px)
maxBlockSize: number // 600-2400 (max content height in px)
justify: boolean
hyphenate: boolean
flow: 'paginated' | 'scrolled'
// When false, new books open with the publisher's embedded styles instead of these defaults.
// Per-book settings always apply regardless of this flag.
overrideBookFormatting: boolean
}
export interface PdfReaderSettings {
scrollMode: 'continuous' | 'page'
spread: 'none' | 'odd' | 'even'
zoomMode: 'fit-width' | 'fit-page' | 'custom'
customScale: number // 0.25-4.0, used when zoomMode is 'custom'
}
export interface CbxReaderSettings {
fitMode: 'fit-page' | 'fit-width' | 'fit-height' | 'actual'
viewMode: 'single' | 'two-page'
scrollMode: 'paginated' | 'infinite' | 'long-strip'
direction: 'ltr' | 'rtl'
bgColor: 'black' | 'gray' | 'white'
}
export type ReaderSettingsMap = {
epub: EpubReaderSettings
pdf: PdfReaderSettings
cbx: CbxReaderSettings
}
export type ReaderSettings = EpubReaderSettings | PdfReaderSettings | CbxReaderSettings
export const EPUB_READER_DEFAULTS: EpubReaderSettings = {
themeName: 'default',
isDark: false,
fontFamily: null,
fontSize: 16,
lineHeight: 1.5,
maxColumnCount: 2,
gap: 0.05,
maxInlineSize: 720,
maxBlockSize: 1440,
justify: true,
hyphenate: true,
flow: 'paginated',
overrideBookFormatting: true,
}
export const PDF_READER_DEFAULTS: PdfReaderSettings = {
scrollMode: 'continuous',
spread: 'none',
zoomMode: 'fit-page',
customScale: 1.0,
}
export const CBX_READER_DEFAULTS: CbxReaderSettings = {
fitMode: 'fit-page',
viewMode: 'single',
scrollMode: 'paginated',
direction: 'ltr',
bgColor: 'black',
}
export const READER_GROUP_DEFAULTS: ReaderSettingsMap = {
epub: EPUB_READER_DEFAULTS,
pdf: PDF_READER_DEFAULTS,
cbx: CBX_READER_DEFAULTS,
}