aboutsummaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/calendar-sync.el22
-rw-r--r--modules/calibredb-epub-config.el25
-rw-r--r--modules/eat-config.el26
-rw-r--r--modules/font-config.el40
-rw-r--r--modules/keyboard-compat.el6
-rw-r--r--modules/nov-reading.el282
6 files changed, 353 insertions, 48 deletions
diff --git a/modules/calendar-sync.el b/modules/calendar-sync.el
index 1079a72be..297d1fe61 100644
--- a/modules/calendar-sync.el
+++ b/modules/calendar-sync.el
@@ -381,12 +381,28 @@ Syncs all calendars immediately, then every `calendar-sync-interval-minutes'."
;; User can manually sync or it will happen on next timer tick if auto-sync is enabled
))
-;; Start auto-sync if enabled and calendars are configured
-;; Syncs immediately then every calendar-sync-interval-minutes (default: 60 minutes)
+;; Defer auto-sync until calendar data is first needed.
+;;
+;; The :secret-host feed URLs live in authinfo.gpg, and BOTH the immediate sync
+;; and every periodic timer tick resolve them. Calling `calendar-sync-start' at
+;; load (immediate sync + recurring timer) therefore decrypts authinfo.gpg right
+;; after startup, prompting for the GPG passphrase on a cold gpg-agent (e.g.
+;; after a reboot). Defer the whole start to the first org-agenda use, so the
+;; unlock happens when the user actually asks for calendar data. A manual
+;; `calendar-sync-start' / `calendar-sync-now' still works on demand.
+(defun calendar-sync--auto-start-on-first-agenda ()
+ "Start auto-sync on the first org-agenda use, then remove this hook.
+One-shot: deferring `calendar-sync-start' until the agenda is first built keeps a
+cold gpg-agent from being prompted for the authinfo passphrase at startup.
+Removes itself before starting so a `calendar-sync-start' error can't re-fire it."
+ (remove-hook 'org-agenda-mode-hook #'calendar-sync--auto-start-on-first-agenda)
+ (calendar-sync-start))
+
+;; Arm the deferred start when auto-sync is enabled and calendars are configured.
(when (and calendar-sync-auto-start
calendar-sync-calendars
(not noninteractive))
- (calendar-sync-start))
+ (add-hook 'org-agenda-mode-hook #'calendar-sync--auto-start-on-first-agenda))
(provide 'calendar-sync)
diff --git a/modules/calibredb-epub-config.el b/modules/calibredb-epub-config.el
index a8b131be8..b03d83ed0 100644
--- a/modules/calibredb-epub-config.el
+++ b/modules/calibredb-epub-config.el
@@ -31,6 +31,8 @@
(declare-function nov-render-document "nov" ())
(defvar nov-text-width) ; from nov.el; set buffer-local here
+(require 'nov-reading) ;; reading-view theme layer: palettes + typography + size
+
;; calibredb commands the curated menu drives (all autoloaded by calibredb)
(declare-function calibredb-switch-library "calibredb" ())
(declare-function calibredb-search-keyword-filter "calibredb-search")
@@ -314,12 +316,8 @@ A positive DELTA narrows the text column; a negative DELTA widens it."
(defun cj/nov-apply-preferences ()
"Apply preferences after nov-mode has launched."
(interactive)
- ;; Use Merriweather for comfortable reading with appropriate scaling.
- ;; (Reading fg color stripped; falls back to the theme default until a
- ;; themeable reading face exists -- see todo.org.)
- (face-remap-add-relative 'variable-pitch :family "Merriweather" :height 1.0)
- (face-remap-add-relative 'default :family "Merriweather" :height 180)
- (face-remap-add-relative 'fixed-pitch :height 180)
+ ;; Reading typography + color palette live in the nov-reading theme layer.
+ (cj/nov-reading-setup)
;; Enable visual-line-mode for proper text wrapping
(visual-line-mode 1)
;; Set fill-column as a fallback
@@ -428,14 +426,19 @@ Try to use the Calibre book id from the parent folder name (for example,
("<" . nov-history-back)
(">" . nov-history-forward)
("," . backward-paragraph)
- ;; +/= widen the text column, -/_ narrow it (50%..100% of the window)
- ("+" . cj/nov-widen-text)
- ("=" . cj/nov-widen-text)
- ("-" . cj/nov-narrow-text)
- ("_" . cj/nov-narrow-text)
+ ;; +/- adjust the page font size, = resets it to the default height
+ ("+" . cj/nov-reading-text-bigger)
+ ("-" . cj/nov-reading-text-smaller)
+ ("=" . cj/nov-reading-text-reset)
+ ;; { } adjust the text-column width (50%..100% of the window)
+ ("}" . cj/nov-widen-text)
+ ("{" . cj/nov-narrow-text)
;; open current EPUB with zathura (same key in pdf-view)
("z" . cj/nov-open-external)
("t" . nov-goto-toc)
+ ;; c cycles reading palettes (sepia/dark/light/none); C picks one by name
+ ("c" . cj/nov-cycle-reading-palette)
+ ("C" . cj/nov-set-reading-palette)
("C-c C-b" . cj/nov-jump-to-calibredb)))
;; ------------------------- Nov bookmark naming -------------------------------
diff --git a/modules/eat-config.el b/modules/eat-config.el
index f53baed31..1de24dc4f 100644
--- a/modules/eat-config.el
+++ b/modules/eat-config.el
@@ -27,6 +27,7 @@
(declare-function eat "eat" (&optional program arg))
(declare-function eshell "eshell" (&optional arg))
+(declare-function cj/dashboard-only "dashboard-config")
(defvar eat-mode-map)
(defvar eat-semi-char-mode-map)
(defvar eshell-buffer-name)
@@ -110,12 +111,17 @@ ARGS is (TERMINAL OUTPUT)."
(eat-sixel-render-formats '(xpm svg half-block background none)) ; inline images (on by default)
(eat-query-before-killing-running-terminal 'auto) ; confirm before killing a terminal with a live process
:config
- ;; F12 and C-; must reach Emacs from inside EAT. In semi-char mode (EAT's
- ;; default) EAT forwards unbound keys to the terminal -- a letter runs
+ ;; F1, F12, and C-; must reach Emacs from inside EAT. In semi-char mode
+ ;; (EAT's default) EAT forwards unbound keys to the terminal -- a letter runs
;; `eat-self-input' -- so bind these explicitly or they never reach Emacs:
- ;; F12 toggles the terminal window, C-; opens the global prefix map.
+ ;; F1 runs the kill-all sweep back to the dashboard (`cj/dashboard-only',
+ ;; which buries agent buffers rather than killing them), F12 toggles the
+ ;; terminal window, C-; opens the global prefix map. Unlike ghostel, EAT
+ ;; needs no exception-list or keymap rebuild -- the bind alone suffices.
+ (keymap-set eat-semi-char-mode-map "<f1>" #'cj/dashboard-only)
(keymap-set eat-semi-char-mode-map "<f12>" #'cj/term-toggle)
(keymap-set eat-semi-char-mode-map "C-;" cj/custom-keymap)
+ (keymap-set eat-mode-map "<f1>" #'cj/dashboard-only)
(keymap-set eat-mode-map "<f12>" #'cj/term-toggle)
(keymap-set eat-mode-map "C-;" cj/custom-keymap))
@@ -302,6 +308,16 @@ Escape."
(interactive)
(cj/--term-send-string "\e"))
+(defun cj/term-backward-kill-word ()
+ "Delete the previous word in the terminal program's input line.
+Sends M-DEL (ESC DEL) to the pty, which readline and most line editors map to
+backward-kill-word -- the same word-boundary delete C-<backspace> does in normal
+Emacs buffers (it stops at punctuation). EAT's default forwards C-<backspace> as
+a bare key the program ignores, so the word never gets deleted; sending the
+escape sequence the program actually understands is what makes the key work."
+ (interactive)
+ (cj/--term-send-string "\e\d"))
+
(defun cj/term--tmux-output (&rest args)
"Run tmux with ARGS and return its stdout.
Signal `user-error' when tmux exits with a non-zero status."
@@ -499,6 +515,10 @@ pty; without tmux, moves point up in EAT's emacs-mode buffer."
;; to semi-char. One key gets out of either copy view.
(keymap-set eat-semi-char-mode-map "<escape>" #'cj/term-send-escape)
(keymap-set eat-mode-map "<escape>" #'eat-semi-char-mode)
+ ;; Ctrl+Backspace deletes the previous word, matching its behavior in normal
+ ;; buffers. Terminals send no standard code for it, so EAT's default forwards
+ ;; a bare key the program drops; send M-DEL instead (readline backward-kill-word).
+ (keymap-set eat-semi-char-mode-map "C-<backspace>" #'cj/term-backward-kill-word)
;; Word-motion arrows edit the terminal program's input (claude, readline), so
;; forward them to the pty. EAT's default leaves them in the non-bound-keys
;; list, which moved Emacs point instead and desynced it from the real cursor
diff --git a/modules/font-config.el b/modules/font-config.el
index 2be051ddc..3aa3d80f6 100644
--- a/modules/font-config.el
+++ b/modules/font-config.el
@@ -83,11 +83,6 @@
(FiraCode-Literata
:default-family "Fira Code Nerd Font"
:variable-pitch-family "Literata")
- (EBook
- :default-family "Lexend"
- :default-weight regular
- :default-height 200
- :variable-pitch-family "Lexend")
(24-point-font
:default-height 240)
(20-point-font
@@ -165,32 +160,27 @@ If FRAME is nil, uses the selected frame."
t
nil))
-;; ------------------------------- All The Icons -------------------------------
-;; icons made available through fonts
+;; ------------------------------- Nerd Icons fonts ----------------------------
+;; nerd-icons (configured in nerd-icons-config.el) renders glyphs from the
+;; "Symbols Nerd Font Mono" font. Auto-install it on the first GUI frame when
+;; it is missing -- the same convenience the dropped all-the-icons setup gave.
-(declare-function all-the-icons-install-fonts "all-the-icons")
+(declare-function nerd-icons-install-fonts "nerd-icons")
-(defun cj/maybe-install-all-the-icons-fonts (&optional _frame)
- "Install all-the-icons fonts if needed and we have a GUI."
+(defun cj/maybe-install-nerd-icons-fonts (&optional _frame)
+ "Install the nerd-icons font if it is missing and we have a GUI."
(when (and (env-gui-p)
- (not (cj/font-installed-p "all-the-icons")))
- (all-the-icons-install-fonts t)
+ (not (cj/font-installed-p "Symbols Nerd Font Mono")))
+ (nerd-icons-install-fonts t)
;; Remove this hook after successful installation
- (remove-hook 'server-after-make-frame-hook #'cj/maybe-install-all-the-icons-fonts)))
+ (remove-hook 'server-after-make-frame-hook #'cj/maybe-install-nerd-icons-fonts)))
-(use-package all-the-icons
- :demand t
- :config
- ;; Handle both daemon and non-daemon modes
+;; nerd-icons loads after this module (see init.el order), so defer the wiring
+;; until it is present. Daemon: install on the first GUI frame; otherwise now.
+(with-eval-after-load 'nerd-icons
(if (daemonp)
- (add-hook 'server-after-make-frame-hook #'cj/maybe-install-all-the-icons-fonts)
- (cj/maybe-install-all-the-icons-fonts)))
-
-(use-package all-the-icons-nerd-fonts
- :after all-the-icons
- :demand t
- :config
- (all-the-icons-nerd-fonts-prefer))
+ (add-hook 'server-after-make-frame-hook #'cj/maybe-install-nerd-icons-fonts)
+ (cj/maybe-install-nerd-icons-fonts)))
;; ----------------------------- Emoji Fonts Per OS ----------------------------
diff --git a/modules/keyboard-compat.el b/modules/keyboard-compat.el
index 172f96c7b..9395b9c86 100644
--- a/modules/keyboard-compat.el
+++ b/modules/keyboard-compat.el
@@ -68,12 +68,6 @@ This runs after init to override any package settings."
nerd-icons-icon-for-buffer))
(advice-add fn :around #'cj/--icon-blank-in-terminal)))
-(with-eval-after-load 'all-the-icons
- (dolist (fn '(all-the-icons-icon-for-file
- all-the-icons-icon-for-dir
- all-the-icons-icon-for-mode))
- (advice-add fn :around #'cj/--icon-blank-in-terminal)))
-
;; =============================================================================
;; GUI-specific fixes
;; =============================================================================
diff --git a/modules/nov-reading.el b/modules/nov-reading.el
new file mode 100644
index 000000000..4134f4975
--- /dev/null
+++ b/modules/nov-reading.el
@@ -0,0 +1,282 @@
+;;; nov-reading.el --- Reading-view theme layer for nov-mode EPUBs -*- lexical-binding: t; -*-
+;; author: Craig Jennings <c@cjennings.net>
+
+;;; Commentary:
+;;
+;; Layer: 4 (Added features).
+;; Category: O (optional commands + faces).
+;; Load shape: eager.
+;; Eager reason: defines the reading faces and commands the nov launch hook and
+;; keymap reference; the faces must exist for theme-studio's inventory too.
+;; Top-level side effects: defface x9 (3 palettes + per-palette heading/link),
+;; defcustoms, a defgroup, a defvar.
+;; Runtime requires: none (face-remap and text-scale are built in).
+;; Direct test load: yes.
+;;
+;; A small theme layer on top of the stock `nov' package (no fork): how an EPUB
+;; *reads*, kept buffer-local so it never disturbs the frame or other buffers.
+;; Two knobs:
+;;
+;; - Reading palette -- the background + foreground, as sepia / dark / light,
+;; each a face the dupre theme / theme-studio own (registered as the
+;; "nov-reading" bespoke app in theme-studio's face_data.py).
+;; - Typography -- a serif family and a base height, with +/-/= adjusting the
+;; page font size live via a buffer-local text-scale on top of the base.
+;; The live size is remembered globally, so every book opens where you left
+;; it; "=" returns to the base height.
+;;
+;; calibredb-epub-config.el owns the library/calibre side and the text-width /
+;; centering layout; this module owns reading color and typography. Its launch
+;; entry point `cj/nov-reading-setup' is called from that module's nov-mode hook.
+
+;;; Code:
+
+(defgroup cj/nov-reading nil
+ "Reading-view theming for nov-mode EPUBs."
+ :group 'cj)
+
+;; ----------------------------- Reading palettes ------------------------------
+;; nov renders through shr and defines no faces, so a palette is a buffer-local
+;; face-remap of `default'. Each palette is one face carrying a :background and
+;; :foreground, so the theme owns the real colors (the hex defaults here are a
+;; starting point to tune in theme-studio).
+
+(defface cj/nov-reading-sepia
+ '((t :background "#1f1b16" :foreground "#c9b187"))
+ "Sepia reading palette for nov-mode: warm dark background, tan text."
+ :group 'cj/nov-reading)
+
+(defface cj/nov-reading-dark
+ '((t :background "#15140f" :foreground "#cfc8b8"))
+ "Dark reading palette for nov-mode: near-black background, light-gray text."
+ :group 'cj/nov-reading)
+
+(defface cj/nov-reading-light
+ '((t :background "#ece3cf" :foreground "#2a2622"))
+ "Light reading palette for nov-mode: cream background, near-black text."
+ :group 'cj/nov-reading)
+
+;; Structural faces: recolor shr's heading (h1-h6) and link faces per palette,
+;; remapped buffer-local so the EPUB's hierarchy reads in the palette's accent
+;; while mail/eww (the other shr consumers) keep the theme's shr colors. Heading
+;; faces carry :foreground only -- shr's per-level height and weight survive the
+;; relative remap; link faces add :underline so the cue reads as a link.
+
+(defface cj/nov-reading-sepia-heading
+ '((t :foreground "#e6c98a"))
+ "Heading accent for the sepia reading palette (recolors shr-h1..h6)."
+ :group 'cj/nov-reading)
+
+(defface cj/nov-reading-sepia-link
+ '((t :foreground "#c98f5a" :underline t))
+ "Link accent for the sepia reading palette (recolors shr-link)."
+ :group 'cj/nov-reading)
+
+(defface cj/nov-reading-dark-heading
+ '((t :foreground "#e8e0cc"))
+ "Heading accent for the dark reading palette (recolors shr-h1..h6)."
+ :group 'cj/nov-reading)
+
+(defface cj/nov-reading-dark-link
+ '((t :foreground "#8fb0c4" :underline t))
+ "Link accent for the dark reading palette (recolors shr-link)."
+ :group 'cj/nov-reading)
+
+(defface cj/nov-reading-light-heading
+ '((t :foreground "#5a3d28"))
+ "Heading accent for the light reading palette (recolors shr-h1..h6)."
+ :group 'cj/nov-reading)
+
+(defface cj/nov-reading-light-link
+ '((t :foreground "#8a5a2a" :underline t))
+ "Link accent for the light reading palette (recolors shr-link)."
+ :group 'cj/nov-reading)
+
+(defcustom cj/nov-reading-palettes
+ '(("sepia" :face cj/nov-reading-sepia
+ :heading cj/nov-reading-sepia-heading
+ :link cj/nov-reading-sepia-link)
+ ("dark" :face cj/nov-reading-dark
+ :heading cj/nov-reading-dark-heading
+ :link cj/nov-reading-dark-link)
+ ("light" :face cj/nov-reading-light
+ :heading cj/nov-reading-light-heading
+ :link cj/nov-reading-light-link))
+ "Alist of reading-palette NAME -> face property list for nov-mode.
+Each entry's plist supplies the palette's colors, all theme-owned faces:
+ :face reading-view :background and :foreground, remapped onto `default'
+ :heading recolors shr's heading faces (h1-h6) for this palette
+ :link recolors shr's link face for this palette
+The selector and cycle commands choose among these names. Add an entry to add a
+palette; omit :heading or :link to leave that element at the theme's default."
+ :type '(alist :key-type string
+ :value-type
+ (plist :options ((:face face) (:heading face) (:link face))))
+ :group 'cj/nov-reading)
+
+(defcustom cj/nov-reading-default-palette "sepia"
+ "Reading palette applied to a fresh nov-mode buffer.
+A key in `cj/nov-reading-palettes', or nil for the theme's normal rendering."
+ :type '(choice (const :tag "None (theme default)" nil) string)
+ :group 'cj/nov-reading)
+
+(defvar-local cj/nov--reading-remap-cookies nil
+ "List of `face-remap-add-relative' cookies for the active reading palette.
+Covers the `default' remap and any shr heading/link remaps, so switching
+palettes can remove them all at once.")
+
+(defvar-local cj/nov--reading-palette nil
+ "Name of the reading palette active in this buffer, or nil for none.")
+
+(defun cj/nov--reading-palette-plist (name)
+ "Return the face property list for palette NAME, or nil when unknown.
+NAME nil (the no-palette state) and unknown names both yield nil."
+ (cdr (assoc name cj/nov-reading-palettes)))
+
+(defun cj/nov--reading-palette-face (name)
+ "Return the base (bg/fg) face for palette NAME, or nil when NAME is unknown."
+ (plist-get (cj/nov--reading-palette-plist name) :face))
+
+(defun cj/nov--next-reading-palette (current names)
+ "Return the palette after CURRENT in the cycle NAMES then nil, wrapping.
+CURRENT nil is the no-palette state, and a returned nil means no palette. An
+unknown CURRENT falls back to the first palette."
+ (let* ((cycle (append names (list nil)))
+ (tail (cdr (member current cycle))))
+ (car (or tail cycle))))
+
+(defun cj/nov--apply-reading-palette (name)
+ "Apply reading palette NAME buffer-local; NAME nil removes any palette.
+Remaps `default' to the palette's :face, and (when present) shr's heading faces
+h1-h6 to its :heading face and shr-link to its :link face. Removes the previous
+palette's remaps first so switching never stacks, and leaves the typography
+remap (a separate `default' remap) untouched."
+ (mapc #'face-remap-remove-relative cj/nov--reading-remap-cookies)
+ (setq cj/nov--reading-remap-cookies nil)
+ (let* ((plist (cj/nov--reading-palette-plist name))
+ (face (plist-get plist :face)))
+ (when face
+ (push (face-remap-add-relative 'default face)
+ cj/nov--reading-remap-cookies)
+ (let ((heading (plist-get plist :heading)))
+ (when heading
+ (dolist (h '(shr-h1 shr-h2 shr-h3 shr-h4 shr-h5 shr-h6))
+ (push (face-remap-add-relative h heading)
+ cj/nov--reading-remap-cookies))))
+ (let ((link (plist-get plist :link)))
+ (when link
+ (push (face-remap-add-relative 'shr-link link)
+ cj/nov--reading-remap-cookies))))
+ (setq cj/nov--reading-palette (and face name))))
+
+(defun cj/nov-set-reading-palette (name)
+ "Choose reading palette NAME for this nov buffer; \"none\" clears it.
+Interactively prompts among `cj/nov-reading-palettes' plus \"none\"."
+ (interactive
+ (list (completing-read "Reading palette: "
+ (cons "none" (mapcar #'car cj/nov-reading-palettes))
+ nil t)))
+ (unless (derived-mode-p 'nov-mode)
+ (user-error "Not in a nov-mode buffer"))
+ (cj/nov--apply-reading-palette (unless (equal name "none") name))
+ (message "Reading palette: %s" (or cj/nov--reading-palette "none")))
+
+(defun cj/nov-cycle-reading-palette ()
+ "Cycle to the next reading palette, then the no-palette state, wrapping."
+ (interactive)
+ (unless (derived-mode-p 'nov-mode)
+ (user-error "Not in a nov-mode buffer"))
+ (let ((next (cj/nov--next-reading-palette
+ cj/nov--reading-palette
+ (mapcar #'car cj/nov-reading-palettes))))
+ (cj/nov--apply-reading-palette next)
+ (message "Reading palette: %s" (or next "none"))))
+
+;; ------------------------------- Typography ----------------------------------
+
+(defcustom cj/nov-reading-font-family "Merriweather"
+ "Variable-pitch serif family for the EPUB reading view."
+ :type 'string
+ :group 'cj/nov-reading)
+
+(defcustom cj/nov-reading-text-height 180
+ "Base `default'-face height (1/10 pt) the reading view renders at.
+The +/-/= keys adjust the page size from here with a buffer-local text-scale.
+That adjustment is remembered globally (see `cj/nov-reading-text-scale-file'):
+every book and every session opens at the size you last left it, and `='
+returns to this base."
+ :type 'integer
+ :group 'cj/nov-reading)
+
+(defvar cj/nov-reading-text-scale-file
+ (expand-file-name "data/nov-reading-text-scale" user-emacs-directory)
+ "File persisting the global reading text-scale offset across sessions.
+A single integer: the buffer-local `text-scale-mode-amount' the +/-/= keys
+last set, applied on top of `cj/nov-reading-text-height' when a book opens.")
+
+(defun cj/nov-reading--parse-text-scale (s)
+ "Parse S (a string or nil) as an integer text-scale offset; 0 when invalid.
+Surrounding whitespace is tolerated; non-integer content yields 0."
+ (let ((trimmed (and (stringp s) (string-trim s))))
+ (if (and trimmed (string-match-p "\\`[+-]?[0-9]+\\'" trimmed))
+ (string-to-number trimmed)
+ 0)))
+
+(defun cj/nov-reading--load-text-scale ()
+ "Return the persisted reading text-scale offset, or 0 when none is saved."
+ (if (file-readable-p cj/nov-reading-text-scale-file)
+ (cj/nov-reading--parse-text-scale
+ (with-temp-buffer
+ (insert-file-contents cj/nov-reading-text-scale-file)
+ (buffer-string)))
+ 0))
+
+(defun cj/nov-reading--save-text-scale (amount)
+ "Persist AMOUNT as the global reading text-scale offset.
+Creates the data directory when absent."
+ (make-directory (file-name-directory cj/nov-reading-text-scale-file) t)
+ (with-temp-file cj/nov-reading-text-scale-file
+ (insert (number-to-string amount))))
+
+(defun cj/nov-reading-apply-typography ()
+ "Apply the reading family and base height buffer-local.
+Remaps `variable-pitch', `default', and `fixed-pitch' so nov's shr output reads
+as a comfortably-sized serif page."
+ (face-remap-add-relative 'variable-pitch
+ :family cj/nov-reading-font-family :height 1.0)
+ (face-remap-add-relative 'default
+ :family cj/nov-reading-font-family
+ :height cj/nov-reading-text-height)
+ (face-remap-add-relative 'fixed-pitch :height cj/nov-reading-text-height))
+
+(defun cj/nov-reading-text-bigger ()
+ "Increase the page font size and remember it across books and sessions."
+ (interactive)
+ (text-scale-increase 1)
+ (cj/nov-reading--save-text-scale text-scale-mode-amount))
+
+(defun cj/nov-reading-text-smaller ()
+ "Decrease the page font size and remember it across books and sessions."
+ (interactive)
+ (text-scale-decrease 1)
+ (cj/nov-reading--save-text-scale text-scale-mode-amount))
+
+(defun cj/nov-reading-text-reset ()
+ "Reset the page font size to the base reading height; clears the saved offset."
+ (interactive)
+ (text-scale-set 0)
+ (cj/nov-reading--save-text-scale 0))
+
+;; ------------------------------- Launch hook ---------------------------------
+
+(defun cj/nov-reading-setup ()
+ "Apply the reading view (typography + default palette) to this nov buffer.
+Restores the remembered page font size on top of the base height.
+Called from the nov-mode launch hook in calibredb-epub-config.el."
+ (cj/nov-reading-apply-typography)
+ (text-scale-set (cj/nov-reading--load-text-scale))
+ (when cj/nov-reading-default-palette
+ (cj/nov--apply-reading-palette cj/nov-reading-default-palette)))
+
+(provide 'nov-reading)
+;;; nov-reading.el ends here