skip to content

GnoponEmacs

Components

Package Management

  (defvar straight-install-script-path
  "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el")

(setq straight-repository-branch "develop")

(defvar bootstrap-version)
(let ((bootstrap-file
         (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
        (bootstrap-version 5))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
    (url-retrieve-synchronously straight-install-script-path
     'silent 'inhibit-cookies)
        (goto-char (point-max))
        (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))
  (straight-use-package 'use-package)

User Experience

User Configuration

  (setq user-mail-address "emsenn@emsenn.net")
(setq ring-bell-function 'ignore)

(setq make-backup-files nil)
(setq inhibit-startup-message t)
(setq inhibit-startup-screen t)
(setq initial-scratch-message
        ";; LANDBACK")
(menu-bar-mode -1)
(tool-bar-mode -1)
(show-paren-mode 1)
(setq line-number-mode t)
(setq column-number-mode t)
(setq-default truncate-lines t)
(setq-default line-spacing 1)
(setq sentence-end-double-space nil)
(global-visual-line-mode t)
(setq echo-keystrokes 0.1)
(setq fill-column 68)
(setq mouse-wheel-scroll-amount '(1 ((shift) .1)))

Mixed Pitch Mode

(use-package mixed-pitch
  :straight t
  :hook
  (text-mode . mixed-pitch-mode))

Modus Theme

(use-package modus-themes
  :straight t
  :config
  (load-theme 'modus-vivendi t))

Olivetti

(use-package olivetti
:straight t
:hook
(text-mode . olivetti-mode))
(require 'ucs-normalize)

Recordkeeping

Beancount

(straight-use-package
 '(beancount :type git :host github
             :repo "beancount/beancount-mode"))

YASnippet

(use-package
  yasnippet :straight t
  :hook ((text-mode
          prog-mode
          conf-mode
          snippet-mode)
         . yas-minor-mode-on)
  :custom (yas-snippet-dirs '("~/ems/org/met/yas-snippets")))

Org-mode

Org-mode Configuration
(use-package
  org :straight t
  :custom
  (org-directory "~/ems/org")
  (org-startup-folded 'fold)
  (org-hide-block-startup 'hideblocks)
  (org-catch-invisible-edits 'show-and-error)
  (org-agenda-custom-commands
        '(("A" "Agenda and TODO"
           ((agenda "" )
            (alltodo "" )))))
  (org-agenda-todo-ignore-scheduled 'all)
  (org-agenda-files (list "~/ems/org"))
  (org-export-with-section-numbers nil)
  (org-export-with-smart-quotes nil)
  (org-export-with-toc nil)
  (org-html-htmlize-output-type 'nil)
  (org-html-validation-link nil)
  (org-log-done 'time)
  (org-log-into-drawer 'LOGBOOK)
  (org-image-actual-width '(400))
  (org-cite-global-bibliography '("~/ems/bibliography.bib"))
  (org-export-global-macros
   '(
     ("nl" . "(eval \"\\n\")")
     ))
  :bind (
         ("C-c l" . org-store-link)
         ("C-c a" . org-agenda)
         ("C-c c" . org-capture)
         ("C-c t" . org-toggle-link-display)
         ("C-c C-q" . counsel-org-tag)
         )
  :defines gpe/emsenn-net-head
  :config

        ;; from https://www.john2x.com/blog/blogging-with-orgmode.html
        (defun gpe/org-publish-format-sitemap (title sitemap)
          (let ((posts (cdr sitemap)))
            (concat
             (format "#+title: %s\n" title)
             "#+options: timestamp:nil date:nil\n"
             (format "#+date: %s\n" (format-time-string "[%Y-%m-%d %a %H:%M:%S]" (current-time)))
             (org-list-to-org (cons (car sitemap) posts)))))
        (setq gpe/emsenn-net-head
              (with-temp-buffer
                (insert "<style>\n")
                (insert-file-contents "~/ems/org/src/emsenn-net/main.css")
                (goto-char (point-max)) ;; move to end before appending
                (insert "\n</style>")
                (buffer-string)))
        (setq gpe/emsenn-net-preamble
              (with-temp-buffer
                (insert-file-contents "~/ems/org/src/emsenn-net/preamble.html")
                (buffer-string)))
  (org-babel-do-load-languages 'org-babel-load-languages
                               '(
                                 (shell . t)
                                 )))

Org-roam

  (use-package
  org-roam :straight t
  :after org
  :config
  (defvar gpe/org-roam-public-node-files '())
  (defun gpe/insert-backlinks (backend)
  "Insert backlinks at the end of the buffer before Org export.
BACKEND is the export backend symbol, e.g., 'html or 'latex."
  (when (org-export-derived-backend-p backend 'html)
    (let ((node (org-roam-node-at-point)))
      (when node
        (save-excursion
          (goto-char (point-max))
          (insert "\n* Backlinks\n")
          (dolist (ref (org-roam-backlinks-get node))
            (let* ((source (org-roam-backlink-source-node ref))
                   (props (org-roam-node-properties source))
                   (is-public (string= (cdr (assoc "PUBLIC" props)) "true")))
              (when is-public
                (insert (format "- [[id:%s][%s]]\n"
                                (org-roam-node-id source)
                                (org-roam-node-title source)))))))))))
  (defun gpe/org-roam-public-node-files ()
    "Return a list of Org-roam files where PUBLIC property is true"
    (mapcar #'car
            (org-roam-db-query
             [:select [file]
                      :from nodes
                      :where (like properties '"%public%true%")])))
  (defun gpe/org-roam-open-random-node ()
    "Open a random Org-roam node from the database."
    (interactive)
    (let* ((nodes (org-roam-node-list))
           (count (length nodes)))
      (if (zerop count)
          (message "No Org-roam nodes found.")
        (let* ((random-index (random count))
               (node (nth random-index nodes)))
          (org-roam-node-visit node)))))
  (defun gpe/apply-capture-properties ()
    (let ((properties '(:author :form)))
      (dolist (property properties)
        (let ((value (plist-get org-capture-current-plist property)))
          (when value
            (org-set-property (substring (symbol-name property) 1) value))))))
    (cl-defmethod org-roam-node-slug ((node org-roam-node))
    "Return the slug of NODE."
    (let ((title (org-roam-node-title node))
        (slug-trim-chars '(;; Combining Diacritical Marks https://www.unicode.org/charts/PDF/U0300.pdf
                             768 ; U+0300 COMBINING GRAVE ACCENT
                             769 ; U+0301 COMBINING ACUTE ACCENT
                             770 ; U+0302 COMBINING CIRCUMFLEX ACCENT
                             771 ; U+0303 COMBINING TILDE
                             772 ; U+0304 COMBINING MACRON
                             774 ; U+0306 COMBINING BREVE
                             775 ; U+0307 COMBINING DOT ABOVE
                             776 ; U+0308 COMBINING DIAERESIS
                             777 ; U+0309 COMBINING HOOK ABOVE
                             778 ; U+030A COMBINING RING ABOVE
                             780 ; U+030C COMBINING CARON
                             795 ; U+031B COMBINING HORN
                             803 ; U+0323 COMBINING DOT BELOW
                             804 ; U+0324 COMBINING DIAERESIS BELOW
                             805 ; U+0325 COMBINING RING BELOW
                             807 ; U+0327 COMBINING CEDILLA
                             813 ; U+032D COMBINING CIRCUMFLEX ACCENT BELOW
                             814 ; U+032E COMBINING BREVE BELOW
                             816 ; U+0330 COMBINING TILDE BELOW
                             817 ; U+0331 COMBINING MACRON BELOW
                             )))
      (cl-flet* ((nonspacing-mark-p (char)
                                    (memq char slug-trim-chars))
                 (strip-nonspacing-marks (s)
                                         (ucs-normalize-NFC-string
                                        (apply #'string (seq-remove #'nonspacing-mark-p
                                                                      (ucs-normalize-NFD-string s)))))
                 (cl-replace (title pair)
                             (replace-regexp-in-string (car pair) (cdr pair) title)))
        (let* ((pairs `(("[^[:alnum:][:digit:]]" . "-") ;; convert anything not alphanumeric
                        ("--*" . "-")                   ;; remove sequential underscores
                        ("^-" . "")                     ;; remove starting underscore
                        ("-$" . "")))                   ;; remove ending underscore
               (slug (-reduce-from #'cl-replace (strip-nonspacing-marks title) pairs)))
        (downcase slug)))))
  :hook
  ((org-export-before-processing . gpe/insert-backlinks)
  (org-capture-mode . gpe/apply-capture-properties))
  :custom
  (org-roam-directory (file-truename "~/ems/org"))
  (org-roam-dailies-directory ".")
  (org-roam-db-extra-links-exclude-keys '((node-property "ROAM_REFS")))
  (org-roam-capture-templates
   (let ((files (directory-files "~/ems/org/src/gpe/org-roam-capture-templates/" t "\\.el$")))
    (mapcar (lambda (file)
              (with-temp-buffer
                (insert-file-contents file)
                (read (buffer-string))))
            files)))
  (org-publish-project-alist
   `(
     ("emsenn-net" :components ("emsenn-net-all"))
     ("emsenn-net-all"
      :base-directory "~/ems/org"
      :exclude ".*\\.org$"
      :include ,(gpe/org-roam-public-node-files)
      :publishing-function org-html-publish-to-html
      :preparation-function gpe/org-publish-prepare-backlinks
      :html-doctype "html5"
      :html-html5-fancy t
      :html-container "section"
      :headline-levels 6
      :recursive t
      :html-divs ((preamble "section" "preamble")
              (content "main" "content")
              (postamble "section" "postamble"))
      :html-head-include-scripts nil
      :html-head-include-default-style nil
      :html-head ,gpe/emsenn-net-head
      :html-format-drawer-function gpe/org-html-format-drawer
      :exclude "\\`per/\\|README.org\\|DRAFT\\|PRIVATE"
      :with-footnotes t
      :with-date nil
      :with-timestamps nil
      :with-author nil
      :publishing-directory "~/ems/pub/emsenn-net"
      :makeindex t
      :html-preamble ,gpe/emsenn-net-preamble
      )
     ))
  :bind (
         ("C-c n i" . org-roam-node-insert)
         ("C-c n f" . org-roam-node-find)
         ("C-c n c" . org-roam-node-capture)
         ("C-c n d" . org-roam-dailies-capture-today)
         ("C-c n D" . org-roam-dailies-capture-yesterday)
         )

    )

Org-transclusion

(use-package
  org-transclusion :straight t
  :custom
  (org-transclusion-exclude-elements '(keyword property-drawer))
  :bind (
         ("C-c n t" . org-transclusion-mode)
         ("C-c n T" . org-transclusion-add)
         ))

Publishing

HTMLize

(use-package htmlize :straight t)

Interfaces

Magit

(use-package magit :straight t)

Org-Roam UI

(use-package org-roam-ui
  :straight t)

Learning

Org-drill

;;; Org-drill
(use-package
  org-drill :straight t
  :custom
  (org-drill-scope 'agenda))

Supplements

Full Source

  (defvar straight-install-script-path
  "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el")

(setq straight-repository-branch "develop")

(defvar bootstrap-version)
(let ((bootstrap-file
         (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
        (bootstrap-version 5))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
    (url-retrieve-synchronously straight-install-script-path
     'silent 'inhibit-cookies)
        (goto-char (point-max))
        (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))
  (straight-use-package 'use-package)

  (setq user-mail-address "emsenn@emsenn.net")
(setq ring-bell-function 'ignore)

(setq make-backup-files nil)
(setq inhibit-startup-message t)
(setq inhibit-startup-screen t)
(setq initial-scratch-message
        ";; LANDBACK")
(menu-bar-mode -1)
(tool-bar-mode -1)
(show-paren-mode 1)
(setq line-number-mode t)
(setq column-number-mode t)
(setq-default truncate-lines t)
(setq-default line-spacing 1)
(setq sentence-end-double-space nil)
(global-visual-line-mode t)
(setq echo-keystrokes 0.1)
(setq fill-column 68)
(setq mouse-wheel-scroll-amount '(1 ((shift) .1)))

(use-package mixed-pitch
  :straight t
  :hook
  (text-mode . mixed-pitch-mode))
(use-package modus-themes
  :straight t
  :config
  (load-theme 'modus-vivendi t))
(use-package olivetti
:straight t
:hook
(text-mode . olivetti-mode))
(require 'ucs-normalize)
(straight-use-package
 '(beancount :type git :host github
             :repo "beancount/beancount-mode"))
(use-package
  yasnippet :straight t
  :hook ((text-mode
          prog-mode
          conf-mode
          snippet-mode)
         . yas-minor-mode-on)
  :custom (yas-snippet-dirs '("~/ems/org/met/yas-snippets")))
(use-package
  org :straight t
  :custom
  (org-directory "~/ems/org")
  (org-startup-folded 'fold)
  (org-hide-block-startup 'hideblocks)
  (org-catch-invisible-edits 'show-and-error)
  (org-agenda-custom-commands
        '(("A" "Agenda and TODO"
           ((agenda "" )
            (alltodo "" )))))
  (org-agenda-todo-ignore-scheduled 'all)
  (org-agenda-files (list "~/ems/org"))
  (org-export-with-section-numbers nil)
  (org-export-with-smart-quotes nil)
  (org-export-with-toc nil)
  (org-html-htmlize-output-type 'nil)
  (org-html-validation-link nil)
  (org-log-done 'time)
  (org-log-into-drawer 'LOGBOOK)
  (org-image-actual-width '(400))
  (org-cite-global-bibliography '("~/ems/bibliography.bib"))
  (org-export-global-macros
   '(
     ("nl" . "(eval \"\\n\")")
     ))
  :bind (
         ("C-c l" . org-store-link)
         ("C-c a" . org-agenda)
         ("C-c c" . org-capture)
         ("C-c t" . org-toggle-link-display)
         ("C-c C-q" . counsel-org-tag)
         )
  :defines gpe/emsenn-net-head
  :config

        ;; from https://www.john2x.com/blog/blogging-with-orgmode.html
        (defun gpe/org-publish-format-sitemap (title sitemap)
          (let ((posts (cdr sitemap)))
            (concat
             (format "#+title: %s\n" title)
             "#+options: timestamp:nil date:nil\n"
             (format "#+date: %s\n" (format-time-string "[%Y-%m-%d %a %H:%M:%S]" (current-time)))
             (org-list-to-org (cons (car sitemap) posts)))))
        (setq gpe/emsenn-net-head
              (with-temp-buffer
                (insert "<style>\n")
                (insert-file-contents "~/ems/org/src/emsenn-net/main.css")
                (goto-char (point-max)) ;; move to end before appending
                (insert "\n</style>")
                (buffer-string)))
        (setq gpe/emsenn-net-preamble
              (with-temp-buffer
                (insert-file-contents "~/ems/org/src/emsenn-net/preamble.html")
                (buffer-string)))
  (org-babel-do-load-languages 'org-babel-load-languages
                               '(
                                 (shell . t)
                                 )))
  (use-package
  org-roam :straight t
  :after org
  :config
  (defvar gpe/org-roam-public-node-files '())
  (defun gpe/insert-backlinks (backend)
  "Insert backlinks at the end of the buffer before Org export.
BACKEND is the export backend symbol, e.g., 'html or 'latex."
  (when (org-export-derived-backend-p backend 'html)
    (let ((node (org-roam-node-at-point)))
      (when node
        (save-excursion
          (goto-char (point-max))
          (insert "\n* Backlinks\n")
          (dolist (ref (org-roam-backlinks-get node))
            (let* ((source (org-roam-backlink-source-node ref))
                   (props (org-roam-node-properties source))
                   (is-public (string= (cdr (assoc "PUBLIC" props)) "true")))
              (when is-public
                (insert (format "- [[id:%s][%s]]\n"
                                (org-roam-node-id source)
                                (org-roam-node-title source)))))))))))
  (defun gpe/org-roam-public-node-files ()
    "Return a list of Org-roam files where PUBLIC property is true"
    (mapcar #'car
            (org-roam-db-query
             [:select [file]
                      :from nodes
                      :where (like properties '"%public%true%")])))
  (defun gpe/org-roam-open-random-node ()
    "Open a random Org-roam node from the database."
    (interactive)
    (let* ((nodes (org-roam-node-list))
           (count (length nodes)))
      (if (zerop count)
          (message "No Org-roam nodes found.")
        (let* ((random-index (random count))
               (node (nth random-index nodes)))
          (org-roam-node-visit node)))))
  (defun gpe/apply-capture-properties ()
    (let ((properties '(:author :form)))
      (dolist (property properties)
        (let ((value (plist-get org-capture-current-plist property)))
          (when value
            (org-set-property (substring (symbol-name property) 1) value))))))
    (cl-defmethod org-roam-node-slug ((node org-roam-node))
    "Return the slug of NODE."
    (let ((title (org-roam-node-title node))
        (slug-trim-chars '(;; Combining Diacritical Marks https://www.unicode.org/charts/PDF/U0300.pdf
                             768 ; U+0300 COMBINING GRAVE ACCENT
                             769 ; U+0301 COMBINING ACUTE ACCENT
                             770 ; U+0302 COMBINING CIRCUMFLEX ACCENT
                             771 ; U+0303 COMBINING TILDE
                             772 ; U+0304 COMBINING MACRON
                             774 ; U+0306 COMBINING BREVE
                             775 ; U+0307 COMBINING DOT ABOVE
                             776 ; U+0308 COMBINING DIAERESIS
                             777 ; U+0309 COMBINING HOOK ABOVE
                             778 ; U+030A COMBINING RING ABOVE
                             780 ; U+030C COMBINING CARON
                             795 ; U+031B COMBINING HORN
                             803 ; U+0323 COMBINING DOT BELOW
                             804 ; U+0324 COMBINING DIAERESIS BELOW
                             805 ; U+0325 COMBINING RING BELOW
                             807 ; U+0327 COMBINING CEDILLA
                             813 ; U+032D COMBINING CIRCUMFLEX ACCENT BELOW
                             814 ; U+032E COMBINING BREVE BELOW
                             816 ; U+0330 COMBINING TILDE BELOW
                             817 ; U+0331 COMBINING MACRON BELOW
                             )))
      (cl-flet* ((nonspacing-mark-p (char)
                                    (memq char slug-trim-chars))
                 (strip-nonspacing-marks (s)
                                         (ucs-normalize-NFC-string
                                        (apply #'string (seq-remove #'nonspacing-mark-p
                                                                      (ucs-normalize-NFD-string s)))))
                 (cl-replace (title pair)
                             (replace-regexp-in-string (car pair) (cdr pair) title)))
        (let* ((pairs `(("[^[:alnum:][:digit:]]" . "-") ;; convert anything not alphanumeric
                        ("--*" . "-")                   ;; remove sequential underscores
                        ("^-" . "")                     ;; remove starting underscore
                        ("-$" . "")))                   ;; remove ending underscore
               (slug (-reduce-from #'cl-replace (strip-nonspacing-marks title) pairs)))
        (downcase slug)))))
  :hook
  ((org-export-before-processing . gpe/insert-backlinks)
  (org-capture-mode . gpe/apply-capture-properties))
  :custom
  (org-roam-directory (file-truename "~/ems/org"))
  (org-roam-dailies-directory ".")
  (org-roam-db-extra-links-exclude-keys '((node-property "ROAM_REFS")))
  (org-roam-capture-templates
   (let ((files (directory-files "~/ems/org/src/gpe/org-roam-capture-templates/" t "\\.el$")))
    (mapcar (lambda (file)
              (with-temp-buffer
                (insert-file-contents file)
                (read (buffer-string))))
            files)))
  (org-publish-project-alist
   `(
     ("emsenn-net" :components ("emsenn-net-all"))
     ("emsenn-net-all"
      :base-directory "~/ems/org"
      :exclude ".*\\.org$"
      :include ,(gpe/org-roam-public-node-files)
      :publishing-function org-html-publish-to-html
      :preparation-function gpe/org-publish-prepare-backlinks
      :html-doctype "html5"
      :html-html5-fancy t
      :html-container "section"
      :headline-levels 6
      :recursive t
      :html-divs ((preamble "section" "preamble")
              (content "main" "content")
              (postamble "section" "postamble"))
      :html-head-include-scripts nil
      :html-head-include-default-style nil
      :html-head ,gpe/emsenn-net-head
      :html-format-drawer-function gpe/org-html-format-drawer
      :exclude "\\`per/\\|README.org\\|DRAFT\\|PRIVATE"
      :with-footnotes t
      :with-date nil
      :with-timestamps nil
      :with-author nil
      :publishing-directory "~/ems/pub/emsenn-net"
      :makeindex t
      :html-preamble ,gpe/emsenn-net-preamble
      )
     ))
  :bind (
         ("C-c n i" . org-roam-node-insert)
         ("C-c n f" . org-roam-node-find)
         ("C-c n c" . org-roam-node-capture)
         ("C-c n d" . org-roam-dailies-capture-today)
         ("C-c n D" . org-roam-dailies-capture-yesterday)
         )

    )
(use-package
  org-transclusion :straight t
  :custom
  (org-transclusion-exclude-elements '(keyword property-drawer))
  :bind (
         ("C-c n t" . org-transclusion-mode)
         ("C-c n T" . org-transclusion-add)
         ))
(use-package htmlize :straight t)
(use-package magit :straight t)
(use-package org-roam-ui
  :straight t)
;;; Org-drill
(use-package
  org-drill :straight t
  :custom
  (org-drill-scope 'agenda))

Old Source

This source block is a functioning verison of GnoponEmacs, captured [2025-08-08 Fri 06:06]. I'm keeping it here to make sure I don't lose any necessary chunks as I refactor the chunks into the GnoponEmacs Org-mode file.

;;; GnoponEmacs

;;; Package Management
(defvar straight-install-script-path
  "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el")

(setq straight-repository-branch "develop")

(defvar bootstrap-version)
(let ((bootstrap-file
         (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
        (bootstrap-version 5))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
    (url-retrieve-synchronously straight-install-script-path
     'silent 'inhibit-cookies)
        (goto-char (point-max))
        (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))
  (straight-use-package 'use-package)



;;; User Configuration
(setq user-mail-address "emsenn@emsenn.net")
(setq ring-bell-function 'ignore)

;;; User Experience
(setq inhibit-startup-message t)
(setq inhibit-startup-screen t)
(setq initial-scratch-message
        ";; LANDBACK")
(menu-bar-mode -1)
(tool-bar-mode -1)
(show-paren-mode 1)
(setq line-number-mode t)
(setq column-number-mode t)
(setq-default truncate-lines t)
(setq-default line-spacing 1)
(setq sentence-end-double-space nil)
(global-visual-line-mode t)
(setq echo-keystrokes 0.1)
(setq fill-column 68)
(setq mouse-wheel-scroll-amount '(1 ((shift) .1)))
(use-package mixed-pitch
  :straight t
  :hook
  (text-mode . mixed-pitch-mode))
(use-package modus-themes
  :straight t
  :config
  (load-theme 'modus-vivendi t))
(use-package olivetti
:straight t
:hook
(text-mode . olivetti-mode))
(require 'ucs-normalize)

;;; Beancount
(straight-use-package
 '(beancount :type git :host github
             :repo "beancount/beancount-mode"))

;;; Emacs Behaviors
(setq make-backup-files nil)

;;; HTMLize
(use-package htmlize :straight t)

;;; Magit
(use-package magit :straight t)

;;; YASnippet

(use-package
  yasnippet :straight t
  :hook ((text-mode
          prog-mode
          conf-mode
          snippet-mode)
         . yas-minor-mode-on)
  :custom (yas-snippet-dirs '("~/ems/org/met/yas-snippets")))

;;; Org-mode
(use-package
  org :straight t
  :custom
  (org-directory "~/ems/org")
  (org-startup-folded 'fold)
  (org-hide-block-startup 'hideblocks)
  (org-catch-invisible-edits 'show-and-error)
  (org-agenda-custom-commands
        '(("A" "Agenda and TODO"
           ((agenda "" )
            (alltodo "" )))))
  (org-agenda-todo-ignore-scheduled 'all)
  (org-agenda-files (list "~/ems/org"))
  (org-export-with-section-numbers nil)
  (org-export-with-smart-quotes nil)
  (org-export-with-toc nil)
  (org-html-htmlize-output-type 'nil)
  (org-html-validation-link nil)
  (org-log-done 'time)
  (org-log-into-drawer 'LOGBOOK)
  (org-image-actual-width '(400))
  (org-cite-global-bibliography '("~/ems/bibliography.bib"))
  (org-export-global-macros
   '(
     ("nl" . "(eval \"\\n\")")
     ))
  :bind (
         ("C-c l" . org-store-link)
         ("C-c a" . org-agenda)
         ("C-c c" . org-capture)
         ("C-c t" . org-toggle-link-display)
         ("C-c C-q" . counsel-org-tag)
         )
  :defines gpe/emsenn-net-head
  :config

        ;; from https://www.john2x.com/blog/blogging-with-orgmode.html
        (defun gpe/org-publish-format-sitemap (title sitemap)
          (let ((posts (cdr sitemap)))
            (concat
             (format "#+title: %s\n" title)
             "#+options: timestamp:nil date:nil\n"
             (format "#+date: %s\n" (format-time-string "[%Y-%m-%d %a %H:%M:%S]" (current-time)))
             (org-list-to-org (cons (car sitemap) posts)))))
        (setq gpe/emsenn-net-head
              (with-temp-buffer
                (insert "<style>\n")
                (insert-file-contents "~/ems/org/src/emsenn-net/main.css")
                (goto-char (point-max)) ;; move to end before appending
                (insert "\n</style>")
                (buffer-string)))
        (setq gpe/emsenn-net-preamble
              (with-temp-buffer
                (insert-file-contents "~/ems/org/src/emsenn-net/preamble.html")
                (buffer-string)))
  (org-babel-do-load-languages 'org-babel-load-languages
                               '(
                                 (shell . t)
                                 )))

(use-package
  org-roam :straight t
  :after org
  :config
  (defvar gpe/org-roam-public-node-files '())
  (defun gpe/insert-backlinks (backend)
  "Insert backlinks at the end of the buffer before Org export.
BACKEND is the export backend symbol, e.g., 'html or 'latex."
  (when (org-export-derived-backend-p backend 'html)
    (let ((node (org-roam-node-at-point)))
      (when node
        (save-excursion
          (goto-char (point-max))
          (insert "\n* Backlinks\n")
          (dolist (ref (org-roam-backlinks-get node))
            (let* ((source (org-roam-backlink-source-node ref))
                   (props (org-roam-node-properties source))
                   (is-public (string= (cdr (assoc "PUBLIC" props)) "true")))
              (when is-public
                (insert (format "- [[id:%s][%s]]\n"
                                (org-roam-node-id source)
                                (org-roam-node-title source)))))))))))
  (defun gpe/org-roam-public-node-files ()
    "Return a list of Org-roam files where PUBLIC property is true"
    (mapcar #'car
            (org-roam-db-query
             [:select [file]
                      :from nodes
                      :where (like properties '"%public%true%")])))
  (defun gpe/org-roam-open-random-node ()
    "Open a random Org-roam node from the database."
    (interactive)
    (let* ((nodes (org-roam-node-list))
           (count (length nodes)))
      (if (zerop count)
          (message "No Org-roam nodes found.")
        (let* ((random-index (random count))
               (node (nth random-index nodes)))
          (org-roam-node-visit node)))))
  (defun gpe/apply-capture-properties ()
    (let ((properties '(:author :form)))
      (dolist (property properties)
        (let ((value (plist-get org-capture-current-plist property)))
          (when value
            (org-set-property (substring (symbol-name property) 1) value))))))
    (cl-defmethod org-roam-node-slug ((node org-roam-node))
    "Return the slug of NODE."
    (let ((title (org-roam-node-title node))
        (slug-trim-chars '(;; Combining Diacritical Marks https://www.unicode.org/charts/PDF/U0300.pdf
                             768 ; U+0300 COMBINING GRAVE ACCENT
                             769 ; U+0301 COMBINING ACUTE ACCENT
                             770 ; U+0302 COMBINING CIRCUMFLEX ACCENT
                             771 ; U+0303 COMBINING TILDE
                             772 ; U+0304 COMBINING MACRON
                             774 ; U+0306 COMBINING BREVE
                             775 ; U+0307 COMBINING DOT ABOVE
                             776 ; U+0308 COMBINING DIAERESIS
                             777 ; U+0309 COMBINING HOOK ABOVE
                             778 ; U+030A COMBINING RING ABOVE
                             780 ; U+030C COMBINING CARON
                             795 ; U+031B COMBINING HORN
                             803 ; U+0323 COMBINING DOT BELOW
                             804 ; U+0324 COMBINING DIAERESIS BELOW
                             805 ; U+0325 COMBINING RING BELOW
                             807 ; U+0327 COMBINING CEDILLA
                             813 ; U+032D COMBINING CIRCUMFLEX ACCENT BELOW
                             814 ; U+032E COMBINING BREVE BELOW
                             816 ; U+0330 COMBINING TILDE BELOW
                             817 ; U+0331 COMBINING MACRON BELOW
                             )))
      (cl-flet* ((nonspacing-mark-p (char)
                                    (memq char slug-trim-chars))
                 (strip-nonspacing-marks (s)
                                         (ucs-normalize-NFC-string
                                        (apply #'string (seq-remove #'nonspacing-mark-p
                                                                      (ucs-normalize-NFD-string s)))))
                 (cl-replace (title pair)
                             (replace-regexp-in-string (car pair) (cdr pair) title)))
        (let* ((pairs `(("[^[:alnum:][:digit:]]" . "-") ;; convert anything not alphanumeric
                        ("--*" . "-")                   ;; remove sequential underscores
                        ("^-" . "")                     ;; remove starting underscore
                        ("-$" . "")))                   ;; remove ending underscore
               (slug (-reduce-from #'cl-replace (strip-nonspacing-marks title) pairs)))
        (downcase slug)))))
  :hook
  ((org-export-before-processing . gpe/insert-backlinks)
  (org-capture-mode . gpe/apply-capture-properties))
  :custom
  (org-roam-directory (file-truename "~/ems/org"))
  (org-roam-dailies-directory ".")
  (org-roam-db-extra-links-exclude-keys '((node-property "ROAM_REFS")))
  (org-roam-capture-templates
   (let ((files (directory-files "~/ems/org/src/gpe/org-roam-capture-templates/" t "\\.el$")))
    (mapcar (lambda (file)
              (with-temp-buffer
                (insert-file-contents file)
                (read (buffer-string))))
            files)))
  (org-roam-title-to-slug-function
  (org-publish-project-alist
   `(
     ("emsenn-net" :components ("emsenn-net-all"))
     ("emsenn-net-all"
      :base-directory "~/ems/org"
      :exclude ".*\\.org$"
      :include ,(gpe/org-roam-public-node-files)
      :publishing-function org-html-publish-to-html
      :preparation-function gpe/org-publish-prepare-backlinks
      :html-doctype "html5"
      :html-html5-fancy t
      :html-container "section"
      :headline-levels 6
      :recursive t
      :html-divs ((preamble "section" "preamble")
              (content "main" "content")
              (postamble "section" "postamble"))
      :html-head-include-scripts nil
      :html-head-include-default-style nil
      :html-head ,gpe/emsenn-net-head
      :html-format-drawer-function gpe/org-html-format-drawer
      :exclude "\\`per/\\|README.org\\|DRAFT\\|PRIVATE"
      :with-footnotes t
      :with-date nil
      :with-timestamps nil
      :with-author nil
      :publishing-directory "~/ems/pub/emsenn-net"
      :makeindex t
      :html-preamble ,gpe/emsenn-net-preamble
      )
     ))
  :bind (
         ("C-c n i" . org-roam-node-insert)
         ("C-c n f" . org-roam-node-find)
         ("C-c n c" . org-roam-node-capture)
         ("C-c n d" . org-roam-dailies-capture-today)
         ("C-c n D" . org-roam-dailies-capture-yesterday)
         )

    )

;;; Org-roam-UI
(use-package org-roam-ui
  :straight t)

;;; Org-drill
(use-package
  org-drill :straight t
  :custom
  (org-drill-scope 'agenda))

;;; Org-transclusion
(use-package
  org-transclusion :straight t
  :custom
  (org-transclusion-exclude-elements '(keyword property-drawer))
  :bind (
         ("C-c n t" . org-transclusion-mode)
         ("C-c n T" . org-transclusion-add)
         ))

Backlinks

Created: 2025-08-19 Tue 23:03