From d8fd37004c974dff789bf01fe1c64f29ad12e241 Mon Sep 17 00:00:00 2001 From: pi-bot-01 Date: Thu, 26 Mar 2026 01:01:07 -0700 Subject: [PATCH] Initial commit --- nob.el | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 nob.el diff --git a/nob.el b/nob.el new file mode 100644 index 0000000..2c031e9 --- /dev/null +++ b/nob.el @@ -0,0 +1,178 @@ +;;; nob.el --- custom bookmarks for dealing with novels in eww. -*- lexical-binding: t; -*- + +;; Copyright (C) 2019 Robert Rose + +;; Author: Robert Rose +;; Keywords: lisp eww novel bookmark +;; Version: 0.0.1 +;; Package-Requires ((dash "2.15.0) (emacs "24") (ivy "20190214.1032") + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; This package provides functions for saving and selecting novels for +;; for reading in eww. I wrote nob because bookmarking a chapter in eww +;; and then always having to remove old bookmarks is ridiculous. nov-save +;; works by queryinh the bookmark list a pattern that matches the current +;; url. If it finds a match, it updates the title and url and moves it to +;; the front of the list. This makes shoosing what to to read next, and +;; picking up where you left off much easier. The nob-select function +;; provides an easy to navigate pop up list. + +;;; Code: + +(require 'eww) +(require 'eww-http-code-advice) +(require 'dash) +(require 'ivy) + +(defgroup nob nil + "Settings for nob." + :group 'novels) + +(defcustom nob-bookmarks-file (concat user-emacs-directory "nob-bookmarks.el") + "Location of the file nob uses for bookmark storage." + :group 'nob) + +(defcustom nob-autosave-on-render t + "Adds `nob-eww-after-render-hook' to `eww-after-render-hook' (locally) when `nob-mode' is enabled." + :type 'boolean + :group 'nob) + +(defmacro with-nob (&rest body) + "Execute body with variable: `novels' set." + `(let ((novels + (with-temp-buffer + (insert-file-contents nob-bookmarks-file) + (read (buffer-string))))) + (unwind-protect ,@body))) + +(defmacro with-eww-buffer (&rest body) + "Execute body with variables: `title' and `url' set." + `(let ((title (plist-get eww-data :title)) + (url (plist-get eww-data :url))) + (unwind-protect ,@body))) + +;;; I will need to explore options for replacing `--map-first' +;;; Maybe it's as simple as popping and pushing. That would be nice. +(defun nob-update-bookmark (novels title url) + "Update an existing bookmark. Returns updated novels list.' +This function has no side affects." + (--map-first (string-match (car it) url) `(,(car it) ,title ,url) + (--sort (string-match (car it) url) novels))) + +(defun nob-write-file (novels) + (let ((inhibit-message 1)) + (with-temp-buffer + (insert (pp novels)) + (write-file nob-bookmarks-file)))) + +(defun nob-convert-bookmarks () + "Used to convert bookmarks file to the new format. +(cons pattern (cons title url)) +As I've started hacking away at nob mode, I've come +to realize that I can't simply rewrite the functions. +I'll need some scafolding in place so that nob remains +functional while I continue working on it." + (interactive)) +(with-temp-file (concat user-emacs-directory "nob-bookmarks") + (with-nob + (let* + ((n (mapcar + (lambda (x) + (cons (car x) (cons (cadr x) (caddr x)))) + novels))) + (insert (pp n)) ))) + +(defun nob-save () + "Saves current novel." + (interactive) + (with-nob + (with-eww-buffer + (let ((n (--first (string-match (car it) url) novels))) + (if n + (if (not (string-equal (nth 2 n) url)) + (progn + (nob-write-file (nob-update-bookmark novels title url)) + (message "Bookmarked: %s" title)) + (message "Already Bookmarked")) + (if (y-or-n-p "Add this novel?") + (progn + (nob-write-file + (push `(,(read-string "Match: " url) ,title ,url) novels)) + (message "Bookmarked: %s" title))) )) )) ) + +(defun nob-select () + "Select novel for reading in `eww'." + (interactive) + ;; + ;; version two + ;; `assoc' is a wonderful thing. + ;; originally required `--map' and `--first' from `dash.el' + ;; okay, still using `--map' + (with-nob ; novels: ((pattern title url)) + (let* + ((n (--map (cons (cadr it) (caddr it)) novels)) + ;; the mapcar version is bigger. + ;; (n (mapcar + ;; (lambda (x) + ;; (cons (cadr x) (caddr x))) + ;; novels)) + (s (cdr (assoc (ivy-read "Select Novel: " n) n))) ) + (switch-to-buffer "*nob*") + (unless (eq major-mode 'eww-mode) + (eww-mode)) + (stride-mode 1) + (nob-mode 1) + (article-mode 1) + (eww-browse-url s)))) + +(defun nob-remove () + "Remove an novel." + (interactive) + (with-nob + (let ((s (ivy-read "Remove Novel: " (--map (cdr it) novels)))) + (when (y-or-n-p (format "Remove \"%s\"?" s)) + (nob-write-file + (--remove (string-equal s (car (cdr it))) novels)) + (message "Removed: %s" s)) )) ) + +(defun nob-eww-after-render-hook () + "Eww render hook for nob autosave" + (let ((status (third (plist-get eww-data :error)))) + (if (not (member status '(400 404))) + (with-nob + (with-eww-buffer + (let ((n (--first (string-match (car it) url) novels))) + (if n + (if (not (string-equal (nth 2 n) url)) + (nob-write-file (nob-update-bookmark novels title url))))) )))) ) + +(setq nob-mode-map (make-sparse-keymap)) +(define-key nob-mode-map [?b] 'nob-save) +(define-key nob-mode-map [?B] 'nob-select) + +(define-minor-mode nob-mode + "Novel bookmarks minor mode." + :lighter " Nob" + :keymap nob-mode-map + (when nob-autosave-on-render + (if nob-mode + (add-hook 'eww-after-render-hook 'nob-eww-after-render-hook nil t) + (remove-hook 'eww-after-render-hook 'nob-eww-after-render-hook t) ))) + +(provide 'nob) + +;;; nob.el ends here