diff --git a/templates/reader.templ b/templates/reader.templ index 83059ad..9f84b79 100644 --- a/templates/reader.templ +++ b/templates/reader.templ @@ -43,7 +43,9 @@ templ Reader(user User, metadata ReaderMetadata, progress ReadingProgress, bookm - +
+ +
@DictionaryPopup() diff --git a/templates/reader_templ.go b/templates/reader_templ.go index 7395f15..24d0296 100644 --- a/templates/reader_templ.go +++ b/templates/reader_templ.go @@ -97,7 +97,7 @@ func Reader(user User, metadata ReaderMetadata, progress ReadingProgress, bookma if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -141,7 +141,7 @@ func ReaderChrome(user User, metadata ReaderMetadata, progress ReadingProgress) var templ_7745c5c3_Var5 string templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(metadata.Title) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 60, Col: 54} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 62, Col: 54} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) if templ_7745c5c3_Err != nil { @@ -154,7 +154,7 @@ func ReaderChrome(user User, metadata ReaderMetadata, progress ReadingProgress) var templ_7745c5c3_Var6 string templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d/%d", progress.CurrentPage, progress.TotalPages)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 137, Col: 70} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 139, Col: 70} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) if templ_7745c5c3_Err != nil { @@ -293,7 +293,7 @@ func ReaderBookmarksPanel(bookmarks []Bookmark) templ.Component { var templ_7745c5c3_Var11 string templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(bookmark.CfiPosition) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 349, Col: 38} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 351, Col: 38} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) if templ_7745c5c3_Err != nil { @@ -306,7 +306,7 @@ func ReaderBookmarksPanel(bookmarks []Bookmark) templ.Component { var templ_7745c5c3_Var12 string templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(bookmark.Title) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 352, Col: 49} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 354, Col: 49} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) if templ_7745c5c3_Err != nil { @@ -319,7 +319,7 @@ func ReaderBookmarksPanel(bookmarks []Bookmark) templ.Component { var templ_7745c5c3_Var13 string templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(bookmark.Position) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 354, Col: 27} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 356, Col: 27} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) if templ_7745c5c3_Err != nil { diff --git a/web/src/reader/reader.ts b/web/src/reader/reader.ts index ac2bb31..40761fd 100644 --- a/web/src/reader/reader.ts +++ b/web/src/reader/reader.ts @@ -2,27 +2,48 @@ import "foliate-js/view.js"; import { Alpine } from "../alpine"; import { loadSettings, saveSettings } from "./settings-manager"; import { getToken } from "../storage"; + +const FONT_MAP: Record = { + literata: '"Literata"', + crimson: '"Crimson Pro"', + "source-serif": '"Source Serif 4"', + "eb-garamond": '"EB Garamond"', + libertinus: '"Libertinus Serif"', + "noto-serif": '"Noto Serif"', + "charis-sil": '"Charis SIL"', + "ibm-plex": '"IBM Plex Serif"', +}; + const getCSS = ({ - spacing, + fontFamily, + fontSize, + lineHeight, justify, hyphenate, }: { - spacing: number; + fontFamily: string; + fontSize: number; + lineHeight: number; justify: boolean; hyphenate: boolean; }) => { - const style = getComputedStyle(document.body); + const el = document.getElementById("reader-viewport"); + const style = getComputedStyle(el ?? document.body); const bg = style.getPropertyValue("--reader-bg").trim() || "#fafafa"; const text = style.getPropertyValue("--reader-text").trim() || "#1a1a1a"; const link = style.getPropertyValue("--reader-link").trim() || "#0066cc"; const selection = style.getPropertyValue("--reader-selection").trim() || "#b3d9ff"; + const font = FONT_MAP[fontFamily] ?? '"Literata"'; return ` @namespace epub "http://www.idpf.org/2007/ops"; html { color-scheme: light dark; background-color: ${bg}; color: ${text}; + font-family: ${font}, serif; + font-size: ${fontSize}px; + line-height: ${lineHeight}; } ::selection { background-color: ${selection}; @@ -36,7 +57,7 @@ const getCSS = ({ } } p, li, blockquote, dd { - line-height: ${spacing}; + line-height: ${lineHeight}; text-align: ${justify ? "justify" : "start"}; -webkit-hyphens: ${hyphenate ? "auto" : "manual"}; hyphens: ${hyphenate ? "auto" : "manual"}; @@ -94,11 +115,8 @@ document.addEventListener("alpine:init", () => { progressText: "", sliderValue: 0, settings: null as ReaderSettings | null, - style: { - spacing: 1.4, - justify: true, - hyphenate: true, - }, + justify: true, + hyphenate: true, tocOpen: false, settingsOpen: false, bookmarksOpen: false, @@ -122,17 +140,16 @@ document.addEventListener("alpine:init", () => { this.readingFont = this.settings.reading_font || "literata"; this.fontSize = this.settings.font_size || 16; this.lineHeight = this.settings.line_height || 1.6; - if (this.settings.reading_theme) { - document.body.classList.add(`theme-${this.settings.reading_theme}`); - } if (this.settings.reading_mode) { this.readingMode = this.settings.reading_mode; } else { this.readingMode = this.detectChromeDarkMode() ? "dark" : "light"; } - if (this.readingMode === "dark") { - document.body.classList.add("dark"); - } + } + const viewport = document.getElementById("reader-viewport")!; + viewport.classList.add(`theme-${this.readingTheme}`); + if (this.readingMode === "dark") { + viewport.classList.add("dark"); } this.view = document.getElementById("reader-view") as any; const resp = await fetch(config.fileUrl, { @@ -151,7 +168,7 @@ document.addEventListener("alpine:init", () => { this.zoomPercent = this.renderer.zoomPercent; }); } else { - this.renderer.setStyles?.(getCSS(this.style)); + this.renderer.setStyles?.(this.buildCSS()); } this.view.addEventListener("load", (e: any) => { const { doc } = e.detail; @@ -285,13 +302,11 @@ document.addEventListener("alpine:init", () => { } }, applyTheme() { - document.body.className = document.body.className - .replace(/theme-(?!tokyo|dracula|nord|solarized|monokai|one-dark|material|catppuccin)[\w-]+/g, "") - .replace(/\bdark\b/g, "") - .trim(); - document.body.classList.add(`theme-${this.readingTheme}`); + const viewport = document.getElementById("reader-viewport")!; + viewport.className = "absolute inset-x-0 top-[52px] bottom-[52px]"; + viewport.classList.add(`theme-${this.readingTheme}`); if (this.readingMode === "dark") { - document.body.classList.add("dark"); + viewport.classList.add("dark"); } this.applyStyles(); saveSettings({ @@ -321,7 +336,16 @@ document.addEventListener("alpine:init", () => { }, applyStyles() { if (!this.renderer?.setStyles) return; - this.renderer.setStyles(getCSS(this.style)); + this.renderer.setStyles(this.buildCSS()); + }, + buildCSS() { + return getCSS({ + fontFamily: this.readingFont, + fontSize: this.fontSize, + lineHeight: this.lineHeight, + justify: this.justify, + hyphenate: this.hyphenate, + }); }, async addBookmark() { const token = getToken(); diff --git a/web/static/foliate-themes.css b/web/static/foliate-themes.css index d0a414c..b725255 100644 --- a/web/static/foliate-themes.css +++ b/web/static/foliate-themes.css @@ -6,6 +6,20 @@ CATEGORY 1: CLASSIC READING (6 themes) Time-tested, comfortable for long sessions ========================================== */ +#reader-viewport { + background-color: var(--reader-bg); + color: var(--reader-text); + font-family: var(--reader-font, "Literata", serif); + font-size: var(--reader-font-size, 16px); + line-height: var(--reader-line-height, 1.6); +} +#reader-viewport ::selection { + background-color: var(--reader-selection); +} +#reader-viewport a { + color: var(--reader-link); + text-decoration: underline; +} /* Light - Default theme */ .theme-light { --reader-bg: #fafafa; @@ -256,4 +270,6 @@ --reader-link: #268bd2; --reader-selection: #073642; } - +#reader-viewport { + background-color: var(--reader-bg); +}