diff options
| author | Szymon Szukalski <szymon@szymonszukalski.com> | 2026-04-07 09:19:44 +1000 |
|---|---|---|
| committer | Szymon Szukalski <szymon@szymonszukalski.com> | 2026-04-07 09:19:44 +1000 |
| commit | 62b9eded8a04e9236b62b8ecbfb0eaa2fd1a60d1 (patch) | |
| tree | 634d99711995e4c0352abc03d99d6136b2d3e56b | |
| parent | 0d787210e8be2ffe53a44c2d8af95c849c776f53 (diff) | |
refactor: simplify org config
| -rw-r--r-- | README.md | 6 | ||||
| -rw-r--r-- | config.org | 420 |
2 files changed, 151 insertions, 275 deletions
@@ -75,9 +75,9 @@ emacs --batch -Q --load ./init.el ### MOC -There is a central [moc.org](/Users/skas/org/moc.org) note at `~/org/moc.org`. It is a deliberately small, curated navigation surface for the notes system rather than an exhaustive index or system of record. +There is a central `~/org/moc.org` note. It is a deliberately small, curated navigation surface for the notes system rather than an exhaustive index or system of record, and the config assumes the file already exists. -The MOC opens automatically on Emacs startup through startup-hook behavior, but it is not configured as the default fallback buffer when nothing is open. `C-c n M` opens it manually at any time. +The MOC opens automatically on Emacs startup through startup-hook behavior, but the config now just opens the file directly rather than creating it. `C-c n M` opens it manually at any time. Its Quick Access section provides actionable links for opening the agenda, today's note, capture, and a new note, while the rest of the file stays lightweight and curated around active projects, areas, and a few high-leverage resources. @@ -158,4 +158,4 @@ If you change terminal behavior, test it in a real `emacs -nw` session. Batch lo - Update `config.org` first, then regenerate `init.el` and `early-init.el`. - Keep this README aligned with the current configuration. If package usage, startup behavior, keybindings, or workflow changes, update this file in the same change. -- Do not document planned behavior as if it already exists.
\ No newline at end of file +- Do not document planned behavior as if it already exists. @@ -10,18 +10,18 @@ These settings have to exist before the first GUI frame is created, so they tangle into =early-init.el=. #+begin_src emacs-lisp :tangle early-init.el -;;; early-init.el --- generated from config.org -*- lexical-binding: t; -*- + ;;; early-init.el --- generated from config.org -*- lexical-binding: t; -*- -;;; Commentary: + ;;; Commentary: -;; This file is generated from config.org. Do not edit it directly. + ;; This file is generated from config.org. Do not edit it directly. -;;; Code: + ;;; Code: -(dolist (parameter '((width . 140) - (height . 42))) - (add-to-list 'default-frame-alist parameter) - (add-to-list 'initial-frame-alist parameter)) + (dolist (parameter '((width . 140) + (height . 42))) + (add-to-list 'default-frame-alist parameter) + (add-to-list 'initial-frame-alist parameter)) #+end_src * Bootstrapping @@ -29,22 +29,22 @@ tangle into =early-init.el=. This is the start of the main runtime entry point, which tangles into =init.el=. #+begin_src emacs-lisp -;;; init.el --- generated from config.org -*- lexical-binding: t; -*- + ;;; init.el --- generated from config.org -*- lexical-binding: t; -*- -;;; Commentary: + ;;; Commentary: -;; This file is generated from config.org. Do not edit it directly. + ;; This file is generated from config.org. Do not edit it directly. -;;; Code: + ;;; Code: -(let ((minver "27.1")) - (when (version< emacs-version minver) - (error "Your Emacs is too old -- this config requires v%s or higher" minver))) -(when (version< emacs-version "28.1") - (message - (concat - "Your Emacs is old, and some functionality in this config will be " - "disabled. Please upgrade if possible."))) + (let ((minver "27.1")) + (when (version< emacs-version minver) + (error "Your Emacs is too old -- this config requires v%s or higher" minver))) + (when (version< emacs-version "28.1") + (message + (concat + "Your Emacs is old, and some functionality in this config will be " + "disabled. Please upgrade if possible."))) #+end_src * Shared paths and system identity @@ -58,22 +58,10 @@ configuration, including the Org directory under =~/org/=. (defconst *is-a-linux* (eq system-type 'gnu/linux)) (defconst *is-a-mac* (eq system-type 'darwin)) - (defun ss/home-path (path) - "Expand PATH relative to the user's home directory." - (expand-file-name path "~")) - - (defun ss/config-path (path) - "Expand PATH relative to `user-emacs-directory'." - (expand-file-name path user-emacs-directory)) - - (defun ss/org-path (path) - "Expand PATH relative to the Org directory." - (expand-file-name path (ss/home-path "org/"))) - (provide 'init-paths) ;; Keep custom-set-variables out of the main config. - (setq custom-file (ss/config-path "custom.el")) + (setq custom-file (expand-file-name "custom.el" user-emacs-directory)) #+end_src * Package setup @@ -82,20 +70,19 @@ This section bootstraps packages and defines the archives the rest of the configuration relies on. #+begin_src emacs-lisp - (eval-and-compile - (require 'package) - - (setq package-archives - (append '(("melpa" . "https://melpa.org/packages/")) - package-archives) - package-archive-priorities '(("gnu" . 10) - ("nongnu" . 8) - ("melpa" . 5)) - package-install-upgrade-built-in t - use-package-always-ensure nil) - - (package-initialize) - (require 'use-package)) + (require 'package) + + (setq package-archives + (append '(("melpa" . "https://melpa.org/packages/")) + package-archives) + package-archive-priorities '(("gnu" . 10) + ("nongnu" . 8) + ("melpa" . 5)) + package-install-upgrade-built-in t + use-package-always-ensure nil) + + (package-initialize) + (require 'use-package) #+end_src * Interface defaults @@ -103,75 +90,34 @@ configuration relies on. This section sets the visual defaults: theme, fonts, and frame behavior. #+begin_src emacs-lisp - (defconst ss/font-family "JetBrains Mono" - "Preferred font family for GUI Emacs.") - - (defconst ss/font-height 140 - "Preferred default font height for GUI Emacs.") - - (defconst ss/font-weight 'medium - "Preferred default font weight for GUI Emacs.") - - (defconst ss/frame-width 140 - "Preferred width for graphical Emacs frames, in columns.") - - (defconst ss/frame-height 42 - "Preferred height for graphical Emacs frames, in lines.") - - (defun ss/apply-frame-size (&optional frame) - "Apply the preferred size to FRAME when it is graphical. - If FRAME is nil, use the selected frame." - (let ((target-frame (or frame (selected-frame)))) - (when (display-graphic-p target-frame) - (set-frame-size target-frame ss/frame-width ss/frame-height)))) - - (defun ss/apply-font-faces () - "Apply the original JetBrains-based face setup." - (set-face-attribute - 'default nil - :family ss/font-family :height ss/font-height :weight ss/font-weight) - (set-face-attribute - 'fixed-pitch nil - :family ss/font-family :weight ss/font-weight) - (set-face-attribute - 'fixed-pitch-serif nil - :family ss/font-family :weight ss/font-weight)) - - (defun ss/apply-gui-defaults (&optional frame) - "Apply GUI defaults to FRAME. -If FRAME is nil, use the selected frame." - (let ((target-frame (or frame (selected-frame)))) - (when (display-graphic-p target-frame) - (with-selected-frame target-frame - (ss/apply-frame-size target-frame) - (ss/disable-menu-bar) - (tool-bar-mode -1) - (scroll-bar-mode -1) - (tooltip-mode -1) - (when (find-font (font-spec :name ss/font-family)) - (ss/apply-font-faces)))))) - - (defun ss/disable-menu-bar () - "Disable the menu bar for the current frame/session." - (menu-bar-mode -1)) - - (add-hook 'after-make-frame-functions #'ss/apply-gui-defaults) - (when (display-graphic-p) - (ss/apply-gui-defaults)) + (set-frame-size (selected-frame) 140 42) + (menu-bar-mode -1) + (tool-bar-mode -1) + (scroll-bar-mode -1) + (tooltip-mode -1) + (set-face-attribute + 'default nil + :family "JetBrains Mono" :height 140 :weight 'medium) + (set-face-attribute + 'fixed-pitch nil + :family "JetBrains Mono" :weight 'medium) + (set-face-attribute + 'fixed-pitch-serif nil + :family "JetBrains Mono" :weight 'medium)) (unless (display-graphic-p) - (add-hook 'emacs-startup-hook #'ss/disable-menu-bar)) + (add-hook 'emacs-startup-hook (lambda () (menu-bar-mode -1)))) (setq inhibit-startup-message t - inhibit-startup-screen t - ring-bell-function 'ignore) + inhibit-startup-screen t + ring-bell-function 'ignore) - (use-package modus-themes - :ensure nil - :no-require t - :config - (load-theme 'modus-vivendi t)) + (use-package modus-themes + :ensure nil + :no-require t + :config + (load-theme 'modus-vivendi t)) (line-number-mode 1) (column-number-mode 1) @@ -196,39 +142,35 @@ If FRAME is nil, use the selected frame." display-time-default-load-average nil) (display-time-mode 1)) - (defun ss/mode-line-right-align () - "Return a spacer that right-aligns `mode-line-misc-info'." - (propertize - " " - 'display - `((space :align-to - (- right - ,(+ 2 (string-width (format-mode-line mode-line-misc-info)))))))) - ;; Keep the theme's faces, but make the right edge alignment dynamic. (setq-default mode-line-format (list - ;; Left padding - " " - "%e" - mode-line-front-space - mode-line-mule-info - mode-line-client - mode-line-modified - mode-line-remote - mode-line-frame-identification - mode-line-buffer-identification - " " - mode-line-position - '(vc-mode vc-mode) - " " - mode-line-modes - ;; Right-align from here - '(:eval (ss/mode-line-right-align)) - mode-line-misc-info - ;; Right padding - " " - mode-line-end-spaces)) + ;; Left padding + " " + "%e" + mode-line-front-space + mode-line-mule-info + mode-line-client + mode-line-modified + mode-line-remote + mode-line-frame-identification + mode-line-buffer-identification + " " + mode-line-position + '(vc-mode vc-mode) + " " + mode-line-modes + ;; Right-align from here + '(:eval (propertize + " " + 'display + `((space :align-to + (- right + ,(+ 2 (string-width (format-mode-line mode-line-misc-info)))))))) + mode-line-misc-info + ;; Right padding + " " + mode-line-end-spaces)) #+end_src @@ -243,28 +185,28 @@ choices. (prefer-coding-system 'utf-8) (setq auto-save-default nil - backup-inhibited t - echo-keystrokes 0.1 - compilation-ask-about-save nil - mouse-wheel-scroll-amount '(1 ((shift) . 1)) - mouse-wheel-progressive-speed nil - mouse-wheel-follow-mouse t - scroll-step 1 - scroll-conservatively 101 - enable-recursive-minibuffers t - gc-cons-threshold (* 128 1024 1024) - read-process-output-max (* 4 1024 1024) - process-adaptive-read-buffering nil) + backup-inhibited t + echo-keystrokes 0.1 + compilation-ask-about-save nil + mouse-wheel-scroll-amount '(1 ((shift) . 1)) + mouse-wheel-progressive-speed nil + mouse-wheel-follow-mouse t + scroll-step 1 + scroll-conservatively 101 + enable-recursive-minibuffers t + gc-cons-threshold (* 128 1024 1024) + read-process-output-max (* 4 1024 1024) + process-adaptive-read-buffering nil) (fset 'yes-or-no-p 'y-or-n-p) (global-auto-revert-mode 1) (delete-selection-mode 1) (setq-default indent-tabs-mode nil - fill-column 80 - tab-width 2 - indicate-empty-lines t - sentence-end-double-space nil) + fill-column 80 + tab-width 2 + indicate-empty-lines t + sentence-end-double-space nil) #+end_src @@ -326,7 +268,8 @@ PARA notes, so project, area, and resource files can surface TODOs without pulling in daily or archived notes. A small directory helper keeps PARA subdirectories easy to create from the minibuffer before capturing into them. A curated =moc.org= in the Org root acts as the startup landing page and quick -navigation surface without becoming a global fallback buffer. +navigation surface. The config assumes that file already exists and opens it +directly during startup rather than creating it on demand. #+begin_src emacs-lisp (use-package org @@ -355,6 +298,9 @@ navigation surface without becoming a global fallback buffer. (defconst ss/org-archives-directory (expand-file-name "archives/" ss/org-directory) "Directory for archived notes.") + (defconst ss/moc-file (expand-file-name "moc.org" ss/org-directory) + "Central MOC note.") + (defconst ss/org-note-directories (list ss/org-directory ss/org-daily-directory @@ -367,7 +313,7 @@ navigation surface without becoming a global fallback buffer. (defconst ss/org-agenda-directories (list ss/org-projects-directory - ss/org-areas-directory) + ss/org-areas-directory) "Directories whose Org files feed the agenda.") (defconst ss/org-subdirectory-roots @@ -379,7 +325,7 @@ navigation surface without becoming a global fallback buffer. (defun ss/denote-capture-in-directory (directory &optional keywords &rest prompts) "Start a Denote Org capture in DIRECTORY with KEYWORDS and PROMPTS. -If PROMPTS is empty, rely on `denote-prompts'." + If PROMPTS is empty, rely on `denote-prompts'." (let* ((prompt-for-keywords (memq :keywords prompts)) (denote-directory directory) (denote-use-directory (unless (memq :subdirectory prompts) directory)) @@ -396,17 +342,6 @@ If PROMPTS is empty, rely on `denote-prompts'." (memq :template prompts)) (denote-org-capture)))) - (defun ss/note-subdirectory-candidates (root) - "Return existing subdirectories under ROOT as relative paths." - (sort - (delete-dups - (mapcar (lambda (path) - (directory-file-name (file-relative-name path root))) - (seq-filter - #'file-directory-p - (directory-files-recursively root directory-files-no-dot-files-regexp t t)))) - #'string<)) - (defun ss/create-note-subdirectory () "Create a PARA subdirectory using minibuffer completion." (interactive) @@ -416,105 +351,52 @@ If PROMPTS is empty, rely on `denote-prompts'." nil t)) (root (alist-get root-name ss/org-subdirectory-roots nil nil #'string=)) (completion-extra-properties '(:category file)) + (candidates + (sort + (delete-dups + (mapcar (lambda (path) + (directory-file-name (file-relative-name path root))) + (seq-filter + #'file-directory-p + (directory-files-recursively root directory-files-no-dot-files-regexp t t)))) + #'string<)) (subdirectory (completing-read (format "Subdirectory in %s: " root-name) - (ss/note-subdirectory-candidates root) + candidates nil nil)) (target (expand-file-name subdirectory root)) (existing (file-directory-p target))) (make-directory target t) - (ss/refresh-org-agenda-files) + (ss/refresh-org-agenda-files) (message "%s note directory: %s" (if existing "Using existing" "Created") target))) - (defun ss/ensure-org-note-directories () - "Create the Org directories used by the notes workflow." - (mapc (lambda (directory) - (make-directory directory t)) - ss/org-note-directories)) - - (defun ss/ensure-org-agenda-loaded () - "Load Org agenda support before using agenda-specific helpers. -This ensures `org-agenda-file-regexp' and `org-agenda' are available." - (require 'org-agenda)) - - (defun ss/org-agenda-files () - "Return the Org files that should be scanned by the agenda." - (ss/ensure-org-agenda-loaded) - (delete-dups - (apply #'append - (mapcar (lambda (directory) - (if (file-directory-p directory) - (directory-files-recursively directory org-agenda-file-regexp) - nil)) - ss/org-agenda-directories)))) - (defun ss/refresh-org-agenda-files (&rest _) "Refresh `org-agenda-files' from the current PARA directories. -Ignore any arguments passed by advice wrappers." - (setq org-agenda-files (ss/org-agenda-files))) - - (defun ss/daily-note-path (&optional time) - "Return the file name for the daily note at TIME. -If TIME is nil, use the current date." - (expand-file-name - (format-time-string "%Y-%m-%d.org" time) - ss/org-daily-directory)) - - (defun ss/moc-path () - "Return the file name for the central MOC note." - (expand-file-name "moc.org" ss/org-directory)) - - (defun ss/moc-template () - "Return the initial contents for the central MOC note." - (concat - "#+title: MOC\n\n" - "* Quick Access\n" - "- [[elisp:(ss/open-agenda)][Open agenda]]\n" - "- [[elisp:(ss/open-todays-note)][Today's note]]\n" - "- [[elisp:(org-capture nil)][Capture]]\n" - "- [[elisp:(denote-open-or-create)][New note]]\n\n" - "* Projects (active)\n" - "- Backend Chapter Uplift\n" - "- API Strategy\n" - "- Maturity Matrix\n" - "- DPT Ways of Working\n\n" - "* Areas\n" - "- My Role\n" - "- People and Performance\n" - "- SE Practice\n" - "- Stakeholder Alignment\n" - "- Engineering Department\n\n" - "* Resources (high leverage)\n" - "- API Strategy Reference\n" - "- Triage System\n" - "- Weekly Focus\n")) - - (defun ss/ensure-moc () - "Create the central MOC note when it does not exist. -Return the path to the note." - (let ((file (ss/moc-path))) - (unless (file-exists-p file) - (make-directory (file-name-directory file) t) - (with-temp-file file - (insert (ss/moc-template)))) - file)) - - (defun ss/daily-note-template (&optional time) - "Return the initial contents for the daily note at TIME." - (format "#+title: %s\n\n* Tasks\n\n* Notes\n\n* Open Loops\n" - (format-time-string "%Y-%m-%d" (or time (current-time))))) + Ignore any arguments passed by advice wrappers." + (require 'org-agenda) + (setq org-agenda-files + (delete-dups + (apply #'append + (mapcar (lambda (directory) + (if (file-directory-p directory) + (directory-files-recursively directory org-agenda-file-regexp) + nil)) + ss/org-agenda-directories))))) (defun ss/ensure-daily-note (&optional time) "Create the daily note for TIME when it does not exist. -Return the path to the note." + Return the path to the note." (let* ((date (or time (current-time))) - (file (ss/daily-note-path date))) + (file (expand-file-name + (format-time-string "%Y-%m-%d.org" date) + ss/org-daily-directory))) (unless (file-exists-p file) (make-directory (file-name-directory file) t) (with-temp-file file - (insert (ss/daily-note-template date)))) + (insert (format "#+title: %s\n\n* Tasks\n\n* Notes\n\n* Open Loops\n" + (format-time-string "%Y-%m-%d" date))))) file)) (defun ss/open-todays-note () @@ -525,22 +407,14 @@ Return the path to the note." (defun ss/open-moc () "Open the central MOC note." (interactive) - (find-file (ss/ensure-moc))) - - (defun ss/open-moc-on-startup () - "Open the central MOC note during normal startup." - (when (and (equal (buffer-name (current-buffer)) "*scratch*") - (not buffer-file-name)) - (ss/open-moc))) + (find-file ss/moc-file)) (defun ss/open-agenda () "Refresh agenda files and invoke `org-agenda'." (interactive) - (ss/ensure-org-agenda-loaded) (call-interactively #'org-agenda)) :init - (add-hook 'emacs-startup-hook #'ss/open-moc-on-startup) - (ss/ensure-moc) + (add-hook 'emacs-startup-hook (lambda () (find-file ss/moc-file))) :bind (("C-c a" . ss/open-agenda) ("C-c c" . org-capture) ("C-c n M" . ss/open-moc) @@ -548,7 +422,7 @@ Return the path to the note." ("C-c n d" . ss/open-todays-note)) :config (setq org-directory ss/org-directory - org-hide-emphasis-markers t) + org-hide-emphasis-markers t) (add-hook 'org-mode-hook (lambda () (setq-local org-hide-emphasis-markers t) @@ -556,7 +430,9 @@ Return the path to the note." (font-lock-ensure))) (ss/refresh-org-agenda-files) (advice-add 'org-agenda :before #'ss/refresh-org-agenda-files) - (ss/ensure-org-note-directories)) + (mapc (lambda (directory) + (make-directory directory t)) + ss/org-note-directories)) #+end_src ** Capture entry points @@ -684,14 +560,14 @@ plumbing in this file. #+begin_src emacs-lisp (use-package gptel :ensure t - :init + :init (setq gptel-default-mode 'org-mode - gptel-model 'gpt-4o - gptel-backend (gptel-make-gh-copilot "Copilot")) + gptel-model 'gpt-4o + gptel-backend (gptel-make-gh-copilot "Copilot")) :bind (("C-c n g" . gptel) - ("C-c n s" . gptel-send) - ("C-c n r" . gptel-rewrite) - ("C-c n a" . gptel-add))) + ("C-c n s" . gptel-send) + ("C-c n r" . gptel-rewrite) + ("C-c n a" . gptel-add))) #+end_src * Generated file footers @@ -700,13 +576,13 @@ The closing blocks just finish the generated startup files cleanly. #+begin_src emacs-lisp -(when (file-exists-p custom-file) - (load custom-file nil 'nomessage)) + (when (file-exists-p custom-file) + (load custom-file nil 'nomessage)) -;;; init.el ends here + ;;; init.el ends here #+end_src #+begin_src emacs-lisp :tangle early-init.el -;;; early-init.el ends here + ;;; early-init.el ends here #+end_src |
