summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcashmere <cashmere@autistici.org>2026-07-30 03:27:04 +0200
committercashmere <cashmere@autistici.org>2026-07-30 03:34:29 +0200
commit33369841f5e30f8adf68047798fa36b003ce558b (patch)
tree129f0c03e9e9059f922eb573967612038010ce7f
parentc8d731021d78bbcb975bef22451c66337aec8b30 (diff)
downloademacs-33369841f5e30f8adf68047798fa36b003ce558b.tar.gz
md: 2026-07-30T03:27:08+02:00
-rw-r--r--config.el567
-rw-r--r--config.org128
2 files changed, 344 insertions, 351 deletions
diff --git a/config.el b/config.el
index c2ad92d..1fc7b78 100644
--- a/config.el
+++ b/config.el
@@ -114,8 +114,8 @@ Set per-host in the gitignored `local.el'.")
(scroll-bar-mode -1)
(add-to-list 'default-frame-alist '(vertical-scroll-bars . nil))
(add-to-list 'default-frame-alist '(horizontal-scroll-bars . nil))
- (set-frame-parameter (selected-frame) 'alpha-background 90)
- (add-to-list 'default-frame-alist '(alpha-background . 90))
+ ;; (set-frame-parameter (selected-frame) 'alpha-background 90)
+ ;; (add-to-list 'default-frame-alist '(alpha-background . 90))
(global-hl-line-mode 1) ;; Highlight the current line
(add-hook 'org-mode-hook (lambda () (setq-local global-hl-line-mode nil))) ;; hl-line repaints wipe kitty-graphics scaled headings
(add-hook 'markdown-mode-hook (lambda () (setq-local global-hl-line-mode nil))) ;; same conflict for kitty-graphics markdown headings
@@ -276,6 +276,26 @@ Selects the compilation window so the cursor lands in it."
(use-package async :ensure t)
+(use-package general
+ :ensure (:wait t)
+ :demand t
+ :config
+ (general-evil-setup))
+
+(eval-and-compile
+ (require 'general)
+
+ (general-create-definer my-leader
+ :states '(normal visual insert)
+ :keymaps 'override
+ :prefix "SPC"
+ :non-normal-prefix "M-SPC")
+
+ (general-create-definer my-local-leader
+ :states '(normal visual)
+ :keymaps 'override
+ :prefix ","))
+
;; Always-available IRC entry points so `M-x run-irc` works from any daemon
;; (hub, work, standalone). Calling `erc-tls` autoloads ERC on demand.
;; Soju routes to the upstream network via the USER string, so we still need
@@ -454,11 +474,6 @@ Temporarily disables notifications during the fetch."
(add-hook 'erc-join-hook hook-fn))
(run-irc))))
-;; ERC keybindings and hooks — register from any daemon, since `run-irc`
-;; autoloads ERC even outside the hub daemon.
-;; `general' is required eagerly (not via with-eval-after-load) so the
-;; `general-def' macro is expanded when config.el is compiled; inside a
-;; deferred lambda it would survive as a function call and fail at runtime.
(with-eval-after-load 'erc
(add-hook 'erc-mode-hook
(lambda ()
@@ -529,11 +544,20 @@ Temporarily disables notifications during the fetch."
(memq this-command '(evil-yank evil-yank-line)))
(my/send-to-clipboard (car kill-ring))))
(advice-add 'evil-yank :after #'my/evil-yank-to-clipboard)
- ;; elfeed-search-yank bypasses evil-yank (uses gui-set-selection directly),
- ;; so route it through the same clipboard path — needed in TTY frames.
(defun my/elfeed-yank-to-clipboard (&rest _)
(my/send-to-clipboard (car kill-ring)))
- (advice-add 'elfeed-search-yank :after #'my/elfeed-yank-to-clipboard))
+ (advice-add 'elfeed-search-yank :after #'my/elfeed-yank-to-clipboard)
+ (advice-add 'link-hint-copy-link :after #'my/elfeed-yank-to-clipboard)
+ (with-eval-after-load 'pass
+ (dolist (cmd '(pass-copy
+ pass-copy-field
+ pass-copy-username
+ pass-copy-url
+ pass-otp-token-copy
+ pass-otp-uri-copy
+ pass-view-copy-password
+ pass-view-copy-token))
+ (advice-add cmd :after #'my/elfeed-yank-to-clipboard))))
(use-package isearch
:ensure nil ;; This is built-in, no need to fetch it.
@@ -694,23 +718,6 @@ Temporarily disables notifications during the fetch."
("C-c ^ n" . smerge-next) ;; Move to the next conflict.
("C-c ^ p" . smerge-previous))) ;; Move to the previous conflict.
-(use-package general
- :ensure (:wait t)
- :demand t
- :config
- (general-evil-setup)
-
- (general-create-definer my-leader
- :states '(normal visual insert)
- :keymaps 'override
- :prefix "SPC"
- :non-normal-prefix "M-SPC")
-
- (general-create-definer my-local-leader
- :states '(normal visual)
- :keymaps 'override
- :prefix ","))
-
(use-package markdown-mode
:ensure t
:hook (markdown-mode . nb/markdown-unhighlight)
@@ -759,105 +766,6 @@ Temporarily disables notifications during the fetch."
(markdown-header-face-5 ((t (:height 1.1 :foreground "#b48ead" :weight bold :inherit markdown-header-face))))
(markdown-header-face-6 ((t (:height 1.05 :foreground "#5e81ac" :weight semi-bold :inherit markdown-header-face)))))
-(use-package claude-code-ide
- :ensure (:host github :repo "manzaltu/claude-code-ide.el"
- :files ("*.el"))
- :defer t
- :commands (claude-code-ide
- claude-code-ide-menu
- claude-code-ide-toggle
- claude-code-ide-toggle-recent
- claude-code-ide-continue
- claude-code-ide-resume
- claude-code-ide-stop
- claude-code-ide-send-prompt
- claude-code-ide-insert-at-mentioned
- claude-code-ide-switch-to-buffer
- claude-code-ide-list-sessions
- claude-code-ide-check-status
- claude-code-ide-show-debug
- claude-code-ide-clear-debug)
- :custom
- ;; CLI
- (claude-code-ide-cli-path "claude")
- (claude-code-ide-cli-debug nil)
- ;; Terminal backend
- (claude-code-ide-terminal-backend 'ghostel)
-
- ;; Window placement — mirror eca on the right side so the two AI assistants
- ;; don't fight over the same slot
- (claude-code-ide-use-side-window t)
- (claude-code-ide-window-side 'right)
- (claude-code-ide-window-width 75)
- (claude-code-ide-focus-on-open t)
-
- ;; Ediff integration for code suggestions
- (claude-code-ide-use-ide-diff t)
- (claude-code-ide-focus-claude-after-ediff t)
- (claude-code-ide-show-claude-window-in-ediff t)
- (claude-code-ide-switch-tab-on-ediff t)
-
- ;; Diagnostics — auto-pick flycheck/flymake
- (claude-code-ide-diagnostics-backend 'auto)
-
- ;; Allow Claude to evaluate Elisp in this Emacs (executeCode MCP tool)
- (claude-code-ide-enable-execute-code t)
-
- :init
- ;; Evil: open the Claude terminal in insert state so you can type immediately.
- ;; Defined in :init so it applies even before claude-code-ide loads.
- (with-eval-after-load 'evil
- (evil-set-initial-state 'eat-mode 'insert))
-
- (defun my/claude-code-ide--enter-insert (&rest _)
- "Force evil insert state in the Claude terminal buffer after invocation."
- (when (bound-and-true-p evil-mode)
- (run-at-time
- 0.05 nil
- (lambda ()
- (when-let* ((buf (seq-find
- (lambda (b)
- (string-match-p "\\*claude-code"
- (buffer-name b)))
- (buffer-list))))
- (with-current-buffer buf
- (when (get-buffer-window buf)
- (select-window (get-buffer-window buf)))
- (evil-insert-state)))))))
-
- (dolist (cmd '(claude-code-ide
- claude-code-ide-toggle
- claude-code-ide-toggle-recent
- claude-code-ide-continue
- claude-code-ide-resume
- claude-code-ide-switch-to-buffer))
- (advice-add cmd :after #'my/claude-code-ide--enter-insert))
-
- ;; Leader-key bindings (SPC a …) — defined pre-load so :commands autoloads fire.
- (with-eval-after-load 'general
- ;; 2026-06-23: replaced claude-code-ide bindings with pi-coding-agent under `a'.
- ;; pi-coding-agent only exposes 3 user-facing commands, so the prefix is intentionally sparse.
- (my-leader
- "aa" '(agent-shell-sidebar-toggle :wk "start")
- "at" '(agent-shell-toggle :wk "toggle")
- "am" '(agent-shell-help-menu :wk "open session")
- "ar" '(agent-shell-send-region :wk "send region")))
-
- :config
- ;; Runs only on first invocation — not at Emacs startup.
- (claude-code-ide-emacs-tools-setup))
-
-(use-package eca
- :ensure t
- :config
- (setopt evil-collection-eca-maps 't))
-
-(use-package pi-coding-agent
- :ensure t
- :commands (pi-coding-agent
- pi-coding-agent-toggle
- pi-coding-agent-open-session-file))
-
(defun my/agent-shell-autoscroll (&rest _)
(dolist (win (get-buffer-window-list (current-buffer) 'visible))
(when (>= (window-point win)
@@ -937,7 +845,7 @@ Temporarily disables notifications during the fetch."
(use-package agent-shell-sidebar
:after agent-shell
- :ensure (:host github :repo "cmacrae/agent-shell-sidebar"))
+ :ensure (:host nil :repo "ssh://git@git.cashmere.rs/agent-shell-sidebar.git"))
(use-package eldoc
:ensure nil
@@ -961,50 +869,6 @@ Temporarily disables notifications during the fetch."
'("~/.emacs.d/config.el"
"~/.emacs.d/init.el"))
-(use-package flycheck
- :ensure t
- :config
- (setq flycheck-display-errors-function nil)
- (setq flycheck-global-modes
- '(not ghostel-mode vterm-mode term-mode shell-mode eshell-mode eat-mode))
- (add-hook 'after-init-hook #'global-flycheck-mode))
-
-(use-package flycheck-rust
- :ensure t
- :config
- (setq flycheck-rust-check-tests nil)
- (add-hook 'rust-ts-mode-hook #'flycheck-rust-setup))
-
-(use-package flycheck-python-ruff
- :ensure (:host github :repo "v4n6/flycheck-python-ruff")
- :hook (python-ts-mode . flycheck-python-ruff-setup))
-
-(use-package flycheck-eglot
- :ensure t
- :after (flycheck eglot)
- :config
- (global-flycheck-eglot-mode 1))
-
-(use-package sideline
- :ensure t
- :hook (flycheck-mode . sideline-mode)
- :config
- (setq sideline-delay 0.001)
- :custom
- (sideline-backends-right '(sideline-flycheck))
- (sideline-display-backend-name t))
-
-(use-package sideline-flycheck
- :ensure t
- :hook (flycheck-mode . sideline-flycheck-setup))
-
-;; (use-package flycheck-vale
-;; :ensure t
-;; :after flycheck
-;; :demand t
-;; :config
-;; (flycheck-vale-setup))
-
(use-package xref
:ensure nil)
@@ -1100,15 +964,16 @@ Temporarily disables notifications during the fetch."
:ensure (:host github :repo "cashmeredev/kitty-graphics.el")
:demand t
:custom
- (kitty-gfx-enable-video t)
- (kitty-gfx-shr-scale 'fit)
- (kitty-gfx-shr-fit-width 0.4)
- (kitty-gfx-shr-fit-height 20)
- :hook (dired-mode . kitty-gfx-dired-auto-preview-mode)
+ (kitty-graphics-enable-video t)
+ (kitty-graphics-shr-scale 'fit)
+ (kitty-graphics-shr-fit-width 0.4)
+ (kitty-graphics-shr-fit-height 20)
+ (kitty-graphics-doc-view-resolution-scale 2.0)
+ :hook (dired-mode . kitty-graphics-dired-auto-preview-mode)
:config
- (setq kitty-gfx-enable-browser t
- kitty-gfx-casty-program "~/projects/casty/bin/casty.js"
- kitty-gfx-casty-chrome "helium-browser")
+ (setq kitty-graphics-enable-browser t
+ kitty-graphics-casty-program "~/projects/casty/bin/casty.js"
+ kitty-graphics-casty-chrome "helium-browser")
(kitty-graphics-setup))
@@ -1586,9 +1451,12 @@ Skips capture tasks and projects."
(:name "Schedule"
:time-grid t
:order 1)
+ (:name "Scheduled today"
+ :scheduled today
+ :order 2)
(:name "Deadlines"
:deadline today
- :order 2)
+ :order 3)
(:discard (:anything t))))))
;; Block 2: active + next tasks (what you can work on right now)
@@ -1623,6 +1491,9 @@ Skips capture tasks and projects."
(:name "Schedule"
:time-grid t
:order 1)
+ (:name "Scheduled today"
+ :scheduled today
+ :order 2)
(:discard (:anything t))))))
;; Block 2: next 6 days
@@ -1691,12 +1562,37 @@ Skips capture tasks and projects."
(:name "Schedule"
:time-grid t
:order 1)
+ (:name "Scheduled today"
+ :scheduled today
+ :order 2)
(:name "Deadlines"
:deadline today
- :order 2)
+ :order 3)
(:discard (:anything t))))))
- ;; Block 2: project
+ ;; Block 2: next 6 days
+ (agenda ""
+ ((org-agenda-start-on-weekday nil)
+ (org-agenda-start-day "+1d")
+ (org-agenda-span 6)
+ (org-deadline-warning-days 0)
+ (org-agenda-overriding-header "Next 6 Days")
+ (org-super-agenda-groups
+ '((:anything t)))))
+
+ ;; Block 3: upcoming deadlines beyond this week
+ (agenda ""
+ ((org-agenda-time-grid nil)
+ (org-agenda-start-on-weekday nil)
+ (org-agenda-start-day "+7d")
+ (org-agenda-span 14)
+ (org-agenda-show-all-dates nil)
+ (org-agenda-entry-types '(:deadline))
+ (org-agenda-overriding-header "Upcoming Deadlines (+14d)")
+ (org-super-agenda-groups
+ '((:anything t)))))
+
+ ;; Block 4: project
(org-ql-block '(and (tags "project")
(not (done)))
((org-ql-block-header "Project")
@@ -1707,7 +1603,7 @@ Skips capture tasks and projects."
(:name "WAIT" :todo "WAIT" :order 4)
(:discard (:anything t))))))
- ;; Block 3: development
+ ;; Block 5: development
(org-ql-block '(and (tags "development")
(not (done)))
((org-ql-block-header "Development")
@@ -1718,7 +1614,7 @@ Skips capture tasks and projects."
(:name "WAIT" :todo "WAIT" :order 4)
(:discard (:anything t))))))
- ;; Block 4: blog
+ ;; Block 6: blog
(org-ql-block '(and (tags "blog")
(not (done)))
((org-ql-block-header "Blog")
@@ -1729,7 +1625,7 @@ Skips capture tasks and projects."
(:name "WAIT" :todo "WAIT" :order 4)
(:discard (:anything t))))))
- ;; Block 5: self
+ ;; Block 7: self
(org-ql-block '(and (tags "self")
(not (done)))
((org-ql-block-header "Self")
@@ -1740,7 +1636,7 @@ Skips capture tasks and projects."
(:name "WAIT" :todo "WAIT" :order 4)
(:discard (:anything t))))))
- ;; Block 6: backlog — everything not covered above
+ ;; Block 8: backlog — everything not covered above
(org-ql-block '(and (todo)
(not (done))
(not (tags "project" "development" "blog" "self")))
@@ -1909,6 +1805,23 @@ Skips capture tasks and projects."
"\\`\\*Native-compile-Log\\*\\'"
"\\`\\*Async-native-compile-log\\*\\'")))
+(use-package xdg-launcher
+ :ensure (:host github :repo "emacs-exwm/xdg-launcher")
+ :commands (xdg-launcher-run-app)
+ :config
+ (with-eval-after-load 'consult
+ (push 'xdg-launcher-consult-source
+ (cdr (memq 'consult-source-buffer consult-buffer-sources)))))
+
+(defun my/app-launcher ()
+ "Launch an XDG application.
+In the dedicated floating \"emacs-launcher\" frame, close the frame
+after the app was launched."
+ (interactive)
+ (xdg-launcher-run-app)
+ (when (string= (frame-parameter nil 'name) "emacs-launcher")
+ (delete-frame)))
+
(use-package denote
:ensure (:wait t)
:hook (dired-mode . denote-dired-mode)
@@ -1925,6 +1838,24 @@ Skips capture tasks and projects."
(setq denote-agenda-include-regexp "_agenda")
(denote-agenda-insinuate))
+(defun my/denote-toggle-agenda-keyword ()
+ "Toggle the \"agenda\" keyword on the current Denote note.
+Renames the file and rewrites its front matter, saving the buffer."
+ (interactive)
+ (unless (and buffer-file-name (denote-file-is-note-p buffer-file-name))
+ (user-error "Current buffer is not a Denote note"))
+ (save-buffer)
+ (let* ((keywords (denote-extract-keywords-from-path buffer-file-name))
+ (denote-rename-confirmations nil)
+ (denote-save-buffers t)
+ (new-keywords (if (member "agenda" keywords)
+ (remove "agenda" keywords)
+ (cons "agenda" keywords))))
+ (denote-rename-file buffer-file-name 'keep-current new-keywords
+ 'keep-current 'keep-current 'keep-current)
+ (message "%s the agenda"
+ (if (member "agenda" new-keywords) "Added to" "Removed from"))))
+
(use-package denote-journal
:ensure t
:config
@@ -2038,35 +1969,13 @@ Works on the base filename (without extension), e.g. matches \"-agenda\", \":age
(add-hook 'org-after-todo-state-change-hook #'my/denote-remove-agenda-filetag-and-silent-rename)
(use-package denote-menu
- :ensure t
- :defer t
- :config
- (setq denote-menu-title-column-width 60)
-
- (defun denote-menu-export-to-dired ()
- "Switch to a plain Dired buffer listing the filtered *Denotes* files.
-Bypasses dirvish's `dired-noselect' advice: dirvish keys session
-buffers by directory only, so it would reuse the existing buffer
-for `denote-directory' and silently drop the file-list filter."
- (interactive)
- (if-let* ((paths (denote-menu--entries-to-paths))
- (default-directory (denote-directory)))
- (pop-to-buffer-same-window
- (unwind-protect
- (progn
- (advice-remove 'dired-noselect 'dirvish-dired-noselect-a)
- (dired-noselect (cons default-directory paths)))
- (when (bound-and-true-p dirvish-override-dired-mode)
- (advice-add 'dired-noselect :around 'dirvish-dired-noselect-a))))
- (user-error "No files to export"))))
+ :ensure nil
+ :after denote
+ :commands (denote-menu))
(use-package denote-org
:ensure t)
-(use-package consult-denote
- :ensure (:host github :repo "protesilaos/consult-denote")
- :defer t)
-
(defun my/convert-all-denote-links-in-directory (directory)
"Converts all denote: links to file: links in a specific directory."
(interactive "DDirectory: ")
@@ -2312,8 +2221,15 @@ completion-category-overrides '((file (styles partial-completion))))) ;; Customi
:ensure t
:hook
(after-init . marginalia-mode)
+ :custom
+ (marginalia-align 'right)
:config
- (setq marginalia-align 'center))
+ (set-face-attribute 'marginalia-documentation nil
+ :inherit nil
+ :slant 'italic
+ :foreground "grey43")
+ (set-face-attribute 'marginalia-key nil
+ :foreground "#00ff5f"))
(use-package eglot
:ensure nil
@@ -2729,7 +2645,8 @@ so the working-tree diff stays visible until the user explicitly stages."
:init
(smartparens-global-mode 1)
:config
- (require 'smartparens-config))
+ (require 'smartparens-config)
+ (sp-local-pair 'org-mode "~" nil :actions nil))
(use-package evil-smartparens
:ensure t
:after evil-collection
@@ -3016,13 +2933,7 @@ created later still get them."
(add-hook 'server-after-make-frame-hook #'cashmere/set-fonts)
(cashmere/set-fonts)
-(use-package grip-mode
- :ensure t
- :config
- (setq grip-command 'auto)) ;; auto, grip, go-grip or mdopen
-;; (add-to-list 'custom-theme-load-path "~/.emacs.d/themes/")
-;; (load-theme 'tinty t)
(use-package dirvish
:ensure (:host github :repo "latiagertrutis/dirvish" :branch "main")
@@ -3051,35 +2962,25 @@ created later still get them."
(setq delete-by-moving-to-trash t)
(setq dired-mouse-drag-files t)
- ;; On TTY: drop dirvish's GUI-only media dispatchers (image/video/gif).
- ;; They call `create-image' + `image-size' on cached thumbnails →
- ;; "Window system frame should be used" on terminal frames. Bare
- ;; symbols here — `dirvish-*-dp' names only appear after
- ;; `dirvish--preview-dps-validate' interns them (dirvish.el:577).
- ;;
- ;; kitty-graphics ships its own dirvish dispatcher (`kitty-image',
- ;; registered by `kitty-gfx--install-dirvish' on `kitty-graphics-mode'
- ;; enable, see kitty-graphics.el:4040) that handles images + video
- ;; thumbnails via the Kitty protocol. It runs before this filter
- ;; thanks to being prepended; removing the GUI dispatchers is purely
- ;; a safety net for cases where `kitty-image' returns nil (backend
- ;; lost, mode disabled mid-flight).
- (unless (display-graphic-p)
- (with-eval-after-load 'dirvish
- (setq dirvish-preview-dispatchers
- (cl-remove-if (lambda (d)
- (memq d '(image gif video video-mtn)))
- dirvish-preview-dispatchers)))))
+ (define-advice dirvish--preview-dps-validate
+ (:around (fn &optional dps) tty-media-filter)
+ "Drop GUI-only media dispatchers when the session's frame is a TTY."
+ (let ((dps (if (display-graphic-p)
+ dps
+ (cl-remove-if (lambda (d)
+ (memq d '(image gif video video-mtn)))
+ (or dps dirvish-preview-dispatchers)))))
+ (funcall fn dps))))
(use-package zoxide
- :ensure t)
-
-(use-package dired-rsync
- :ensure t)
-
-(use-package dired-rsync-transient
:ensure t
- :after (dired-rsync transient))
+ :config
+ (defun my/zoxide-add-dired ()
+ "Append the current dired/dirvish directory to the zoxide database."
+ (when (and default-directory
+ (not (file-remote-p default-directory)))
+ (zoxide-add default-directory)))
+ (add-hook 'dired-after-readin-hook #'my/zoxide-add-dired))
(defun my/reload-config ()
"Re-tangle config.org and reload the generated config.el in place.
@@ -3443,7 +3344,6 @@ activates automatically. Edit, then `C-c C-c' commits, `C-c C-k' aborts."
(my-leader
"TAB" '(:ignore t :wk "workspace")
"TAB TAB" '(my/workspace-display :wk "display")
- "TAB h" '(enlight-open :wk "dashboard")
"TAB ." '(my/workspace-switch-to :wk "switch to…")
"TAB `" '(my/workspace-other :wk "last")
"TAB n" '(my/workspace-new :wk "new")
@@ -3568,25 +3468,21 @@ activates automatically. Edit, then `C-c C-c' commits, `C-c C-k' aborts."
(t (message "No formatter for %s" major-mode))))
(my-leader
- "SPC" '((lambda ()
- (interactive)
- (if (projectile-project-p)
- (consult-projectile-find-file)
- (projectile-switch-project)))
- :wk "find file/switch project")
+ "SPC" '(my/find-file-or-switch-project :wk "find file/switch project")
"sp" '(consult-projectile :wk "search project")
- "ss" '(consult-line :wk "search project")
+ "ss" '(my/fzfa-swiper :wk "search line")
"sg" '(my/consult-ripgrep-in :wk "grep in dir")
"sf" '(my/consult-fd-in :wk "find file in dir")
- "/" '(consult-ripgrep :wk "search project")
- "." '(find-file :wk "find file")
- "," '(consult-buffer :wk "switch buffer")
+ "/" '(fzfa-rg :wk "search project")
+ "." '(fzfa-find :wk "find file")
+ "," '(fzfa-buffer :wk "switch buffer")
":" (lambda () (interactive) (execute-extended-command nil))
"u" '(universal-argument :wk "universal argument")
"d" '(:ignore t :wk "denote")
+ "da" '(my/denote-toggle-agenda-keyword :wk "toggle agenda tag")
"dj" '(denote-journal-new-or-existing-entry :wk "journal")
- "dd" '(denote-menu-list-notes t :wk "List all notes")
+ "dd" '(denote-menu t :wk "List all notes")
"dm" '(:ignore t :wk "Merge Notes")
"dmr" '(denote-merge-region :wk "Merge Region")
"dmf" '(denote-merge-file :wk "Merge File")
@@ -3601,21 +3497,22 @@ activates automatically. Edit, then `C-c C-c' commits, `C-c C-k' aborts."
"f" '(:ignore t :wk "files")
"fd" '(dired-jump :wk "dired")
"fD" '(dired-jump :wk "dired jump")
- "fr" '(consult-recent-file :wk "recent files")
- "ff" '(find-file :wk "find file")
+ "fr" '(fzfa-recent-file :wk "recent files")
+ "ff" '(fzfa-fd :wk "find file")
"fs" '(save-buffer :wk "save file")
"b" '(:ignore t :wk "buffer/bookmarks")
- "bb" '(consult-bookmark :wk "display current bookmarks")
+ "bb" '(fzfa-bookmark :wk "display current bookmarks")
"bi" '(my/ibuffer-workspace :wk "ibuffer (workspace)")
"bp" '(projectile-ibuffer :wk "ibuffer project")
"bd" '(bookmark-delete :wk "delete bookmark")
"bk" '(kill-current-buffer :wk "kill buffer")
"bs" '(bookmark-set :wk "save bookmark")
+ "br" '(rename-buffer :wk "rename buffer")
"p" '(:ignore t :wk "project")
- "pp" '(projectile-switch-project :wk "switch project")
- "pf" '(projectile-find-file :wk "find file")
+ "pp" '(my/fzfa-switch-project-find-file :wk "switch project workspace")
+ "pf" '(fzfa-project-find-file :wk "find file")
"ps" '(consult-ripgrep :wk "search")
"pb" '(consult-projectile-buffer :wk "buffers")
"pk" '(projectile-kill-buffers :wk "kill buffers")
@@ -3644,8 +3541,12 @@ activates automatically. Edit, then `C-c C-c' commits, `C-c C-k' aborts."
"gu" '(my/magit-uncommit :wk "uncommit (keep & unstage)")
"gb" '(vc-annotate :wk "blame")
"gT" '(my/code-todos-harvest :wk "harvest code TODOs")
-
+ "aa" '(agent-shell :wk "start")
+ "at" '(agent-shell-toggle :wk "toggle")
+ "am" '(agent-shell-help-menu :wk "open session")
+ "ar" '(agent-shell-send-region :wk "send region")
"o" '(:ignore t :wk "open")
+ "oa" '(my/app-launcher :wk "app launcher")
"os" '(my/snip-upload :wk "snip buffer/region")
"oS" '(my/snip-upload-file :wk "snip file")
"op" '(pass :wk "pass")
@@ -3763,7 +3664,7 @@ activates automatically. Edit, then `C-c C-c' commits, `C-c C-k' aborts."
"mC" '(org-capture :wk "capture")
"mc" '(:wk "set" :ignore)
- "mk" '(kitty-gfx-org-heading-sizes :wk "kgfx headlines")
+ "mk" '(kitty-graphics-org-heading-sizes :wk "kgfx headlines")
"mcd" '(org-deadline :wk "deadline")
"mcs" '(org-schedule :wk "schedule")
"mce" '(org-set-effort :wk "effort")
@@ -3788,13 +3689,6 @@ activates automatically. Edit, then `C-c C-c' commits, `C-c C-k' aborts."
"mc" '(rust-run-clippy :wk "clippy")
"mC" '(rust-check :wk "check"))
-(with-eval-after-load 'denote-menu
- (general-def 'normal denote-menu-mode-map
- "r" 'denote-menu-filter
- "c" 'denote-menu-clear-filters
- "e" 'denote-menu-export-to-dired
- "o" 'denote-menu-filter-out-keyword))
-
(with-eval-after-load 'tmr
(general-def 'normal tmr-tabulated-mode-map
"y" 'tmr-clone
@@ -4077,28 +3971,6 @@ reset is unnecessary, so do the handler resolution ourselves and skip
"f" #'link-hint-open-link
"F" #'link-hint-copy-link))
-(use-package engine-mode
- :ensure t
- :commands (engine/search-duckduckgo
- engine/search-google
- engine/search-github
- engine/search-mdn
- engine/search-wikipedia
- engine/search-arch-wiki
- engine/search-nixpkgs
- engine/search-rustdocs)
- :config
- (engine-mode 1)
- (setq engine/browser-function 'eww-browse-url)
- (defengine duckduckgo "https://duckduckgo.com/html/?q=%s" :keybinding "d")
- (defengine google "https://google.com/search?q=%s" :keybinding "g")
- (defengine github "https://github.com/search?q=%s&type=code" :keybinding "h")
- (defengine mdn "https://developer.mozilla.org/en-US/search?q=%s" :keybinding "m")
- (defengine wikipedia "https://en.wikipedia.org/wiki/Special:Search?search=%s" :keybinding "w")
- (defengine arch-wiki "https://wiki.archlinux.org/index.php?search=%s" :keybinding "a")
- (defengine nixpkgs "https://search.nixos.org/packages?query=%s" :keybinding "n")
- (defengine rustdocs "https://docs.rs/releases/search?query=%s" :keybinding "r"))
-
(use-package mu4e
:ensure nil
:defer t
@@ -4211,14 +4083,6 @@ reset is unnecessary, so do the handler resolution ourselves and skip
(global-colorful-mode t)
(add-to-list 'global-colorful-modes 'helpful-mode))
-(use-package gleam-ts-mode
- :ensure t
- :mode "\\.gleam\\'"
- :hook (gleam-ts-mode . eglot-ensure)
- :config
- (add-to-list 'eglot-server-programs
- '(gleam-ts-mode . ("gleam" "lsp"))))
-
(use-package envrc
:ensure t
:defer 1
@@ -4339,6 +4203,37 @@ opening another file in same project does not re-notify."
(inheritenv-add-advice 'ghostel-compile)
(inheritenv-add-advice 'ghostel-recompile))
+(when-let* ((garden-dir (expand-file-name
+ (or (alist-get 'garden my/local-packages) "~/garden")))
+ ((file-directory-p garden-dir)))
+ (use-package vui
+ :ensure (:host github :repo "d12frosted/vui.el" :files ("*.el")))
+
+ (add-to-list 'load-path (expand-file-name "lisp/" garden-dir))
+ (setq garden-directory (file-name-as-directory garden-dir))
+ (require 'garden-core)
+ (garden-auto-index-mode 1)
+ (autoload 'garden "garden-dashboard" nil t)
+ (autoload 'garden-sidecar "garden-dashboard" nil t)
+ (autoload 'garden-search "garden-dashboard" nil t)
+ (autoload 'garden-fleet "garden-fleet" nil t)
+ (autoload 'garden-connect "garden-connect" nil t)
+ (autoload 'garden-connect-pick "garden-connect" nil t)
+ (autoload 'garden-publish "garden-publish" nil t)
+ (autoload 'garden-publish-all "garden-publish" nil t)
+ (autoload 'garden-publish-wiki "garden-publish" nil t)
+ (autoload 'garden-publish-blog "garden-publish" nil t)
+ (autoload 'garden-publish-deploy "garden-publish" nil t)
+ (autoload 'denote-capf-setup "denote-capf" nil t)
+ (with-eval-after-load 'denote-capf
+ (setq denote-capf-directories '("~/org/")))
+ (add-hook 'org-mode-hook #'denote-capf-setup)
+
+ (defun my/denote-capf-tab-in-insert ()
+ (when (featurep 'evil)
+ (evil-local-set-key 'insert (kbd "TAB") #'indent-for-tab-command)))
+ (add-hook 'org-mode-hook #'my/denote-capf-tab-in-insert))
+
(use-package sops
:ensure (:type git :host github :repo "djgoku/sops")
:init
@@ -4409,4 +4304,74 @@ opening another file in same project does not re-notify."
:ensure t
:hook (org-mode . org-auto-tangle-mode))
+(use-package fzfa
+ :ensure (:host github :repo "jojojames/fzfa")
+ :custom
+ (fzfa-preview-delay 0.25))
+
+(defun my/project-open-workspace ()
+ "Open the workspace for the project chosen by `project-switch-project'.
+`project-switch-project' conveys the chosen root via
+`project-current-directory-override', not `default-directory'."
+ (interactive)
+ (my/workspace-switch-to-project
+ (project-root (project-current t))))
+
+(defun my/fzfa-switch-project-find-file ()
+ "Switch to a known project's workspace and immediately browse its files.
+Picks a project with fzfa, lets `my/workspace-switch-to-project'
+switch to (or create) the persp workspace named after it, then runs
+`fzfa-project-find-file' inside it. Binding `project-switch-commands'
+to a command symbol makes `project-switch-project' invoke it directly."
+ (interactive)
+ (let ((my/workspace-switch-project-function #'fzfa-project-find-file)
+ (project-switch-commands #'my/project-open-workspace))
+ (fzfa-project-switch-project)))
+
+(defun my/fzfa-swiper ()
+ "Search lines of the current buffer with fontified candidates.
+Like `fzfa-swiper', but keeps font-lock faces on the candidates and
+jumps to the candidate's line as the selection moves. Aborting
+restores the original point."
+ (interactive)
+ (let* ((source (or (buffer-file-name) (buffer-name)))
+ (origin (point-marker))
+ (candidates
+ (save-excursion
+ (font-lock-ensure)
+ (goto-char (point-min))
+ (let ((i 1) lines)
+ (while (not (eobp))
+ (let ((content (buffer-substring
+ (line-beginning-position)
+ (line-end-position))))
+ (unless (string-empty-p content)
+ (push (fzfa--location-candidate
+ (concat (propertize (format "%d:" i)
+ 'face 'line-number)
+ content)
+ source i)
+ lines)))
+ (forward-line 1)
+ (cl-incf i))
+ (nreverse lines)))))
+ (fzfa-visit-location
+ (fzfa-completing-read
+ :candidates candidates
+ :prompt "swiper: "
+ :category 'fzfa-location
+ :preview `(:preview fzfa--location-preview
+ :return ,(lambda (cand)
+ (unless cand
+ (goto-char origin))))))))
+
+(defun my/find-file-or-switch-project ()
+ "Find a file in the current project; outside one, pick a project first.
+After picking a project its files are shown immediately, so this also
+works from the master workspace (e.g. *scratch*)."
+ (interactive)
+ (if (projectile-project-p)
+ (fzfa-project-find-file)
+ (my/fzfa-switch-project-find-file)))
+
(provide 'init)
diff --git a/config.org b/config.org
index dd5c198..38a185c 100644
--- a/config.org
+++ b/config.org
@@ -1784,9 +1784,12 @@ Skips capture tasks and projects."
(:name "Schedule"
:time-grid t
:order 1)
+ (:name "Scheduled today"
+ :scheduled today
+ :order 2)
(:name "Deadlines"
:deadline today
- :order 2)
+ :order 3)
(:discard (:anything t))))))
;; Block 2: active + next tasks (what you can work on right now)
@@ -1821,6 +1824,9 @@ Skips capture tasks and projects."
(:name "Schedule"
:time-grid t
:order 1)
+ (:name "Scheduled today"
+ :scheduled today
+ :order 2)
(:discard (:anything t))))))
;; Block 2: next 6 days
@@ -1889,12 +1895,37 @@ Skips capture tasks and projects."
(:name "Schedule"
:time-grid t
:order 1)
+ (:name "Scheduled today"
+ :scheduled today
+ :order 2)
(:name "Deadlines"
:deadline today
- :order 2)
+ :order 3)
(:discard (:anything t))))))
- ;; Block 2: project
+ ;; Block 2: next 6 days
+ (agenda ""
+ ((org-agenda-start-on-weekday nil)
+ (org-agenda-start-day "+1d")
+ (org-agenda-span 6)
+ (org-deadline-warning-days 0)
+ (org-agenda-overriding-header "Next 6 Days")
+ (org-super-agenda-groups
+ '((:anything t)))))
+
+ ;; Block 3: upcoming deadlines beyond this week
+ (agenda ""
+ ((org-agenda-time-grid nil)
+ (org-agenda-start-on-weekday nil)
+ (org-agenda-start-day "+7d")
+ (org-agenda-span 14)
+ (org-agenda-show-all-dates nil)
+ (org-agenda-entry-types '(:deadline))
+ (org-agenda-overriding-header "Upcoming Deadlines (+14d)")
+ (org-super-agenda-groups
+ '((:anything t)))))
+
+ ;; Block 4: project
(org-ql-block '(and (tags "project")
(not (done)))
((org-ql-block-header "Project")
@@ -1905,7 +1936,7 @@ Skips capture tasks and projects."
(:name "WAIT" :todo "WAIT" :order 4)
(:discard (:anything t))))))
- ;; Block 3: development
+ ;; Block 5: development
(org-ql-block '(and (tags "development")
(not (done)))
((org-ql-block-header "Development")
@@ -1916,7 +1947,7 @@ Skips capture tasks and projects."
(:name "WAIT" :todo "WAIT" :order 4)
(:discard (:anything t))))))
- ;; Block 4: blog
+ ;; Block 6: blog
(org-ql-block '(and (tags "blog")
(not (done)))
((org-ql-block-header "Blog")
@@ -1927,7 +1958,7 @@ Skips capture tasks and projects."
(:name "WAIT" :todo "WAIT" :order 4)
(:discard (:anything t))))))
- ;; Block 5: self
+ ;; Block 7: self
(org-ql-block '(and (tags "self")
(not (done)))
((org-ql-block-header "Self")
@@ -1938,7 +1969,7 @@ Skips capture tasks and projects."
(:name "WAIT" :todo "WAIT" :order 4)
(:discard (:anything t))))))
- ;; Block 6: backlog — everything not covered above
+ ;; Block 8: backlog — everything not covered above
(org-ql-block '(and (todo)
(not (done))
(not (tags "project" "development" "blog" "self")))
@@ -2189,6 +2220,28 @@ Denote fitself focused on the last versions to have a core for note taking and t
(denote-agenda-insinuate))
#+end_src
+** my/denote-toggle-agenda-keyword
+
+#+begin_src emacs-lisp
+(defun my/denote-toggle-agenda-keyword ()
+ "Toggle the \"agenda\" keyword on the current Denote note.
+Renames the file and rewrites its front matter, saving the buffer."
+ (interactive)
+ (unless (and buffer-file-name (denote-file-is-note-p buffer-file-name))
+ (user-error "Current buffer is not a Denote note"))
+ (save-buffer)
+ (let* ((keywords (denote-extract-keywords-from-path buffer-file-name))
+ (denote-rename-confirmations nil)
+ (denote-save-buffers t)
+ (new-keywords (if (member "agenda" keywords)
+ (remove "agenda" keywords)
+ (cons "agenda" keywords))))
+ (denote-rename-file buffer-file-name 'keep-current new-keywords
+ 'keep-current 'keep-current 'keep-current)
+ (message "%s the agenda"
+ (if (member "agenda" new-keywords) "Added to" "Removed from"))))
+#+end_src
+
** denote-journal
#+begin_src emacs-lisp
@@ -2311,29 +2364,13 @@ Works on the base filename (without extension), e.g. matches \"-agenda\", \":age
** denote-menu
+My own vui-based replacement for Protesilaos' =denote-menu= (in =lisp/denote-menu.el=). Lists the notes of the current silo in a borderless, elfeed-style table with filtering and sorting, with first-class evil bindings.
+
#+begin_src emacs-lisp
(use-package denote-menu
- :ensure t
- :defer t
- :config
- (setq denote-menu-title-column-width 60)
-
- (defun denote-menu-export-to-dired ()
- "Switch to a plain Dired buffer listing the filtered *Denotes* files.
-Bypasses dirvish's `dired-noselect' advice: dirvish keys session
-buffers by directory only, so it would reuse the existing buffer
-for `denote-directory' and silently drop the file-list filter."
- (interactive)
- (if-let* ((paths (denote-menu--entries-to-paths))
- (default-directory (denote-directory)))
- (pop-to-buffer-same-window
- (unwind-protect
- (progn
- (advice-remove 'dired-noselect 'dirvish-dired-noselect-a)
- (dired-noselect (cons default-directory paths)))
- (when (bound-and-true-p dirvish-override-dired-mode)
- (advice-add 'dired-noselect :around 'dirvish-dired-noselect-a))))
- (user-error "No files to export"))))
+ :ensure nil
+ :after denote
+ :commands (denote-menu))
#+end_src
** denote-org
@@ -4281,8 +4318,9 @@ activates automatically. Edit, then `C-c C-c' commits, `C-c C-k' aborts."
"u" '(universal-argument :wk "universal argument")
"d" '(:ignore t :wk "denote")
+ "da" '(my/denote-toggle-agenda-keyword :wk "toggle agenda tag")
"dj" '(denote-journal-new-or-existing-entry :wk "journal")
- "dd" '(denote-menu-list-notes t :wk "List all notes")
+ "dd" '(denote-menu t :wk "List all notes")
"dm" '(:ignore t :wk "Merge Notes")
"dmr" '(denote-merge-region :wk "Merge Region")
"dmf" '(denote-merge-file :wk "Merge File")
@@ -4502,17 +4540,6 @@ activates automatically. Edit, then `C-c C-c' commits, `C-c C-k' aborts."
"mC" '(rust-check :wk "check"))
#+end_src
-** denote-menu keymaps
-
-#+begin_src emacs-lisp
-(with-eval-after-load 'denote-menu
- (general-def 'normal denote-menu-mode-map
- "r" 'denote-menu-filter
- "c" 'denote-menu-clear-filters
- "e" 'denote-menu-export-to-dired
- "o" 'denote-menu-filter-out-keyword))
-#+end_src
-
** tmr keynmaps
#+begin_src emacs-lisp
@@ -5270,7 +5297,9 @@ opening another file in same project does not re-notify."
#+begin_src emacs-lisp
(use-package fzfa
- :ensure (:host github :repo "jojojames/fzfa"))
+ :ensure (:host github :repo "jojojames/fzfa")
+ :custom
+ (fzfa-preview-delay 0.25))
(defun my/project-open-workspace ()
"Open the workspace for the project chosen by `project-switch-project'.
@@ -5319,15 +5348,14 @@ restores the original point."
(cl-incf i))
(nreverse lines)))))
(fzfa-visit-location
- (let ((fzfa-preview-delay 0.1))
- (fzfa-completing-read
- :candidates candidates
- :prompt "swiper: "
- :category 'fzfa-location
- :preview `(:preview fzfa--location-preview
- :return ,(lambda (cand)
- (unless cand
- (goto-char origin)))))))))
+ (fzfa-completing-read
+ :candidates candidates
+ :prompt "swiper: "
+ :category 'fzfa-location
+ :preview `(:preview fzfa--location-preview
+ :return ,(lambda (cand)
+ (unless cand
+ (goto-char origin))))))))
(defun my/find-file-or-switch-project ()
"Find a file in the current project; outside one, pick a project first.