diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..b9c4c2a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,53 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Repository Purpose + +Personal dotfiles repository for Shawn Borton's development environment. Configs for Doom Emacs, Vim, Bash, and tmux — primarily targeting Linux (Arch Linux and NixOS systems: archie, peregrine) with some historical macOS artifacts. + +## File Overview + +| File | Purpose | +|------|---------| +| `config.el` | Primary Doom Emacs private config (keybindings, UI, packages config) | +| `init.el` | Doom Emacs module selection | +| `packages.el` | Additional Doom Emacs package declarations | +| `.vimrc` | Vim config with vim-plug plugin manager | +| `.bash_profile` | Shell aliases, prompt, NVM setup | +| `.tmux.conf` | tmux keybindings and status bar | + +## Doom Emacs Workflow + +After editing `config.el`, `init.el`, or `packages.el`: + +```bash +doom sync # Sync packages and modules (required after packages.el changes) +doom reload # Reload config without restarting (for config.el/init.el changes) +``` + +## Key Conventions + +**Doom Emacs:** +- Uses Evil mode with `jk` as the escape sequence +- Leader key bindings are defined in `config.el` under `map!` blocks +- Custom packages are declared in `packages.el` using `package!` with optional `:recipe` for git sources +- Modules are toggled in `init.el` with flags like `(python +lsp +pyright +tree-sitter)` +- Fonts: JetBrains Mono (14pt) for fixed-pitch, Overpass Nerd Font (18pt) for variable-pitch +- Theme: `doom-oceanic-next` + +**Vim:** +- Plugin manager: vim-plug (auto-installs to `~/.vim/autoload/plug.vim` on first run) +- Run `:PlugInstall` after adding new plugins to `.vimrc` + +**tmux:** +- Prefix is `C-a` (not the default `C-b`) +- Reload config with `C-a r` + +## Notable Integrations in config.el + +- **Elfeed** (RSS): Connects to a self-hosted Fever API at `rss.onionpatch.cloud`; feeds are tagged and filtered with custom keybindings under the leader key +- **Pocket**: Integrated with elfeed for article saving via `pocket-reader` +- **gptel / chatgpt-shell**: LLM/ChatGPT integration packages declared in `packages.el` +- **org-roam**: Unpinned in `packages.el` for a custom version; roam2 module enabled in `init.el` +- **Dired**: Custom `h`/`l` navigation keys; Thunar integration for GUI file manager diff --git a/config.el b/config.el index 806088f..81fe97c 100644 --- a/config.el +++ b/config.el @@ -2,9 +2,24 @@ ;; Place your private configuration here! Remember, you do not need to run 'doom ;; sync' after modifying this file! +;; ** Default Init File +;; Load the default init file. This is the place where distro packagers can place +;; their own configuration. Doom disables it as an optimization, but it's useful +;; for storing information that can only reliably be obtained through package +;; managers. More specifically, it's used in this dotfiles to specify the full path +;; to some commands used by Doom. Doing so removes the need to clutter =PATH= with +;; commands not needed outside of Doom. +;; +;; +;;#+begin_src emacs-lisp +;;(load "default" 'noerror 'nomessage) +;;#+end_src + + ;; Sane defaults (defvar is-archie-p (equal (system-name) "archie")) +(defvar is-peregrine-p (equal (system-name) "peregrine")) (defvar is-macos-p (equal (system-name) "Shawns-Macbook-Pro.local")) (setq-default window-combination-resize t) (setq global-auto-revert-non-file-buffers t) @@ -13,8 +28,11 @@ password-cache-expiry nil ; Trust your computers. scroll-margin 2 ; It's nice to maintain a little margin window-resize-pixelwise t + reb-re-syntax 'string ; Normalize Emacs regexps ) -(setq auth-sources "~/.authinfo.gpg") +;;; (setq auth-sources "~/.authinfo.gpg") +;;;(setq auth-sources +;;; '((:source "~/.authinfo.gpg"))) (display-time-mode 1) (global-subword-mode 1) @@ -49,11 +67,12 @@ ;; `load-theme' function. This is the default: ;;(use-package! all-the-icons) (setq doom-font (font-spec :family "JetBrains Mono" :style "Regular" :size 14) - doom-variable-pitch-font (font-spec :family "JetBrains Mono" :size 18) + doom-variable-pitch-font (font-spec :family "Overpass Nerd Font" :style "Regular" :size 18) doom-big-font (font-spec :family "JetBrains Mono" :style "Regular" :size 26)) -(defvar sb-org-font (font-spec :family "Source Code Pro" :style "Regular" :size 14)) +(defvar sb-org-font (font-spec :family "SauceCodePro" :style "Regular" :size 14)) -(setq doom-theme 'doom-nord) +;; (setq doom-theme 'doom-nord) +(setq doom-theme 'doom-oceanic-next) (after! doom-themes ;;(load-theme 'doom-nano-dark t) (setq doom-themes-enable-bold t @@ -61,6 +80,10 @@ (map! :leader :desc "Load new theme" "h t" #'counsel-load-theme) +(funcall (lambda () + (set-frame-parameter nil 'alpha-background 95) + (add-to-list 'default-frame-alist '(alpha-background . 95)) + )) ;; This determines the style of line numbers in effect. If set to `nil', line ;; numbers are disabled. For relative line numbers, set this to `relative'. @@ -77,7 +100,7 @@ ;; remove the modes from `doom-modeline-continuous-word-count-modes'. ;; (after! doom-modeline - (setq doom-modeline-height 18) + (setq doom-modeline-height 28) (setq doom-modeline-bar-width 6) (setq doom-modeline-enable-word-count t) (setq doom-modeline-continuous-word-count-modes '(markdown-mode org-mode))) @@ -90,6 +113,10 @@ (consult-buffer)) (setq +ivy-buffer-preview t) +;; Escape insert mode with 'jk': +(after! evil-escape + (setq! evil-escape-key-sequence "jk")) + ;; Here are some additional functions/macros that could help you configure Doom: ;; ;; - `load!' for loading external *.el files relative to this one @@ -123,8 +150,8 @@ "Open the current directory in Thunar." (interactive) (let ((default-directory (if (buffer-file-name) - (file-name-directory (buffer-file-name)) - default-directory))) + (file-name-directory (buffer-file-name)) + default-directory))) (start-process "Thunar" nil "thunar" default-directory))) (defun sbinfo/my-find-file-with-pattern (dir pattern) @@ -170,22 +197,22 @@ ;; (map! :leader ;; :desc "Edit agenda file" ;; "- a" #'(lambda () (interactive) (find-file "~/Org/agenda.org")) -(map! :leader - :desc "Edit eshell aliases" - "- e" #'(lambda () (interactive) (find-file "~/.doom.d/aliases")) - :leader - :desc "Edit doom init.el" - "- i" #'(lambda () (interactive) (find-file "~/.doom.d/init.el")) - :leader - :desc "Edit doom config.el" - "- c" #'(lambda () (interactive) (find-file "~/.doom.d/config.el")) - :leader - :desc "Edit doom packages.el" - "- p" #'(lambda () (interactive) (find-file "~/.doom.d/packages.el"))) +;; (map! :leader +;; :desc "Edit eshell aliases" +;; "- e" #'(lambda () (interactive) (find-file "~/.doom.d/aliases")) +;; :leader +;; :desc "Edit doom init.el" +;; "- i" #'(lambda () (interactive) (find-file "~/.doom.d/init.el")) +;; :leader +;; :desc "Edit doom config.el" +;; "- c" #'(lambda () (interactive) (find-file "~/.doom.d/config.el")) +;; :leader +;; :desc "Edit doom packages.el" +;; "- p" #'(lambda () (interactive) (find-file "~/.doom.d/packages.el"))) ;; (after! mastodon - ;; (setq mastodon-auth-source-file "~/.authinfo") - ;; (setq mastodon-instance-url "https://distrotoot.com/")) +;; (setq mastodon-auth-source-file "~/.authinfo") +;; (setq mastodon-instance-url "https://distrotoot.com/")) ;; TWITTER (setq twittering-use-master-password t) @@ -197,11 +224,24 @@ ;;(setq rmh-elfeed-org-files (list "~/Dropbox/Org/feedly.org")) (setq elfeed-use-curl t) (setq elfeed-protocol-fever-update-unread-only t) - (setq elfeed-protocol-feeds (list - (list "fever+https://shawnb@rss.onionpatch.cloud" - :api-url "https://rss.onionpatch.cloud/api/fever.php" - :password ")9vwibuQmv9pr" - :autotags '(("https://www.axios.com" news)("Hacker News" technology))))) + (setq elfeed-protocol-feeds '(("fever+https://shawnb@rss.onionpatch.cloud" + :api-url "https://rss.onionpatch.cloud/api/fever.php" + :password ")9vwibuQmv9pr" + :autotags (("https://feeds.arstechnica.com/arstechnica/index" secondary) + ("https://api.axios.com/feed/" nooz) + ("https://rss.csmonitor.com/feeds/all" nooz secondary) + ("https://hnrss.org/newest?points=150") + ("https://theimaginativeconservative.org/feed" primary) + ("https://kill-the-newsletter.com/feeds/nnug6b0e4e7h8swq.xml" bellingcat nooz primary) + ("https://planet.emacslife.com/atom.xml" primary emacs) + ("https://www.plough.com/en/Plough-RSS-Feed" primary religion) + ("https://reason.com/feed/" nooz secondary) + ("https://www.readtangle.com/rss/" primary nooz) + ("https://tracydurnell.com/feed/" primary blog) + ("https://www.voanews.com/api/zqboml-vomx-tpeivmy" nooz primary) + ("https://www.voanews.com/api/zpbovl-vomx-tpe_vmr" primary) + ("https://www.voanews.com/api/zyritl-vomx-tpettmq" techno primary) + ("https://www.voanews.com/api/zmbjqvl-vomx-tpeyvror" primary))))) ;; enable elfeed-protocol (setq elfeed-protocol-enabled-protocols '(fever)) (elfeed-protocol-enable) @@ -212,12 +252,12 @@ (setq main-elfeed-feed "https://shawnb@rss.onionpatch.cloud/api/fever.php") (defun freshrss-network-connection-p () (not (condition-case nil - (delete-process - (make-network-process - :name freshrss-hostname - :host "elpa.gnu.org" - :service 443)) - (error t)))) + (delete-process + (make-network-process + :name freshrss-hostname + :host "elpa.gnu.org" + :service 443)) + (error t)))) (defun elfeed-full-update () (interactive) @@ -242,11 +282,8 @@ :desc "Get Hacker News items" "r h" #'(lambda () (interactive) (elfeed-search-set-filter "@1-week-ago +unread =Hacker")) :leader - :desc "Add entry to Pocket" - "r k e" #'(lambda () (interactive) (pocket-reader-elfeed-entry-add-link)) - :leader - :desc "Add search item to Pocket" - "r k s" #'(lambda () (interactive) (pocket-reader-elfeed-search-add-link)) + :desc "Get MetaFilter items" + "r m" #'(lambda () (interactive) (elfeed-search-set-filter "@1-week-ago +unread =Meta")) :leader :desc "Get News items" "r n e" #'(lambda () (interactive) (elfeed-search-set-filter "@1-week-ago +unread +News")) @@ -262,6 +299,9 @@ :leader :desc "Get Writing items" "r w" #'(lambda () (interactive) (elfeed-search-set-filter "@1-week-ago +unread +Writing"))) +;; :leader +;; :desc "Get AI summary" +;; "r z" #'(lambda () (interactive) (elfeed-summarize-search-toggle-summary))) (use-package! elfeed-goodies :init (elfeed-goodies/setup) @@ -280,35 +320,45 @@ (setq elfeed-goodies/entry-pane-position 'bottom) (setq elfeed-search-filter "@1-week-ago +unread " - elfeed-search-title-min-width 80) - ;;elfeed-show-refresh-function #'+rss/elfeed-show-refresh--better-style) + elfeed-search-title-min-width 80 + shr-max-image-proportion 0.6) -;;(defface elfeed-show-title-face '((t (:weight ultrabold :slant italic :height 1.5))) - ;; "title face in elfeed show buffer" - ;; :group 'elfeed) +;; (defface elfeed-show-title-face '((t (:weight ultrabold :slant italic :height 1.5))) +;; "title face in elfeed show buffer" +;; :group 'elfeed) +;; (defface elfeed-show-author-face `((t (:weight light))) +;; "title face in elfeed show buffer" +;; :group 'elfeed) +;; (set-face-attribute 'elfeed-search-title-face nil +;; :foreground 'nil +;; :weight 'light) -;; (after! elfeed-show - ;; (require 'url)) - -(defun +rss/elfeed-show-refresh--better-style () - "Update the buffer to match the selected entry, using a mail-style." +(defun sbinfo/elfeed-filter-by-ordinality () + "Select a default filter and update elfeed." (interactive) - (let* ((inhibit-read-only t) - (title (elfeed-entry-title elfeed-show-entry)) - (date (seconds-to-time (elfeed-entry-date elfeed-show-entry))) - (author (elfeed-meta elfeed-show-entry :author)) - (link (elfeed-entry-link elfeed-show-entry)) - (tags (elfeed-entry-tags elfeed-show-entry)) - (tagsstr (mapconcat #'symbol-name tags ", ")) - (nicedate (format-time-string ("%a, %e %b %Y %T %Z" date)) - (content (elfeed-deref (elfeed-entry-content elfeed-show-entry))) - (type (elfeed-entry-content-type elfeed-show-entry)) - (feed (elfeed-entry-feed elfeed-show-entry)) - (feed-title (elfeed-feed-title feed)) - (base (and feed (elfeed-compute-base (elfeed-feed-url feed))))) - (erase-buffer) - (insert "\n"))) - ) + (let* ((filters + '(("Default - 3 Days" . "@3-days-ago +unread -Sports -Videos -wait") + ("Default - 7 Days" . "@7-days-ago +unread -Sports -Videos -wait") + ("Default with Videos" . "@7-days-ago +unread") + ("Primary - 7 Days" . "@7-days-ago +unread +primary") + ("Secondary - 7 Days" . "@7-days-ago +unread +secondary") + ("Weekly News" . "@7-days-ago +nooz") + ("Wait Items" . "@7-days-ago +unread +wait"))) + ;; ("Third (3rd)" . "@7-days-ago +3rd +unread") + ;; ("Outlets" . "@2-days-ago +outlet"))) + (filter + (completing-read "Elfeed Filter: " filters nil t))) + (setq elfeed-search-filter + (alist-get filter filters nil nil #'string=)) + (elfeed-search-update :force))) + +(use-package! wombag + :config + (setq wombag-host "https://read.onionpatch.cloud" ;where you access Wallabag + wombag-username "shawnb" + wombag-password "6!EzrhCk94qxm" + wombag-client-id "3_2c3vfqmpkqm8oosg8ccwo4scgoscosc0oowssgcs8oowc0w844" + wombag-client-secret "5otka1i1kpc84wwwwsc404o8o0wow4sgcksk0ok8s8ws0c4c8g")) ;; ⌘-b ; browse to URL in foreground @@ -330,11 +380,11 @@ ;; (map! :map pocket-reader-mode-map ;; "ta" #'pocket-reader-add-tags)) - (defun writing-mode () +(defun writing-mode () (interactive) ;; (zen/toggle) (buffer-face-mode) - (linum-mode 0) + ;;(linum-mode 0) (blink-cursor-mode) (visual-line-mode 1) (setq truncate-lines nil @@ -342,15 +392,18 @@ display-line-numbers nil)) (add-hook 'markdown-mode-hook 'writing-mode) -(setq ispell-program-name "/usr/bin/aspell") +;; (setq ispell-program-name "/usr/bin/aspell") +(setq ispell-program-name "/home/shawnb/.nix-profile/bin/aspell") (setq ispell-list-command "--list") (defun sbinfo/org-mode-setup () + (setq +zen-text-scale 0) (org-indent-mode) - (org-superstar-mode 1) - (org-bullets-mode 1)) - ;; (variable-pitch-mode 1) - ;; (visual-line-mode 1)) + ;; (org-superstar-mode 1) + (org-bullets-mode 1) + (display-line-numbers-mode -1) + (variable-pitch-mode 1)) +;; (visual-line-mode 1)) (defun sbinfo/org-font-setup () ;; Set faces for heading levels @@ -362,25 +415,46 @@ (org-level-6 . 1.0) (org-level-7 . 1.0) (org-level-8 . 1.0))) - ;; (set-face-attribute (car face) nil :font "JetBrains Mono" :weight 'regular :height (cdr face)))) - ;; (set-face-attribute (car face) nil :font "Fira Code Retina" :weight 'regular :height (cdr face)))) - ;; (set-face-attribute (car face) nil :font "Source Code Pro" :weight 'regular :height (cdr face))) - (set-face-attribute (car face) nil :font "Source Code Pro" :height (cdr face)))) + (set-face-attribute (car face) nil :font "Overpass Nerd Font" :weight 'regular :height (cdr face))) - ;; Ensure that anything that should be fixed-pitch in Org files appears that way - ;; (set-face-attribute 'org-block nil :foreground nil :inherit 'fixed-pitch) - ;; (set-face-attribute 'org-code nil :inherit '(shadow fixed-pitch)) - ;; (set-face-attribute 'org-table nil :inherit '(shadow fixed-pitch)) - ;; (set-face-attribute 'org-verbatim nil :inherit '(shadow fixed-pitch)) - ;; (set-face-attribute 'org-special-keyword nil :inherit '(font-lock-comment-face fixed-pitch)) - ;; (set-face-attribute 'org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch)) - ;; (set-face-attribute 'org-checkbox nil :inherit 'fixed-pitch)) + ;; Ensure that anything that should be fixed-pitch in Org files appears that way + (set-face-attribute 'org-block nil :foreground nil :inherit 'fixed-pitch) + (set-face-attribute 'org-hide nil :inherit 'fixed-pitch) + (set-face-attribute 'org-code nil :inherit '(shadow fixed-pitch)) + (set-face-attribute 'org-table nil :inherit '(shadow fixed-pitch)) + (set-face-attribute 'org-verbatim nil :inherit '(shadow fixed-pitch)) + (set-face-attribute 'org-special-keyword nil :inherit '(font-lock-comment-face fixed-pitch)) + (set-face-attribute 'org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch)) + (set-face-attribute 'org-checkbox nil :inherit 'fixed-pitch) + ) +;; (set-face-attribute (car face) nil :font "Fira Code Retina" :weight 'regular :height (cdr face)))) +;; (set-face-attribute (car face) nil :font "Source Code Pro" :weight 'regular :height (cdr face))) +;; (set-face-attribute (car face) nil :font "SauceCodePro" :height (cdr face)))) -(defvar sbinfo/open-ai-key "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") +;; AI +(defun gptel-add-key () + "Read API key from file and ensure it's clean." + (string-trim + (with-temp-buffer + ;; (insert-file-contents "~/.secrets/claude_key") + (insert-file-contents "~/.secrets/opencode_key") + (buffer-string)))) (after! chatgpt-shell (setq chatgpt-shell-openai-key sbinfo/open-ai-key)) +(use-package! elfeed-summarize + ;; :ensure + :after elfeed + :init (require 'llm-openai) + :config + (setq elfeed-summarize-llm-provider + (make-llm-openai-compatible + :key (gptel-add-key) + :url "https://opencode.ai/zen/v1/" + :chat-model "nemotron-3-ultra-free")) + (elfeed-summarize-mode 1)) + (after! org-ai (add-hook 'org-mode-hook #'org-ai-mode) (org-ai-install-yasnippets) @@ -388,16 +462,35 @@ (setq org-ai-openai-api-token sbinfo/open-ai-key)) (use-package! gptel - :config - (setq! gptel-api-key sbinfo/open-ai-key)) + :custom + ;;(setq gptel-model "claude-3-7-sonnet-20250219") + (setq gptel-model "claude-sonnet-4-6") + :config (gptel-add-key) + + (setq gptel-backend + (gptel-make-anthropic "Claude" + :stream t + :key #'gptel-add-key))) + +;; (use-package! gptel +;; :config +;; ) + +(use-package! org-web-tools + :commands org-web-tools--url-as-readable-org) (after! org + (add-hook! org-mode :append + #'flymake-vale-load) + + (add-hook 'org-mode-hook #'sbinfo/org-mode-setup) (setq org-directory "~/org/" - org-agenda-files '("agenda.org" "goals_2024.org" "job_search_2022.org") + org-agenda-files '("agenda.org" "goals_2026.org" "Pillbox.org") org-agenda-start-with-follow-mode t org-default-notes-file (expand-file-name "notes.org" org-directory) org-ellipsis " ▼ " org-bullets-bullet-list '("◉" "○" "●" "○" "●" "○" "●") + org-adapt-indentation t org-indent-indentation-per-level 4 org-log-done 'time org-roam-directory "~/org/roam/" @@ -409,46 +502,109 @@ ;; ex. of org-link-abbrev-alist in action ;; [[arch-wiki:Name_of_Page][Description]] org-link-abbrev-alist ; This overwrites the default Doom org-link-abbrev-list - '(("ddg" . "https://duckduckgo.com/?q=") - ("wiki" . "https://en.wikipedia.org/wiki/")) + '(("ddg" . "https://duckduckgo.com/?q=") + ("wiki" . "https://en.wikipedia.org/wiki/")) org-todo-keywords ; This overwrites the default Doom org-todo-keywords - '((sequence - "TODO(t)" ; A task that is ready to be tackled - "BLOG(b)" ; Blog writing assignments - "PROJ(p)" ; A project that contains other tasks - "WAIT(w)" ; Something is holding up this task - "|" ; The pipe necessary to separate "active" states and "inactive" states - "DONE(d)" ; Task has been completed - "CANCELLED(c)" )) ; Task has been cancelled + '((sequence + "TODO(t)" ; A task that is ready to be tackled + "BLOG(b)" ; Blog writing assignments + "PROJ(p)" ; A project that contains other tasks + "WAIT(w)" ; Something is holding up this task + "|" ; The pipe necessary to separate "active" states and "inactive" states + "DONE(d)" ; Task has been completed + "CANCELLED(c)" )) ; Task has been cancelled org-capture-templates '(("b" "Bookmark" entry (file "~/org/bookmarksv2.org") "* %^{title}\n#+date: %U\n- URL: %^{link}\n- Context: %?") - ("t" "Personal todo" entry - (file+headline "~/org/agenda.org" "Inbox") - "* [ ] %?\n%i\n%a") - ("n" "Personal notes" entry + ("c" "Coral Lane To-Do" checkitem + (file+headline "~/org/coral_lane_activities.org" "To Dos") + " [ ] %?\n") + ("h" "Templates for hockey games") + ("hp" "Playoff Game" entry + (file "~/org/dalvsminrd12026.org") + (file "~/org/templates/playoffcapture.orgtmpl") :unnarrowed t) + ("hr" "Regular Game" entry + (file+headline "~/org/dallasstars20262027.org" "Dallas Stars 2026-2027 Season") + (file "~/org/templates/regular.orgtmpl") :unnarrowed t) + ("i" "IT Request" checkitem + (file+headline "~/org/notes.org" "Future IT Requests") + " [ ] %?\n") + ("n" "Personal Notes") + ("nn" "Captured notes" entry (file+headline "~/org/notes.org" "Inbox") - "* %u %?\n%i\n%a" :prepend t))) - ;;("j" "Journal" entry - ;;(file+olp+datetree +org-capture-journal-file) - ;;"* %U %?\n%i\n%a" :prepend t))) + "* %u %?\n%i\n%a" :prepend t) + ("np" "Note via org-protocol" entry + (file+headline "~/org/notes.org" "Web Clipper") + (file "~/org/templates/webclipper.orgtmpl") :immediate-finish nil) + ("s" "Shopping Item" checkitem + (file+headline "~/org/Shopping.org" "Needs") + " [ ] %?\n") + ("t" "Personal todo" entry + (file "~/org/agenda.org") + "* TODO %?\n%i\n")) + org-roam-capture-ref-templates + '(("w" "ref" plain "%(org-web-tools--url-as-readable-org \"${ref}\")" + :target (file+head "clips/${slug}.org" "#+title: ${title}\n") + :unnarrowed t)) + ) + + ;;("j" "Journal" entry + ;;(file+olp+datetree +org-capture-journal-file) + ;;"* %U %?\n%i\n%a" :prepend t))) (sbinfo/org-font-setup)) +(setq org-agenda-custom-commands + '(("d" "Today's Agenda" + ( + (tags-todo "+afterfocus+SCHEDULED<\"\"" + ((org-agenda-overriding-header "After Focus Items"))) + (agenda "" ((org-agenda-span 3) + (org-agenda-start-day "-1d"))))) + ("p" "Pills Against the Ageless Ills" ( + (agenda "" ((org-agenda-files '("/home/shawnb/org/Pillbox.org")) + (org-agenda-entry-types '(:scheduled)) + (org-scheduled-past-days 0) + (org-agenda-overriding-header "Pills Against the Ageles Ills"))))))) + +(use-package! org-modern + :hook (org-mode . org-modern-mode) + :config + (setq org-modern-star '("◉" "○" "✸" "✿" "✤" "✜" "◆" "▶") + org-modern-table-vertical 1 + org-modern-table-horizontal 0.2 + org-modern-list '((43 . "➤") + (45 . "–") + (42 . "•")) + org-modern-todo-faces + '(("TODO" :inverse-video t :inherit org-todo) + ("BLOG" :inverse-video t :inherit +org-todo-project) + ("PROJ" :inverse-video t :inherit +org-todo-project) + ("WAIT" :inverse-video t :inherit +org-todo-onhold) + ("CANCELLED" :inverse-video t :inherit +org-todo-cancel)) + org-modern-footnote + (cons nil (cadr org-script-display)) + org-modern-block-fringe nil + org-modern-progress nil + org-modern-priority nil + org-modern-horizontal-rule (make-string 36 ?─) + ) + (custom-set-faces! '(org-modern-statistics :inherit org-checkbox-statistics-todo))) + (use-package! websocket - :after org-roam) + :after org-roam) (use-package! org-roam-ui - :after org -;; normally we'd recommend hooking orui after org-roam, but since org-roam does not have -;; a hookable mode anymore, you're advised to pick something yourself -;; if you don't care about startup time, use -;; :hook (after-init . org-roam-ui-mode) - :config - (setq org-roam-ui-sync-theme t - org-roam-ui-follow t - org-roam-ui-update-on-save t - org-roam-ui-open-on-start t)) + :after org + ;; normally we'd recommend hooking orui after org-roam, but since org-roam does not have + ;; a hookable mode anymore, you're advised to pick something yourself + ;; if you don't care about startup time, use + ;; :hook (after-init . org-roam-ui-mode) + :config + (setq org-roam-ui-sync-theme t + org-roam-ui-follow t + org-roam-ui-update-on-save t + org-roam-ui-open-on-start t)) (defun sbinfo/split-and-indirect-orgtree () "Splilts window to the right and opens an org tree section in it." @@ -479,6 +635,12 @@ ;; Todoist (setq todoist-token "344745a4ca6497cbce38cc957cad0c59c5d91613") +;; Vale +(defvar my-flymake-vale-executable "/home/shawnb/.nix-profile/bin/vale") +(use-package! flymake-vale + :config + (setq flymake-vale-program my-flymake-vale-executable)) + ;; Auto create directories recursively ;; See https://emacsredux.com/blog/2022/06/12/auto-create-missing-directories/ (defun er-auto-create-missing-dirs () @@ -486,7 +648,96 @@ (unless (file-exists-p target-dir) (make-directory target-dir t)))) +;; Run du -hc on a directory in Dired +(defun sbinfo/dired-du () + "Run 'du -hc' on the directory under the cursor in Dired." + (interactive) + (let ((current-dir (dired-get-file-for-visit))) + (if (file-directory-p current-dir) + (dired-do-async-shell-command "du -hc" nil (list current-dir)) + (message "The current point is not a directory.")))) + (add-to-list 'find-file-not-found-functions #'er-auto-create-missing-dirs) ;; ACe Window (global-set-key (kbd "M-o") 'ace-window) (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)) +;; nov +(use-package! nov + :config + (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode)) + ) + +(after! ledger-mode + (setq ledger-binary-path "hledger.sh") + (setq ledger-default-date-format "%Y-%m-%d")) + +;; ================ +;; Helper functions +;; ================ +(defun my/org-hugo-link-vanilla-markdown (link desc info) + "Render org links as vanilla Markdown links instead of Hugo relref." + (let* ((type (org-element-property :type link)) + (raw-path (org-element-property :path link)) + (raw-link (org-element-property :raw-link link))) + (cond + ;; External links - plain markdown + ((member type '("http" "https" "ftp")) + (format "[%s](%s)" (or desc raw-link) raw-link)) + ;; ID links - resolve org-id to file slug + ((string= type "id") + (let* ((id-location (org-id-find raw-path)) + (file (when id-location (car id-location))) + (slug (if file + (file-name-sans-extension (file-name-nondirectory file)) + raw-path))) + (format "[%s](%s)" (or desc slug) slug))) + ;; Fuzzy/internal links - convert to anchor links + ((string= type "fuzzy") + (format "[%s](#%s)" (or desc raw-path) + (org-hugo-slug raw-path))) + ;; Default: use original behavior for other link types + (t nil)))) + +(defun my/org-hugo-link-advice (orig-fun link desc info) + "Advice to use vanilla Markdown links." + (or (my/org-hugo-link-vanilla-markdown link desc info) + (funcall orig-fun link desc info))) + +(advice-add 'org-hugo-link :around #'my/org-hugo-link-advice) + +;; (advice-remove 'org-hugo-link #'my/org-hugo-link-advice) + +;; https://jamesendreshowell.com/2026-04-04-my-first-advice-in-emacs-lisp.html +(defun sbinfo/file-to-string (file) + "Return a string that is the contents of FILE." + (with-temp-buffer + (insert-file-contents file) + (buffer-string))) + +;; Keeping count of pills/puffs in the Pillbox. Designed to mark a Pillbox task as CANCELLED +;; if the supply runs out. +;; Thanks, Claude +(defun sbinfo/decrement-org-property (property) + "Decrement PROPERTY on current heading by 1, clamping at zero. +When the value reaches zero, mark the task as CANCELLED." + (let* ((val (org-entry-get nil property)) + (num (and val (string-to-number val)))) + (when num + (cond + ((> num 1) + (org-entry-put nil property (number-to-string (1- num)))) + ((= num 1) + (org-entry-put nil property "0") + (org-todo "CANCELLED")) ;; Already at zero — do nothing + )))) + +(defun sbinfo/on-recurring-task-done () + "Decrement :REMAINING: when a recurring task is completed." + (when (and (string= org-state "DONE") + (org-entry-get nil "REMAINING")) + (sbinfo/decrement-org-property "REMAINING"))) + +(add-hook 'org-after-todo-state-change-hook #'sbinfo/on-recurring-task-done) +(defun sbinfo/org-capture-frame () + (set-frame-parameter nil 'name "org-capture") + (org-capture)) diff --git a/init.el b/init.el index 39e3448..72f4de3 100644 --- a/init.el +++ b/init.el @@ -21,17 +21,18 @@ ;;layout ; auie,ctsrnm is the superior home row :completion - company ; the ultimate code completion backend + ;;company ; the ultimate code completion backend + (corfu +orderless +dabbrev) ; complete with cap(f), cape and a flying feather! ;;helm ; the *other* search engine for love and life ;;ido ; the other *other* search engine... ;;ivy ; a search engine for love and life - vertico ; the search engine of the future + (vertico +icons) ; the search engine of the future :ui ;;deft ; notational velocity for Emacs doom ; what makes DOOM look the way it does doom-dashboard ; a nifty splash screen for Emacs - ;;doom-quit ; DOOM quit-message prompts when you quit Emacs + doom-quit ; DOOM quit-message prompts when you quit Emacs ;;(emoji +unicode) ; 🙂 hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW ;;hydra @@ -42,21 +43,21 @@ ;;nav-flash ; blink cursor line after big motions ;;neotree ; a project drawer, like NERDTree for vim ophints ; highlight the region an operation acts on - (popup +defaults) ; tame sudden yet inevitable temporary windows + (popup +all +defaults) ; tame sudden yet inevitable temporary windows ;;tabs ; a tab bar for Emacs ;;treemacs ; a project drawer, like neotree but cooler ;;unicode ; extended unicode support for various languages (vc-gutter +pretty) ; vcs diff in the fringe vi-tilde-fringe ; fringe tildes to mark beyond EOB - ;;window-select ; visually switch windows + window-select ; visually switch windows workspaces ; tab emulation, persistence & separate workspaces - ;;zen ; distraction-free coding or writing + zen ; distraction-free coding or writing :editor (evil +everywhere); come to the dark side, we have cookies file-templates ; auto-snippets for empty files fold ; (nigh) universal code folding - ;;(format +onsave) ; automated prettiness + (format +onsave) ; automated prettiness ;;god ; run Emacs commands without modifier keys ;;lispy ; vim for lisp, for people who don't like vim multiple-cursors ; editing in many places at once @@ -67,35 +68,35 @@ ;;word-wrap ; soft wrapping with language-aware indent :emacs - dired ; making dired pretty [functional] + (dired +icons) ; making dired pretty [functional] electric ; smarter, keyword-based electric-indent - ;;ibuffer ; interactive buffer management + (ibuffer +icons) ; interactive buffer management undo ; persistent, smarter undo for your inevitable mistakes vc ; version-control and Emacs, sitting in a tree :term - eshell ; the elisp shell that works everywhere + ;; eshell ; the elisp shell that works everywhere ;;shell ; simple shell REPL for Emacs ;;term ; basic terminal emulator for Emacs ;;vterm ; the best terminal emulation in Emacs :checkers syntax ; tasing you for every semicolon you forget - (spell +flyspell) ; tasing you for misspelling mispelling + (spell +aspell +flyspell) ; tasing you for misspelling mispelling ;;grammar ; tasing grammar mistake every you make :tools ;;ansible ;;biblio ; Writes a PhD for you (citation needed) ;;debugger ; FIXME stepping through code, to help you add bugs - ;;direnv + direnv ;;docker ;;editorconfig ; let someone else argue about tabs vs spaces ;;ein ; tame Jupyter notebooks with emacs (eval +overlay) ; run code, run (also, repls) ;;gist ; interacting with github gists - lookup ; navigate your code and its documentation - (lsp +eglot) ; M-x vscode + (lookup +dictionary) ; navigate your code and its documentation + lsp ; M-x vscode magit ; a git porcelain for Emacs ;;make ; run make tasks from Emacs ;;pass ; password manager for nerds @@ -103,9 +104,9 @@ ;;prodigy ; FIXME managing external services & code builders ;;rgb ; creating color strings ;;taskrunner ; taskrunner for all your projects - ;;terraform ; infrastructure as code + terraform ; infrastructure as code ;;tmux ; an API for interacting with tmux - ;;tree-sitter ; syntax and parsing, sitting in a tree... + tree-sitter ; syntax and parsing, sitting in a tree... ;;upload ; map local to remote projects via ssh/ftp :os @@ -147,20 +148,25 @@ ;;kotlin ; a better, slicker Java(Script) ;;latex ; writing papers in Emacs has never been so fun ;;lean ; for folks with too much to prove - ;;ledger ; be audit you can be + ledger ; be audit you can be ;;lua ; one-based indices? one-based indices markdown ; writing docs for people to ignore ;;nim ; python + lisp at the speed of c - nix ; I hereby declare "nix geht mehr!" + (nix +lsp) ; I hereby declare "nix geht mehr!" ;;ocaml ; an objective camel (org +pretty +hugo + +noter + +pandoc +roam2) ; organize your plain life in plain text ;;php ; perl's insecure younger brother ;;plantuml ; diagrams for confusing people more ;;purescript ; javascript, but functional - (python +lsp +pyright) ; beautiful is better than ugly + (python + +lsp + +pyright + +tree-sitter) ; beautiful is better than ugly ;;qt ; the 'cutest' gui framework ever ;;racket ; a DSL for DSLs ;;raku ; the artist formerly known as perl6 @@ -176,7 +182,9 @@ ;;swift ; who asked for emoji variables? ;;terra ; Earth and Moon in alignment for performance. ;;web ; the tubes - yaml ; JSON, but readable + (yaml + +lsp + +tree-sitter) ; JSON, but readable ;;zig ; C, but simpler :email @@ -187,7 +195,7 @@ :app ;;calendar ;;emms - ;;everywhere ; *leave* Emacs!? You must be joking + everywhere ; *leave* Emacs!? You must be joking ;;irc ; how neckbeards socialize rss ; emacs as an RSS reader ;;twitter ; twitter client https://twitter.com/vnought diff --git a/packages.el b/packages.el index 0e7e722..599ba82 100644 --- a/packages.el +++ b/packages.el @@ -7,74 +7,86 @@ ;; To install SOME-PACKAGE from MELPA, ELPA or emacsmirror: -;(package! some-package) + ;(package! some-package) ;; To install a package directly from a remote git repo, you must specify a ;; `:recipe'. You'll find documentation on what `:recipe' accepts here: ;; https://github.com/raxod502/straight.el#the-recipe-format -;(package! another-package -; :recipe (:host github :repo "username/repo")) + ;(package! another-package + ; :recipe (:host github :repo "username/repo")) ;; If the package you are trying to install does not contain a PACKAGENAME.el ;; file, or is located in a subdirectory of the repo, you'll need to specify ;; `:files' in the `:recipe': -;(package! this-package -; :recipe (:host github :repo "username/repo" -; :files ("some-file.el" "src/lisp/*.el"))) + ;(package! this-package + ; :recipe (:host github :repo "username/repo" + ; :files ("some-file.el" "src/lisp/*.el"))) ;; If you'd like to disable a package included with Doom, you can do so here ;; with the `:disable' property: -;(package! builtin-package :disable t) + ;(package! builtin-package :disable t) ;; You can override the recipe of a built in package without having to specify ;; all the properties for `:recipe'. These will inherit the rest of its recipe ;; from Doom or MELPA/ELPA/Emacsmirror: -;(package! builtin-package :recipe (:nonrecursive t)) -;(package! builtin-package-2 :recipe (:repo "myfork/package")) + ;(package! builtin-package :recipe (:nonrecursive t)) + ;(package! builtin-package-2 :recipe (:repo "myfork/package")) ;; Specify a `:branch' to install a package from a particular branch or tag. ;; This is required for some packages whose default branch isn't 'master' (which ;; our package manager can't deal with; see raxod502/straight.el#279) -;(package! builtin-package :recipe (:branch "develop")) + ;(package! builtin-package :recipe (:branch "develop")) ;; Use `:pin' to specify a particular commit to install. -;(package! builtin-package :pin "1a2b3c4d5e") + ;(package! builtin-package :pin "1a2b3c4d5e") ;; Doom's packages are pinned to a specific commit and updated from release to ;; release. The `unpin!' macro allows you to unpin single packages... -;(unpin! pinned-package) + ;(unpin! pinned-package) ;; ...or multiple packages -;(unpin! pinned-package another-pinned-package) + ;(unpin! pinned-package another-pinned-package) ;; ...Or *all* packages (NOT RECOMMENDED; will likely break things) -;(unpin! t) + ;(unpin! t) (unpin! org-roam) (unpin! straight) (package! all-the-icons-dired) (package! chatgpt-shell :recipe (:host github - :repo "xenodium/chatgpt-shell")) + :repo "xenodium/chatgpt-shell")) (package! counsel-osx-app) (package! doom-nano-modeline :recipe (:host github - :repo "ronisbr/doom-nano-modeline")) + :repo "ronisbr/doom-nano-modeline")) (package! elfeed) (package! elfeed-goodies) ;;(package! elfeed-org) (package! elfeed-protocol) +(package! elfeed-summarize + :recipe (:host github + :repo "fritzgrabo/elfeed-summarize")) (package! ement) +(package! flymake-vale + :recipe (:host github + :repo "tpeacock19/flymake-vale")) (package! gptel) +(package! nov) (package! org-ai) (package! org-bullets) (package! org-roam-ui) (package! org-similarity :recipe (:host github - :repo "brunoarine/org-similarity" - :branch "main")) + :repo "brunoarine/org-similarity" + :branch "main")) (package! org-web-tools :pin "8f8b5c6") -(package! pocket-reader :pin "5354082") +(package! pocket-reader :pin "7f55668") +;; (package! org-web-tools) +;; (package! pocket-reader) (package! todoist) (package! twittering-mode) (package! wc-mode) +(package! wombag + :recipe (:host github + :repo "karthink/wombag")) (package! writegood-mode) -(package! wordsmith-mode) +;; (package! wordsmith-mode)