summaryrefslogtreecommitdiff
path: root/config.org
diff options
context:
space:
mode:
authorcashmere <cashmere@autistici.org>2026-06-03 16:03:18 +0200
committercashmere <cashmere@autistici.org>2026-06-03 16:03:18 +0200
commit43e3efdd42e3cb23f3183c27974a7b9fd0f5a278 (patch)
tree24c84f2e540936f2de106451f6d4fb00ad8abb0d /config.org
parentd6709fc3e5fc1559059895442e37fe0523666772 (diff)
downloademacs-43e3efdd42e3cb23f3183c27974a7b9fd0f5a278.tar.gz
2026-06-03T16:03:23+02:00
Diffstat (limited to 'config.org')
-rw-r--r--config.org1461
1 files changed, 917 insertions, 544 deletions
diff --git a/config.org b/config.org
index 87217ca..941d6c7 100644
--- a/config.org
+++ b/config.org
@@ -8,9 +8,9 @@
#+begin_src emacs-lisp
;;; config.el --- Emacs-Kick --- A feature rich Emacs config for (neo)vi(m)mers -*- lexical-binding: t; -*-
-;; (setenv "LSP_USE_PLISTS" "true")
+(setenv "LSP_USE_PLISTS" "true")
;; (setq debug-on-error t)
-;; (setq gc-cons-threshold #x40000000)
+(setq gc-cons-threshold #x40000000)
;; (setq gc-cons-threshold 50000000)
;; (setenv "LSP_USE_PLISTS" "true")
;; (setq lsp-use-plists t)
@@ -18,10 +18,7 @@
(setq package-enable-at-startup nil)
;; (setq-default mode-line-format t) ;; disabled: boolean t is not valid for mode-line-format
(setq default-frame-alist '((undecorated . t)))
-(setq gc-cons-threshold 100000000)
-
-(add-hook 'emacs-startup-hook
- (lambda () (setq gc-cons-threshold (* 50 1024 1024))))
+;; GC + file-name-handler-alist tuning lives in early-init.el now.
(setq create-lockfiles nil)
(setq make-backup-files nil)
@@ -107,6 +104,7 @@
(inhibit-startup-message t) ;; Disable the startup message when Emacs launches.
(initial-scratch-message "") ;; Clear the initial message in the *scratch* buffer.
(ispell-dictionary "en_US") ;; Set the default dictionary for spell checking.
+ (text-mode-ispell-word-completion nil) ;; Stop ispell capf in text/org buffers (no wordlist on NixOS → corfu errors).
(make-backup-files nil) ;; Disable creation of backup files.
;; (pixel-scroll-precision-mode t) ;; Enable precise pixel scrolling.
(pixel-scroll-precision-use-momentum nil) ;; Disable momentum scrolling for pixel precision.
@@ -308,6 +306,31 @@
* erc
#+begin_src emacs-lisp
+;; Always-available IRC entry points so `M-x run-irc` works from any daemon
+;; (hub, work, standalone). Calling `erc-tls` autoloads ERC on demand.
+(defun my/soju-password ()
+ "Read the Soju bouncer password from the sops-nix secret."
+ (string-trim
+ (with-temp-buffer
+ (insert-file-contents
+ "/home/cashmere/.config/sops-nix/secrets/senpai_password")
+ (buffer-string))))
+
+(defun run-irc ()
+ "Connect to all IRC networks via Soju bouncer."
+ (interactive)
+ (let ((pw (my/soju-password)))
+ (erc-tls :server "bouncer.cashmere.rs"
+ :port 6699
+ :nick "cashmere1337"
+ :user "cashmere/irc.libera.chat@emacs"
+ :password pw)
+ (erc-tls :server "bouncer.cashmere.rs"
+ :port 6699
+ :nick "cashmere"
+ :user "cashmere/ergo@emacs"
+ :password pw)))
+
(when (my/load-for-hub-p)
(use-package erc
:ensure nil
@@ -380,29 +403,6 @@
(erc-update-modules))
-(defun my/soju-password ()
- "Read the Soju bouncer password from the sops-nix secret."
- (string-trim
- (with-temp-buffer
- (insert-file-contents
- "/home/cashmere/.config/sops-nix/secrets/senpai_password")
- (buffer-string))))
-
-(defun run-irc ()
- "Connect to all IRC networks via Soju bouncer."
- (interactive)
- (let ((pw (my/soju-password)))
- (erc-tls :server "bouncer.cashmere.rs"
- :port 6699
- :nick "cashmere1337"
- :user "cashmere/irc.libera.chat@emacs"
- :password pw)
- (erc-tls :server "bouncer.cashmere.rs"
- :port 6699
- :nick "cashmere"
- :user "cashmere/ergo@emacs"
- :password pw)))
-
(defvar my/consult-source-erc
(list :name "IRC"
:narrow ?i
@@ -459,28 +459,32 @@ Temporarily disables notifications during the fetch."
(erc-cmd-LIST))
(defun persp-irc ()
- "Switch to the irc perspective and connect or show ERC buffers."
+ "Switch to (or create) the IRC frame and show ERC buffers."
(interactive)
- (let ((erc-bufs (cl-remove-if-not
- (lambda (buf)
- (with-current-buffer buf
- (derived-mode-p 'erc-mode)))
- (buffer-list))))
- (persp-switch "irc")
+ (let* ((erc-bufs (cl-remove-if-not
+ (lambda (buf)
+ (with-current-buffer buf
+ (derived-mode-p 'erc-mode)))
+ (buffer-list)))
+ (irc-frame (cl-find-if
+ (lambda (f)
+ (string= (frame-parameter f 'name) "irc"))
+ (frame-list))))
+ (if irc-frame
+ (select-frame-set-input-focus irc-frame)
+ (select-frame-set-input-focus (make-frame '((name . "irc")))))
(if erc-bufs
- (progn
- (dolist (buf erc-bufs)
- (persp-add-buffer buf))
- (switch-to-buffer (car erc-bufs)))
+ (switch-to-buffer (car erc-bufs))
(letrec ((hook-fn (lambda ()
- (when (bound-and-true-p persp-mode)
- (let ((buf (current-buffer)))
- (persp-add-buffer buf)
- (switch-to-buffer buf)))
+ (switch-to-buffer (current-buffer))
(remove-hook 'erc-join-hook hook-fn))))
(add-hook 'erc-join-hook hook-fn))
(run-irc))))
+) ;; end (when (my/load-for-hub-p) ...)
+
+;; ERC keybindings and hooks — register from any daemon, since `run-irc`
+;; autoloads ERC even outside the hub daemon.
(with-eval-after-load 'erc
(add-hook 'erc-mode-hook
(lambda ()
@@ -488,11 +492,6 @@ Temporarily disables notifications during the fetch."
(setq-local maximum-scroll-margin 0.0)
(setq-local corfu-auto nil)))
- (add-hook 'erc-join-hook
- (lambda ()
- (when (bound-and-true-p persp-mode)
- (persp-add-buffer (current-buffer)))))
-
;; Prevent accidental ERC buffer kills from parting channels
(add-hook 'kill-buffer-query-functions
(lambda ()
@@ -504,23 +503,23 @@ Temporarily disables notifications during the fetch."
(buffer-name)))
t)))
- (general-def 'normal erc-mode-map
- "q" 'quit-window
- "Q" 'my/erc-quit-all
- "gb" 'my/erc-switch-channel
- "gn" 'erc-track-switch-buffer
- "gH" 'my/erc-fetch-history
- "go" 'erc-channel-names
- "gj" 'erc-join-channel
- "gl" 'my/erc-list-channels
- "gr" 'my/erc-reconnect
- "RET" 'erc-send-current-line)
+ (with-eval-after-load 'general
+ (general-def 'normal erc-mode-map
+ "q" 'quit-window
+ "Q" 'my/erc-quit-all
+ "gb" 'my/erc-switch-channel
+ "gn" 'erc-track-switch-buffer
+ "gH" 'my/erc-fetch-history
+ "go" 'erc-channel-names
+ "gj" 'erc-join-channel
+ "gl" 'my/erc-list-channels
+ "gr" 'my/erc-reconnect
+ "RET" 'erc-send-current-line)
- (general-def '(normal insert) erc-mode-map
- "M-n" 'erc-track-switch-buffer
- "C-k" 'erc-previous-command
- "C-j" 'erc-next-command))
-) ;; end (when (my/load-for-hub-p) ...)
+ (general-def '(normal insert) erc-mode-map
+ "M-n" 'erc-track-switch-buffer
+ "C-k" 'erc-previous-command
+ "C-j" 'erc-next-command)))
;; Auto-start ERC in hub daemon on first frame
(when (my/hub-p)
@@ -531,7 +530,6 @@ Temporarily disables notifications during the fetch."
(add-hook 'server-after-make-frame-hook #'my/hub-auto-start-erc))
#+end_src
-
* lambda-themes
#+begin_src emacs-lisp
@@ -815,6 +813,8 @@ I adjusted the config accordingly to my modes which I use on a frequent.
(or
(mode . eca-chat-mode)))
+ ("ERC" (or (mode . erc-mode)))
+
("Emacs" (or
(mode . emacs-lisp-mode)
(name . "^\\*Help\\*$")
@@ -859,9 +859,31 @@ Not necessary but an eyecandy to look at!
("C-c ^ p" . smerge-previous))) ;; Move to the previous conflict.
#+end_src
+* general (leader keys)
+
+Defined early so later =with-eval-after-load 'general= callbacks find =my-leader= already bound.
+
+#+begin_src emacs-lisp
+(use-package general
+ :ensure (:wait t)
+ :demand t
+ :config
+ (general-evil-setup)
+
+ (general-create-definer my-leader
+ :states '(normal visual)
+ :keymaps 'override
+ :prefix "SPC")
+
+ (general-create-definer my-local-leader
+ :states '(normal visual)
+ :keymaps 'override
+ :prefix ","))
+#+end_src
+
* ai
-** gptel
+** COMMENT gptel
#+begin_src emacs-lisp
(use-package gptel
@@ -946,7 +968,8 @@ Not necessary but an eyecandy to look at!
** eca /(editor code assistant)/
-#+begin_src emacs-lisp
+# Temporarily disabled while testing claude-code-ide — remove :tangle no to re-enable.
+#+begin_src emacs-lisp :tangle no
(use-package eca
:ensure (:host github :repo "editor-code-assistant/eca-emacs"
:files ("*.el"))
@@ -1035,6 +1058,118 @@ Not necessary but an eyecandy to look at!
"C-c ." '(eca-transient-menu :wk "transient menu"))))
#+end_src
+** claude-code-ide
+
+Native [[https://github.com/manzaltu/claude-code-ide.el][Claude Code IDE]] integration via MCP. Requires the =claude= CLI in
+PATH (already provided through the Nix profile) and uses =vterm= as the
+terminal backend (=eat= is also installed as a fallback).
+
+#+begin_src emacs-lisp
+(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 — vterm is preferred, eat as fallback
+ (claude-code-ide-terminal-backend 'vterm)
+ (claude-code-ide-vterm-anti-flicker nil)
+
+ ;; 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 100)
+ (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 'vterm-mode 'insert)
+ (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
+ (my-leader
+ "a" '(:ignore t :wk "claude-code")
+ "aa" '(claude-code-ide :wk "start / toggle")
+ "am" '(claude-code-ide-menu :wk "transient menu")
+ "at" '(claude-code-ide-toggle :wk "toggle window")
+ "aT" '(claude-code-ide-toggle-recent :wk "toggle recent")
+ "ap" '(claude-code-ide-send-prompt :wk "send prompt")
+ "a@" '(claude-code-ide-insert-at-mentioned :wk "@mention region")
+ "ac" '(claude-code-ide-continue :wk "continue last")
+ "ar" '(claude-code-ide-resume :wk "resume session")
+ "as" '(claude-code-ide-switch-to-buffer :wk "switch buffer")
+ "al" '(claude-code-ide-list-sessions :wk "list sessions")
+ "aq" '(claude-code-ide-stop :wk "stop")
+ "a?" '(claude-code-ide-check-status :wk "check status")
+ "ad" '(claude-code-ide-show-debug :wk "show debug")
+ "aD" '(claude-code-ide-clear-debug :wk "clear debug"))
+
+ (my-leader 'visual
+ "a@" '(claude-code-ide-insert-at-mentioned :wk "@mention region")))
+
+ (global-set-key (kbd "C-c C-'") #'claude-code-ide-menu)
+
+ :config
+ ;; Runs only on first invocation — not at Emacs startup.
+ (claude-code-ide-emacs-tools-setup))
+#+end_src
+
* eldoc
#+begin_src emacs-lisp
@@ -1266,18 +1401,18 @@ automatically encrypted before save and can be decrypted in-place with
#+begin_src emacs-lisp
#+end_src
-** org-modern (disabled in favor of render-org)
+** COMMENT org-modern (disabled in favor of render-org)
#+begin_src emacs-lisp
-;; (use-package org-modern
-;; :ensure t
-;; :hook (org-mode . org-modern-mode)
-;; :config
-;; (setq org-modern-hide-stars t)
-;; (setq org-modern-star 'replace)
-;; (setq org-modern-fold-stars '(("◉" . "○")))
-;; (setq org-modern-replace-stars "◉○◉○◉")
-;; (global-org-modern-mode))
+(use-package org-modern
+ :ensure t
+ :hook (org-mode . org-modern-mode)
+ :config
+ (setq org-modern-hide-stars t)
+ (setq org-modern-star 'replace)
+ (setq org-modern-fold-stars '(("◉" . "○")))
+ (setq org-modern-replace-stars "◉○◉○◉")
+ (global-org-modern-mode))
#+end_src
@@ -1306,21 +1441,28 @@ Display inline images in terminal Emacs via the Kitty graphics protocol.
Only activates when running in a Kitty terminal (not in GUI Emacs).
#+begin_src emacs-lisp
-(use-package kitty-graphics
- :ensure (:host github :repo "cashmeredev/kitty-graphics.el")
- :if (and (not (display-graphic-p)) (getenv "KITTY_PID"))
- :config
+;; TEMP: load kitty-graphics from local dev clone (/home/cashmere/projects/
+;; kitty-graphics/) instead of the elpaca-managed build, so edits to the
+;; source apply on Emacs restart without an `elpaca-rebuild' cycle.
+;; Swap back to the `use-package' / `:ensure' form below once changes are
+;; committed + pushed and elpaca has pulled the new commit.
+(when (not (display-graphic-p))
+ (add-to-list 'load-path "/home/cashmere/projects/kitty-graphics")
+ (require 'kitty-graphics)
+ (setq kitty-gfx-enable-video t)
+ (add-hook 'dired-mode-hook #'kitty-gfx-dired-auto-preview-mode)
+ (setq kitty-gfx-debug nil)
(kitty-graphics-mode 1))
- ;; (with-eval-after-load 'dired
- ;; (define-key dired-mode-map (kbd "P") #'kitty-gfx-dired-preview))
-;; (when (and (not (display-graphic-p))
-;; (or (getenv "KITTY_PID") ;; Kitty terminal
-;; (string-prefix-p "foot" (or (getenv "TERM") "")))) ;; foot terminal (Sixel)
-;; (load "~/projects/kitty-graphics/kitty-graphics.el")
-;; (kitty-graphics-mode 1)
-;; (with-eval-after-load 'dired
-;; (define-key dired-mode-map (kbd "P") #'kitty-gfx-dired-preview)))
+;; (use-package kitty-graphics
+;; :ensure (:host github :repo "cashmeredev/kitty-graphics.el")
+;; :if (not (display-graphic-p))
+;; :custom
+;; (kitty-gfx-enable-video t)
+;; :hook
+;; (dired-mode . kitty-gfx-dired-auto-preview-mode)
+;; :config
+;; (kitty-graphics-mode 1))
#+end_src
** org-fancy-properties
@@ -1412,6 +1554,44 @@ Old config
:after org)
#+end_src
+** emacs-jupyter
+
+Jupyter kernels in org-babel. =#+begin_src jupyter-python :session foo=
+gibt persistente Python REPL, inline plots, async, completion.
+
+Braucht system-side: =python3= mit =ipykernel jupyter-client pyzmq= +
+=jupyter= CLI (siehe nix flake systems/md).
+
+=emacs-zmq= wird beim ersten Laden native gebaut.
+
+#+begin_src emacs-lisp
+(use-package jupyter
+ :ensure t
+ :defer t
+ :commands (jupyter-run-repl jupyter-connect-repl)
+ :init
+ ;; lazy: kein :after org, kein emacs-zmq native build beim startup.
+ ;; jupyter laedt erst wenn ein jupyter-* src block ausgefuehrt wird.
+ (with-eval-after-load 'org
+ (setq org-babel-default-header-args:jupyter-python
+ '((:async . "yes")
+ (:session . "py")
+ (:kernel . "python3")
+ (:results . "raw drawer")))
+ (advice-add 'org-babel-get-src-block-info :around
+ (lambda (orig &rest a)
+ (let ((info (apply orig a)))
+ (when (and info
+ (stringp (nth 0 info))
+ (string-prefix-p "jupyter-" (nth 0 info))
+ (not (featurep 'jupyter)))
+ (require 'jupyter)
+ (org-babel-do-load-languages
+ 'org-babel-load-languages
+ (append org-babel-load-languages '((jupyter . t)))))
+ info)))))
+#+end_src
+
** ob-just
Org-babel support for justfile source blocks. Writes the block body to a
@@ -1461,7 +1641,7 @@ Copies the resulting URL to the kill ring and clipboard."
((and (not file) (buffer-file-name) (file-name-extension (buffer-file-name)))
(file-name-extension (buffer-file-name)))))
(ext-flag (if ext (format " -- -ext %s" ext) ""))
- (cmd (format "ssh pb@pb.cashmere.rs%s 2>/dev/null | sed 's/\\x1b\\[[0-9;]*m//g' | grep -oP 'https://\\S+/f/\\S+'" ext-flag))
+ (cmd (format "ssh pb@pb.moneyspread.st%s 2>/dev/null | sed 's/\\x1b\\[[0-9;]*m//g' | grep -oP 'https://\\S+/f/\\S+'" ext-flag))
(url (string-trim
(if (and file (file-exists-p file))
(shell-command-to-string (format "cat %s | %s" (shell-quote-argument file) cmd))
@@ -2384,28 +2564,28 @@ Denote fitself focused on the last versions to have a core for note taking and t
:config
(setopt denote-journal-title-format 'day-date-month-year))
-(defun my/denote-journal-template ()
- "Insert journal structure into a new journal entry.
-Only inserts when the buffer has just the front-matter (fresh file)."
- (when (and (buffer-file-name)
- (string-match-p "journal" (buffer-file-name))
- (<= (count-lines (point-min) (point-max)) 6))
- (goto-char (point-max))
- (insert
- "\n* Clockreport \n"
- "\n#+BEGIN: clocktable :scope file :maxlevel 3 :emphasize nil :link t\n"
- "#+END:\n"
- "\n* Agenda \n"
- "\n** NEXT Plan the day\n"
- ":LOGBOOK:\n"
- ":END:\n"
- "\n* Braindump \n"
- ":PROPERTIES:\n"
- ":VISIBILITY: folded\n"
- ":END:\n")))
+;; (defun my/denote-journal-template ()
+;; "Insert journal structure into a new journal entry.
+;; Only inserts when the buffer has just the front-matter (fresh file)."
+;; (when (and (buffer-file-name)
+;; (string-match-p "journal" (buffer-file-name))
+;; (<= (count-lines (point-min) (point-max)) 6))
+;; (goto-char (point-max))
+;; (insert
+;; "\n* Clockreport \n"
+;; "\n#+BEGIN: clocktable :scope file :maxlevel 3 :emphasize nil :link t\n"
+;; "#+END:\n"
+;; "\n* Agenda \n"
+;; "\n** NEXT Plan the day\n"
+;; ":LOGBOOK:\n"
+;; ":END:\n"
+;; "\n* Braindump \n"
+;; ":PROPERTIES:\n"
+;; ":VISIBILITY: folded\n"
+;; ":END:\n")))
-(advice-add 'denote-journal-new-or-existing-entry :after
- (lambda (&rest _) (my/denote-journal-template)))
+;; (advice-add 'denote-journal-new-or-existing-entry :after
+;; (lambda (&rest _) (my/denote-journal-template)))
#+end_src
** config
@@ -2569,8 +2749,7 @@ Some goodies for better integration with org files.
#+begin_src emacs-lisp
(use-package denote-explore
- :ensure t
- )
+ :ensure t)
#+end_src
** denote-publish
@@ -2723,24 +2902,24 @@ Timers that expired while Emacs was closed fire immediately."
cand))))
#+end_src
-** vertico-posframe
+** COMMENT vertico-posframe
#+begin_src emacs-lisp
-;; (use-package vertico-posframe
-;; :ensure t
-;; :init
-;; (setq vertico-posframe-parameters '((left-fringe . 12) ;; Fringes
-;; (right-fringe . 12)
-;; (accept-focus . t)))
-;; ;; (undecorated . nil) ;; Rounded frame
-
-;; :config
-;; (vertico-posframe-mode 1)
-;; (setq vertico-posframe-width 96 ;; Narrow frame
-;; vertico-posframe-height vertico-count ;; Default height
-;; ;; Don't create posframe for these commands
-;; vertico-multiform-commands '((consult-line (:not posframe))
-;; (consult-ripgrep (:not posframe)))))
+(use-package vertico-posframe
+ :ensure t
+ :init
+ (setq vertico-posframe-parameters '((left-fringe . 12) ;; Fringes
+ (right-fringe . 12)
+ (accept-focus . t)))
+ ;; (undecorated . nil) ;; Rounded frame
+
+ :config
+ (vertico-posframe-mode 1)
+ (setq vertico-posframe-width 96 ;; Narrow frame
+ vertico-posframe-height vertico-count ;; Default height
+ ;; Don't create posframe for these commands
+ vertico-multiform-commands '((consult-line (:not posframe))
+ (consult-ripgrep (:not posframe)))))
#+end_src
** corfu
@@ -2758,18 +2937,18 @@ Timers that expired while Emacs was closed fire immediately."
(corfu-history-mode)
(corfu-popupinfo-mode))
-(defun my/suppress-corfu-terminal-warning (orig-fun type message &rest args)
- (unless (and (eq type 'corfu)
- (string-match-p "corfu-terminal.*not needed" message))
- (apply orig-fun type message args)))
+;; (defun my/suppress-corfu-terminal-warning (orig-fun type message &rest args)
+;; (unless (and (eq type 'corfu)
+;; (string-match-p "corfu-terminal.*not needed" message))
+;; (apply orig-fun type message args)))
-(advice-add 'display-warning :around #'my/suppress-corfu-terminal-warning)
+;; (advice-add 'display-warning :around #'my/suppress-corfu-terminal-warning)
-(use-package corfu-terminal
- :ensure t
- :after corfu
- :init
- (corfu-terminal-mode +1))
+;; (use-package corfu-terminal
+;; :ensure t
+;; :after corfu
+;; :init
+;; (corfu-terminal-mode +1))
#+end_src
*** cape
@@ -2851,7 +3030,16 @@ ensures that every available tree sitter is downloaded and set up for you.
#+begin_src emacs-lisp
(use-package embark
:ensure t
- :defer t)
+ :defer t
+ :bind
+ (("C-." . embark-act)
+ ("M-." . embark-dwim)
+ ("C-h B" . embark-bindings)
+ :map minibuffer-local-map
+ ("C-c C-e" . embark-export)
+ ("C-c C-o" . embark-collect))
+ :init
+ (setq prefix-help-command #'embark-prefix-help-command))
#+end_src
** embark-consult
@@ -2895,7 +3083,10 @@ completion-category-overrides '((file (styles partial-completion))))) ;; Customi
(use-package eglot
:ensure nil
:config
- (setq eglot-autoshutdown t)
+ ;; nil: LSP server stays alive when last managed buffer closes.
+ ;; Verhindert churn bei org src edit buffers (C-c ' macht buffer auf/zu
+ ;; → mit t neuer pyrefly process jedes Mal).
+ (setq eglot-autoshutdown nil)
(setq eglot-send-changes-idle-time 0.1)
(setq eglot-sync-connect nil)
(setq eglot-connect-timeout 30)
@@ -2964,6 +3155,12 @@ completion-category-overrides '((file (styles partial-completion))))) ;; Customi
'(nix-ts-mode . ("nixd")))))
#+end_src
+*** mark eglot-workspace-configuration safe
+
+#+begin_src emacs-lisp
+(put 'eglot-workspace-configuration 'safe-local-variable #'listp)
+#+end_src
+
** python-ts-mode
Because I have to and not want to.
@@ -2973,10 +3170,15 @@ Because I have to and not want to.
:ensure nil
:mode ("\\.py\\'" . python-ts-mode)
:hook (python-ts-mode . eglot-ensure)
+ :custom
+ (python-indent-offset 4)
+ (python-indent-guess-indent-offset-verbose nil)
+ (python-shell-interpreter "python3")
+ (python-shell-completion-native-enable nil)
:config
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs
- '(python-ts-mode . ("rass" "--" "pyrefly" "lsp" "--" "ruff" "server")))))
+ '((python-ts-mode python-mode) . ("rass" "--" "pyrefly" "lsp" "--" "ruff" "server")))))
#+end_src
*** pyvenv
@@ -3122,6 +3324,119 @@ Uses built-in tree-sitter modes for =.ts= and =.tsx= files.
;; '((nushell . t)))))
#+end_src
+** Xonsh
+
+Python-powered shell. Major mode for =.xsh= files.
+
+#+begin_src emacs-lisp
+(use-package xonsh-mode
+ :ensure (:host github :repo "seanfarley/xonsh-mode")
+ :mode ("\\.xsh\\'" "\\.xonshrc\\'"))
+#+end_src
+
+*** xonsh tree-sitter mode
+
+No dedicated xonsh grammar exists. Xonsh = python superset, so derive
+=xonsh-ts-mode= from =python-ts-mode= — get python font-lock, indent,
+imenu, navigation. Xonsh-specific syntax (=$()=, =@()=, =![]=,
+=$VAR=) shows as python parse errors but stays readable. Fallback to
+=xonsh-mode= when python grammar missing.
+
+#+begin_src emacs-lisp
+(define-derived-mode xonsh-ts-mode python-ts-mode "Xonsh[ts]"
+ "Major mode for xonsh, derived from `python-ts-mode'.
+Xonsh is a python superset; reuse python tree-sitter parser."
+ (setq-local comment-start "# "
+ comment-end ""))
+
+(with-eval-after-load 'treesit
+ (when (treesit-language-available-p 'python)
+ (add-to-list 'auto-mode-alist '("\\.xsh\\'" . xonsh-ts-mode))
+ (add-to-list 'auto-mode-alist '("\\.xonshrc\\'" . xonsh-ts-mode))))
+#+end_src
+
+*** LSP in org src edit buffers
+
+=C-c '= opens indirect buffer without =buffer-file-name= → eglot
+refuses to start. Hack: assign temp =.py= path so pyrefly/ruff
+attach. Works for python AND xonsh src blocks (both route to
+python-ts-mode LSP).
+
+#+begin_src emacs-lisp
+(defun my/org-src-eglot-python ()
+ "Give org src edit buffers a stable temp file name so eglot can attach.
+Name derived from owning org file path → identical across re-opens of
+the same src block file, so eglot reuses the existing connection
+instead of churning."
+ (when (and (derived-mode-p 'python-ts-mode 'python-mode)
+ (not buffer-file-name)
+ (bound-and-true-p org-src-mode))
+ (let* ((parent (or (buffer-file-name (org-src-source-buffer))
+ (buffer-name (org-src-source-buffer))
+ "scratch"))
+ (root (or (and (project-current)
+ (project-root (project-current)))
+ temporary-file-directory))
+ (name (format ".ob-src-%s.py"
+ (substring (md5 parent) 0 8))))
+ (setq-local buffer-file-name (expand-file-name name root))
+ (set-buffer-modified-p nil)
+ (eglot-ensure))))
+
+(add-hook 'org-src-mode-hook #'my/org-src-eglot-python)
+
+;; Kein cleanup-hook: temp file bleibt liegen. Sonst killed eglot
+;; jedesmal die LSP connection wenn C-c ' das buffer schliesst →
+;; neuer pyrefly process beim naechsten Edit. File ist harmlos (gitignored
+;; via .gitignore pattern .ob-src-*.py).
+#+end_src
+
+*** xonsh LSP via eglot
+
+No xonsh language server. Body mostly valid python → reuse python
+LSP stack (=pyrefly= + =ruff server= via =rass=, same as
+=python-ts-mode=). Subshell syntax flags as errors — known frickelig
+trade-off. Hook both =xonsh-mode= and =xonsh-ts-mode=.
+
+#+begin_src emacs-lisp
+(with-eval-after-load 'eglot
+ (add-to-list 'eglot-server-programs
+ '((xonsh-mode xonsh-ts-mode)
+ . ("rass" "--" "pyrefly" "lsp" "--" "ruff" "server"))))
+
+;; kein auto-eglot fuer xonsh — pyrefly.toml waere noetig. Manuell via
+;; M-x my/pyrefly-here-xonsh (legt temp pyrefly.toml an + startet eglot).
+;; (add-hook 'xonsh-mode-hook #'eglot-ensure)
+;; (add-hook 'xonsh-ts-mode-hook #'eglot-ensure)
+#+end_src
+
+*** xonsh org babel
+
+Custom =org-babel-execute:xonsh= — writes block body to temp =.xsh= file
+and runs via the =xonsh= interpreter. Params:
+- =:dir= working directory (default: =default-directory=)
+- =:cmdline= extra args passed to =xonsh=
+
+#+begin_src emacs-lisp
+(with-eval-after-load 'org
+ (defun org-babel-execute:xonsh (body params)
+ "Execute a xonsh source block.
+BODY is the xonsh script. PARAMS may include :dir and :cmdline."
+ (let* ((dir (or (cdr (assq :dir params)) default-directory))
+ (cmdline (or (cdr (assq :cmdline params)) ""))
+ (tmp (make-temp-file "ob-xonsh-" nil ".xsh")))
+ (unwind-protect
+ (progn
+ (with-temp-file tmp (insert body))
+ (let ((default-directory dir))
+ (shell-command-to-string
+ (format "xonsh %s %s"
+ cmdline
+ (shell-quote-argument tmp)))))
+ (when (file-exists-p tmp) (delete-file tmp)))))
+ (add-to-list 'org-src-lang-modes '("xonsh" . xonsh)))
+#+end_src
+
** Go mode
#+begin_src emacs-lisp
@@ -3246,7 +3561,7 @@ Uses built-in tree-sitter modes for =.ts= and =.tsx= files.
:ensure t
:config
(setq vterm-timer-delay 0.0001)
- (setq vterm-shell "fish"))
+ (setq vterm-shell "xonsh"))
#+end_src
*** multi-vterm
@@ -3391,7 +3706,18 @@ With prefix arg, always create a new terminal."
(olivetti-margin-width 10) ; No side margins
;; (olivetti-shrink t)
(olivetti-safe t)
-)
+ :config
+ ;; Guard: vertico-posframe calls `text-scale-set' on *Minibuf-1*, which
+ ;; triggers olivetti's buffer-local text-scale-mode-hook. olivetti then
+ ;; calls window-width on the minibuffer window (nil) -> wrong-type-argument.
+ (add-hook 'minibuffer-setup-hook
+ (lambda ()
+ (when (bound-and-true-p olivetti-mode)
+ (olivetti-mode -1))))
+ (define-advice olivetti-set-window
+ (:around (orig win) my/skip-minibuffer)
+ (unless (window-minibuffer-p win)
+ (funcall orig win))))
#+end_src
* shackle
@@ -3435,24 +3761,24 @@ With prefix arg, always create a new terminal."
;; (setq-default visual-fill-column-center-text t)))
#+end_src
-* diff-hl
+* COMMENT diff-hl
#+begin_src emacs-lisp
-;; (use-package diff-hl
-;; :defer t
-;; :ensure t
-;; :hook
-;; (find-file . (lambda ()
-;; (global-diff-hl-mode) ;; Enable Diff-HL mode for all files.
-;; (diff-hl-flydiff-mode) ;; Automatically refresh diffs.
-;; (diff-hl-margin-mode))) ;; Show diff indicators in the margin.
-;; :custom
-;; (diff-hl-side 'left) ;; Set the side for diff indicators.
-;; (diff-hl-margin-symbols-alist '((insert . "┃") ;; Customize symbols for each change type.
-;; (delete . "-")
-;; (change . "┃")
-;; (unknown . "┆")
-;; (ignored . "i"))))
+(use-package diff-hl
+ ;; :defer t
+ :ensure t
+ :hook
+ (find-file . (lambda ()
+ (global-diff-hl-mode) ;; Enable Diff-HL mode for all files.
+ (diff-hl-flydiff-mode) ;; Automatically refresh diffs.
+ (diff-hl-margin-mode))) ;; Show diff indicators in the margin.
+ :custom
+ (diff-hl-side 'left) ;; Set the side for diff indicators.
+ (diff-hl-margin-symbols-alist '((insert . "┃") ;; Customize symbols for each change type.
+ (delete . "-")
+ (change . "┃")
+ (unknown . "┆")
+ (ignored . "i"))))
#+end_src
* hl-todo
@@ -3494,6 +3820,36 @@ With prefix arg, always create a new terminal."
(setq switch-to-prev-buffer-skip
(lambda (window buffer bury-or-kill)
(string-match-p "\\*magit" (buffer-name buffer))))
+
+(defcustom my/auto-git-add-exclude-regexps
+ '("/\\.git/" "/node_modules/" "/\\.direnv/" "/straight/" "/elpaca/"
+ "\\.gpg\\'" "/\\.cache/" "/__pycache__/")
+ "Paths matching these regexps are skipped by `my/auto-git-add-new-file'."
+ :type '(repeat regexp)
+ :group 'my)
+
+(defun my/auto-git-add-new-file ()
+ "Stage newly created file with intent-to-add so magit shows diffs.
+Runs on `after-save-hook'. Only acts when the saved file is inside a
+git project and currently untracked. Uses `git add -N' (intent-to-add)
+so the working-tree diff stays visible until the user explicitly stages."
+ (let ((file buffer-file-name))
+ (when (and file
+ (file-exists-p file)
+ (not (seq-some (lambda (re) (string-match-p re file))
+ my/auto-git-add-exclude-regexps))
+ (locate-dominating-file file ".git"))
+ (let ((default-directory (file-name-directory file)))
+ (when (and (zerop (call-process "git" nil nil nil "rev-parse" "--is-inside-work-tree"))
+ ;; ls-files --error-unmatch exits non-zero if untracked
+ (not (zerop (call-process "git" nil nil nil
+ "ls-files" "--error-unmatch" file))))
+ (call-process "git" nil nil nil "add" "-N" file)
+ (when (fboundp 'magit-refresh)
+ (magit-refresh))
+ (message "auto-staged (intent-to-add): %s" (file-name-nondirectory file)))))))
+
+(add-hook 'after-save-hook #'my/auto-git-add-new-file)
#+end_src
* indent-guide
@@ -3510,26 +3866,6 @@ With prefix arg, always create a new terminal."
* keybindings
-** general
-
-#+begin_src emacs-lisp
-(use-package general
- :ensure (:wait t)
- :demand t
- :config
- (general-evil-setup)
-
- (general-create-definer my-leader
- :states '(normal visual)
- :keymaps 'override
- :prefix "SPC")
-
- (general-create-definer my-local-leader
- :states '(normal visual)
- :keymaps 'override
- :prefix ","))
-#+end_src
-
** evil-mode
#+begin_src emacs-lisp
@@ -3543,6 +3879,7 @@ With prefix arg, always create a new terminal."
(setq evil-undo-system 'undo-tree)
(setq evil-split-window-below t)
(setq evil-vsplit-window-right t)
+ (setq evil-paste-from-register nil)
:config
(evil-mode 1)
(define-key evil-normal-state-map "u" 'undo-tree-undo)
@@ -3677,20 +4014,16 @@ With prefix arg, always create a new terminal."
** evil multiple cursor
#+begin_src emacs-lisp
-;; Paket für echte Multiple Cursors
(use-package evil-mc
:ensure t
:after evil
:config
(global-evil-mc-mode 1)
- ;; Hilfsfunktion: Prüfen, ob wir im Visual-Block-Mode sind
- ;; Diese Funktion fängt den Fehler ab, falls Variablen nicht existieren.
(defun my/evil-visual-block-p ()
(and (bound-and-true-p evil-visual-selection)
(eq evil-visual-selection 'block)))
- ;; Funktion für Insert am Anfang (I)
(defun my/evil-mc-visual-block-insert ()
"Erstellt Cursor am Anfang des Blocks und wechselt in Insert-Mode."
(interactive)
@@ -3698,10 +4031,8 @@ With prefix arg, always create a new terminal."
(progn
(evil-mc-make-cursor-in-visual-selection-beg)
(evil-insert 1))
- ;; Fallback: normales Verhalten, falls kein Block-Mode
(call-interactively 'evil-insert)))
- ;; Funktion für Append am Ende (A)
(defun my/evil-mc-visual-block-append ()
"Erstellt Cursor am Ende des Blocks und wechselt in Insert-Mode."
(interactive)
@@ -3712,10 +4043,8 @@ With prefix arg, always create a new terminal."
;; Fallback: normales Verhalten
(call-interactively 'evil-append)))
- ;; Tastenbelegung im Visual Mode überschreiben
(define-key evil-visual-state-map (kbd "I") 'my/evil-mc-visual-block-insert)
(define-key evil-visual-state-map (kbd "A") 'my/evil-mc-visual-block-append))
-
#+end_src
* flash
@@ -3827,49 +4156,6 @@ With prefix arg, always create a new terminal."
(evil-org-agenda-set-keys))
#+end_src
-* perspective
-
-Workspace isolation via =perspective.el=. Each perspective has its own
-buffer list so switching buffers only shows relevant ones. Per-frame --
-every =emacsclient -c= frame gets its own set of perspectives.
-
-#+begin_src emacs-lisp
-(use-package perspective
- :ensure (:wait t)
- :demand t
- :custom
- (persp-sort 'created)
- (persp-suppress-no-prefix-key-warning t)
- :init
- (setq persp-mode-prefix-key (kbd "C-c M-p"))
- :config
- (persp-mode)
-
- ;; ---- consult integration --------------------------------------
- ;; Make SPC , show only buffers from the current perspective.
- ;; SPC TAB B gives the unfiltered global list.
-
- (with-eval-after-load 'consult
- (consult-customize consult--source-buffer :hidden t :default nil)
- (add-to-list 'consult-buffer-sources persp-consult-source))
-
- ;; ---- keybindings via general ----------------------------------
-
- (my-leader
- "TAB" '(:ignore t :wk "perspective")
- "TAB TAB" '(persp-switch :wk "switch")
- "TAB n" '(persp-next :wk "next")
- "TAB p" '(persp-prev :wk "prev")
- "TAB d" '(persp-kill :wk "delete")
- "TAB r" '(persp-rename :wk "rename")
- "TAB b" '(persp-switch-to-buffer :wk "buffer")
- "TAB B" '(consult-buffer :wk "global buffer")
- "TAB a" '(persp-add-buffer :wk "add buffer")
- "TAB k" '(persp-remove-buffer :wk "remove buffer")))
-#+end_src
-
-
-
* nerd icons
** nerd icons dired
@@ -3883,7 +4169,7 @@ every =emacsclient -c= frame gets its own set of perspectives.
(dired-mode . nerd-icons-dired-mode))
#+end_src
-** nerd icons bla
+** nerd-icons-completion
#+begin_src emacs-lisp
(use-package nerd-icons-completion
@@ -4317,27 +4603,14 @@ every =emacsclient -c= frame gets its own set of perspectives.
(copy-sequence mode-line-misc-info)))
(use-package maple-modeline
+ :disabled t
:ensure (:host github :repo "honmaple/emacs-maple-modeline")
- :hook (after-init . maple-modeline-mode)
+ ;; :hook (elpaca-after-init . maple-modeline-mode)
:config
(setq maple-modeline-separator 'nil)
(setq maple-modeline-height 25)
(setq maple-modeline-icon t)
- ;; (defun maple-modeline-to-header-line ()
- ;; "Move maple-modeline from mode-line to header-line in all buffers."
- ;; (unless (featurep 'ewm)
- ;; (let ((fmt '(:eval (maple-modeline--init))))
- ;; (setq-default header-line-format fmt)
- ;; (setq-default mode-line-format nil)
- ;; (dolist (buf (buffer-list))
- ;; (with-current-buffer buf
- ;; (setq header-line-format fmt)
- ;; (setq mode-line-format nil)))
- ;; (force-mode-line-update t))))
-
- ;; (add-hook 'maple-modeline-mode-hook #'maple-modeline-to-header-line)
-
(maple-modeline-define-segment my-modeline-time
:format (propertize (downcase (format-time-string " %I:%M %p "))
'face 'bold))
@@ -4406,18 +4679,7 @@ every =emacsclient -c= frame gets its own set of perspectives.
count
position))
- (maple-modeline-define ewm-style
- :left ((evil :left (bar :left ""))
- buffer-info
- flycheck
- version-control)
- :right (org-clock-task
- tmr-timer
- mu4e-mail
- position))
-
- (setq maple-modeline-style
- (if (featurep 'ewm) 'ewm-style 'my-custom-style))
+ (setq maple-modeline-style 'my-custom-style)
:custom-face
(header-line ((t (:inherit mode-line :box nil))))
@@ -4427,27 +4689,24 @@ every =emacsclient -c= frame gets its own set of perspectives.
** punch-line
-
#+begin_src emacs-lisp
-;; (use-package punch-line
-;; :ensure (:host github :repo "konrad1977/punch-line")
-;; :config
-;; (setq punch-line-left-separator " "
-;; punch-line-right-separator " "
-;; punch-show-git-info nil
-;; punch-show-buffer-position t
-;; punch-line-music-max-length 80
-;; punch-line-modal-divider-style 'block
-;; punch-line-section-backgrounds 'auto
-;; punch-show-weather-info t
-;; punch-weather-longitude "10.41"
-;; punch-weather-latitude "53.25"
-;; punch-cpu-usage t
-;; punch-show-processes-info t)
-;; (punch-line-mode 1)
-;; (setq-default header-line-format mode-line-format)
-;; (setq-default mode-line-format nil))
-
+(use-package punch-line
+ :ensure (:host github :repo "cashmeredev/punch-line")
+ :config
+ (setq punch-line-left-separator " "
+ punch-line-right-separator " "
+ punch-show-git-info nil
+ punch-show-buffer-position t
+ punch-show-column-info t
+ punch-line-music-max-length 80
+ punch-line-modal-divider-style 'block
+ punch-line-section-backgrounds 'auto
+ punch-show-weather-info t
+ punch-weather-longitude "10.41"
+ punch-weather-latitude "53.25"
+ punch-cpu-usage t
+ punch-show-processes-info t)
+ (punch-line-mode 1))
#+end_src
* soph
@@ -4891,6 +5150,37 @@ every =emacsclient -c= frame gets its own set of perspectives.
(load-theme 'noctalia t)
#+end_src
+** nano-theme
+
+Nicolas Rougier's N Λ N O theme — companion to =nano-modeline=. Built around six base faces (default, critical, salient, faded, popout, subtle) and a deliberately restrained palette. Light variant uses Material colors, dark variant uses Nord.
+
+We keep our own font (=MapleMono NF=) and skip nano's font setup (=nano-fonts-use=). =nano-window-divider-show= paints subtle dividers between windows so the minimal aesthetic still feels structured.
+
+#+begin_src emacs-lisp
+;; (use-package nano-theme
+;; :ensure (:host github :repo "rougier/nano-theme")
+;; :init
+;; (setq nano-fonts-use nil) ;; keep MapleMono NF, don't force Roboto
+;; (setq nano-window-divider-show t) ;; subtle dividers between windows
+;; :config
+;; (load-theme 'nano-dark t)
+
+;; ;; Convenience: cycle between nano-light and nano-dark.
+;; (defun cashmere/nano-toggle-theme ()
+;; "Toggle between `nano-light' and `nano-dark'."
+;; (interactive)
+;; (let ((dark-active (member 'nano-dark custom-enabled-themes)))
+;; (mapc #'disable-theme custom-enabled-themes)
+;; (load-theme (if dark-active 'nano-light 'nano-dark) t)))
+
+;; ;; Soften a few faces that nano leaves a bit too plain.
+;; (with-eval-after-load 'org
+;; (set-face-attribute 'org-document-title nil :height 1.4 :weight 'light)
+;; (set-face-attribute 'org-level-1 nil :height 1.2 :weight 'medium)
+;; (set-face-attribute 'org-level-2 nil :height 1.1 :weight 'medium)
+;; (set-face-attribute 'org-level-3 nil :height 1.05 :weight 'medium)))
+#+end_src
+
* dirvish
#+begin_src emacs-lisp
@@ -4903,7 +5193,8 @@ every =emacsclient -c= frame gets its own set of perspectives.
(dirvish-quick-access-entries
'(("h" "~/" "Home")
("d" "~/Downloads/" "Downloads")
- ("p" "~/projects/" "Projects")))
+ ;; ("p" "~/projects/" "Projects")
+ ))
(dirvish-reuse-session 'open)
(dirvish-attributes '(file-size))
@@ -4919,7 +5210,27 @@ every =emacsclient -c= frame gets its own set of perspectives.
:config
(setq dired-dwim-target t)
(setq delete-by-moving-to-trash t)
- (setq dired-mouse-drag-files 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)))))
#+end_src
* tramp-hlo
@@ -4990,12 +5301,12 @@ still require a restart since elpaca queues run at init time."
;; (my/global-olivetti-mode)
#+end_src
-* org-modern-indent (disabled in favor of render-org)
+* COMMENT org-modern-indent (disabled in favor of render-org)
#+begin_src emacs-lisp
-;; (use-package org-modern-indent
-;; :ensure (:host github :repo "jdtsmith/org-modern-indent")
-;; :hook (org-mode . org-modern-indent-mode))
+(use-package org-modern-indent
+ :ensure (:host github :repo "jdtsmith/org-modern-indent")
+ :hook (org-mode . org-modern-indent-mode))
#+end_src
* ibuffer hide
@@ -5032,7 +5343,8 @@ still require a restart since elpaca queues run at init time."
projectile-indexing-method 'alien
projectile-sort-order 'recentf
projectile-require-project-root nil
- projectile-globally-ignored-buffers '("\\*magit.*")))
+ projectile-globally-ignored-buffers '("\\*magit.*"))
+ (add-hook 'after-save-hook #'projectile-cache-current-file))
#+end_src
** consult projectile
@@ -5046,15 +5358,36 @@ still require a restart since elpaca queues run at init time."
(setq consult-project-function #'projectile-project-root))
#+end_src
-* pass
+** wgrep
#+begin_src emacs-lisp
-(use-package hidepw
+(use-package wgrep
:ensure t
+ :defer t
:custom
- (hidepw-hide-first-line t)
- :hook (pass-view-mode . hidepw-mode))
+ (wgrep-auto-save-buffer t)
+ (wgrep-change-readonly-file t))
+
+(defun my/project-replace ()
+ "Project-wide search via `consult-ripgrep'.
+In the minibuffer press `C-c C-e' (embark-export) — wgrep mode
+activates automatically. Edit, then `C-c C-c' commits, `C-c C-k' aborts."
+ (interactive)
+ (call-interactively #'consult-ripgrep))
+
+(defun my/embark-export-auto-wgrep (&rest _)
+ "Enter wgrep mode automatically after `embark-export'."
+ (when (and (derived-mode-p 'grep-mode)
+ (fboundp 'wgrep-change-to-wgrep-mode))
+ (wgrep-change-to-wgrep-mode)))
+
+(with-eval-after-load 'embark
+ (advice-add 'embark-export :after #'my/embark-export-auto-wgrep))
+#+end_src
+* pass
+
+#+begin_src emacs-lisp
(use-package pass
:ensure t
:defer t
@@ -5064,25 +5397,6 @@ still require a restart since elpaca queues run at init time."
'("\\*Pass.*\\*"
(display-buffer-full-frame)))
- (defvar-local pass--hidden-overlays nil
- "Overlays used to hide the password line.")
-
- (defun pass-toggle-password ()
- "Toggle visibility of the first line (password) in a pass-view buffer."
- (interactive)
- (if pass--hidden-overlays
- (progn
- (mapc #'delete-overlay pass--hidden-overlays)
- (setq pass--hidden-overlays nil))
- (save-excursion
- (goto-char (point-min))
- (let ((ov (make-overlay (line-beginning-position) (line-end-position))))
- (overlay-put ov 'display (propertize "••••••••" 'face 'shadow))
- (overlay-put ov 'pass-hidden t)
- (push ov pass--hidden-overlays)))))
-
- (add-hook 'pass-view-mode-hook #'pass-toggle-password)
-
(with-eval-after-load 'evil
(general-def 'normal pass-mode-map
"q" 'quit-window
@@ -5102,7 +5416,7 @@ still require a restart since elpaca queues run at init time."
(general-def 'normal pass-view-mode-map
"q" 'quit-window
- "t" 'pass-toggle-password
+ "t" 'pass-view-toggle-password
"y" 'pass-copy
"Y" 'pass-copy-field)))
#+end_src
@@ -5148,10 +5462,19 @@ still require a restart since elpaca queues run at init time."
#+begin_src emacs-lisp
(use-package pdf-tools
:ensure nil
+ :if (display-graphic-p)
:magic ("%PDF" . pdf-view-mode)
:config
(pdf-loader-install)
:hook (pdf-view-mode . (lambda () (display-line-numbers-mode -1))))
+
+;; Site-lisp may pre-register pdf-view-mode in magic-mode-alist on TUI emacs.
+;; Strip those entries so opening a PDF in -nw falls back to doc-view/fundamental.
+(unless (display-graphic-p)
+ (setq magic-mode-alist
+ (seq-remove (lambda (e) (eq (cdr e) 'pdf-view-mode)) magic-mode-alist))
+ (setq auto-mode-alist
+ (seq-remove (lambda (e) (eq (cdr e) 'pdf-view-mode)) auto-mode-alist)))
#+end_src
* keymaps
@@ -5203,7 +5526,7 @@ still require a restart since elpaca queues run at init time."
"b" '(:ignore t :wk "buffer/bookmarks")
"bb" '(consult-bookmark :wk "display current bookmarks")
- "bI" '(ibuffer :wk "ibuffer")
+ "ba" '(ibuffer :wk "ibuffer")
"bi" '(projectile-ibuffer :wk "ibuffer project")
"bd" '(bookmark-delete :wk "delete bookmark")
"bk" '(kill-current-buffer :wk "kill buffer")
@@ -5216,7 +5539,7 @@ still require a restart since elpaca queues run at init time."
"pb" '(consult-projectile-buffer :wk "buffers")
"pk" '(projectile-kill-buffers :wk "kill buffers")
"pd" '(projectile-remove-known-project :wk "delete project")
- "pr" '(projectile-recentf :wk "recent files")
+ "pr" '(my/project-replace :wk "project replace (wgrep)")
"pa" '(projectile-add-known-project :wk "add project")
"pi" '(projectile-invalidate-cache :wk "invalidate cache")
@@ -5243,6 +5566,7 @@ still require a restart since elpaca queues run at init time."
"o" '(:ignore t :wk "open")
"os" '(my/snip-upload :wk "snip buffer/region")
"oS" '(my/snip-upload-file :wk "snip file")
+ "op" '(pass :wk "pass")
"h" '(:ignore t :wk "help")
"hm" '(describe-mode :wk "mode")
@@ -5288,23 +5612,17 @@ still require a restart since elpaca queues run at init time."
"t" '(:ignore t :wk "treesitter")
"ts" '(flash-treesitter :wk "flash treesitter")
- ;; ECA (AI assistant)
- "a" '(:ignore t :wk "eca/ai")
- "aa" '(eca :wk "open eca")
- "ac" '(eca-chat-toggle-window :wk "toggle chat")
- "an" '(eca-chat-new :wk "new chat")
- "as" '(eca-chat-select :wk "select chat")
- "ar" '(eca-rewrite :wk "rewrite region")
- "am" '(eca-chat-select-model :wk "select model")
- "av" '(eca-chat-select-variant :wk "select variant")
- "ag" '(eca-chat-select-agent :wk "select agent")
- "at" '(eca-transient-menu :wk "transient menu")
- "ad" '(eca-mcp-details :wk "mcp details")
- "aR" '(eca-restart :wk "restart eca")
- "aS" '(eca-stop :wk "stop eca")
- "aw" '(eca-workspaces :wk "workspaces")
- "ae" '(eca-show-errors :wk "show errors")
- "aC" '(eca-open-global-config :wk "global config"))
+
+ "e" '(:ignore t :wk "eww/web")
+ "e e" '(eww :wk "eww browse / search")
+ "e n" '(my/eww-new-buffer :wk "new eww buffer")
+ "e b" '(eww-list-bookmarks :wk "bookmarks")
+ "e h" '(eww-list-histories :wk "history")
+ "e f" '(elfeed :wk "elfeed (rss)")
+ "e s" '(engine/search-duckduckgo :wk "search duckduckgo")
+ "e L" '(link-hint-open-link :wk "hint open link")
+ "e C" '(link-hint-copy-link :wk "hint copy link")
+ )
(defun my/flash-enabled-p ()
(and (not (derived-mode-p 'magit-mode 'dired-mode 'ibuffer-mode))
@@ -5486,6 +5804,30 @@ date) and the dired header are never skipped."
"h" 'dirvish-hardlink)
#+end_src
+** eshell keymaps
+
+#+begin_src emacs-lisp
+(defun my/eshell-clear ()
+ (interactive)
+ (eshell/clear-scrollback))
+
+(defun my/atuin-history ()
+ (interactive)
+ (eshell-atuin-history))
+
+(with-eval-after-load 'eshell
+ (add-hook 'eshell-mode-hook
+ (lambda ()
+ (local-set-key (kbd "C-l") 'my/eshell-clear)
+ (local-set-key (kbd "C-r") 'my/atuin-history))))
+
+(with-eval-after-load 'evil
+ (with-eval-after-load 'eshell
+ (evil-define-key '(normal insert) eshell-mode-map
+ (kbd "C-r") 'my/atuin-history
+ (kbd "C-l") 'my/eshell-clear)))
+#+end_src
+
** local leader
#+begin_src emacs-lisp
@@ -5498,41 +5840,9 @@ date) and the dired header are never skipped."
"e" '(eshell :wk "eshell")
"z" '(golden-ratio-mode :wk "zoom/golden ratio")
"o" '(my/global-olivetti-mode :wk "center buffer")
- "s" '(my-org-sidecar-left :wk "org sidecar")
-
- "g" '(:wk "gptel" :ignore)
-
- "gg" '(gptel :wk "open gptel")
- "ga" '(gptel-add :wk "add buffer to gptel")
- "gf" '(gptel-add-file :wk "add file to gptel")
- "gA" '(gptel-abort :wk "abort response")
- "gr" '(gptel-rewrite :wk "rewrite section")
- "gm" '(gptel-menu :wk "open gptel menu"))
+ "s" '(my-org-sidecar-left :wk "org sidecar"))
#+end_src
-** emms-keybinds
-
-#+begin_src emacs-lisp
-;; (my-leader
-;; :keymaps 'override
-;; "m" '(:ignore t :wk "music/emms")
-;; "mm" '(emms-browser :wk "browser")
-;; "mp" '(emms-playlist-mode-go :wk "playlist")
-;; "ma" '(emms-add-directory-tree :wk "add directory")
-;; "mf" '(emms-play-file :wk "play file")
-;; "md" '(emms-play-directory :wk "play directory")
-;; "ms" '(emms-start :wk "start/play")
-;; "mS" '(emms-stop :wk "stop")
-;; "mn" '(emms-next :wk "next track")
-;; "mN" '(emms-previous :wk "previous track")
-;; "mP" '(emms-pause :wk "pause")
-;; "mr" '(emms-random :wk "random track")
-;; "mc" '(emms-playlist-clear :wk "clear playlist")
-;; "mC" '(emms-cache-set-from-mpd-all :wk "sync mpd cache")
-;; "mu" '(emms-player-mpd-update-all :wk "update mpd db")
-;; "mw" '(emms-playlist-save :wk "save playlist")
-;; "ml" '(emms-playlist-load :wk "load playlist"))
-#+end_src
** convenient keys
@@ -5608,6 +5918,7 @@ date) and the dired header are never skipped."
((string< a-feed b-feed) t)
((string> a-feed b-feed) nil)
(t (string< a-title b-title)))))))
+
(use-package elfeed-org
:ensure t
:after elfeed
@@ -5616,6 +5927,34 @@ date) and the dired header are never skipped."
:config
(elfeed-org))
+(use-package elfeed-goodies
+ :ensure t
+ :after elfeed
+ :config
+ (elfeed-goodies/setup))
+
+(use-package shrface
+ :ensure t
+ :after elfeed
+ :config
+ (shrface-basic)
+ (shrface-trial)
+ (shrface-default-keybindings)
+ (setq shrface-href-versatile t
+ shrface-bullets-bullet-list '("◉" "○" "✸" "✿")))
+
+(with-eval-after-load 'elfeed
+ (require 'shrface)
+ (add-hook 'elfeed-show-mode-hook
+ (lambda ()
+ (visual-line-mode 1)
+ (setq-local shr-width nil
+ shr-use-fonts t
+ shr-max-image-proportion 0.6
+ shr-indentation 2
+ line-spacing 0.2)
+ (shrface-mode 1))))
+
(defun elfeed-export-from-index-to-csv (output-file)
(interactive "FExport CSV nach: ")
(require 'elfeed)
@@ -5644,6 +5983,140 @@ date) and the dired header are never skipped."
browse-url-generic-program "qutebrowser")
#+end_src
+* eww
+
+** core eww / shr / url settings
+
+#+begin_src emacs-lisp
+(use-package eww
+ :ensure nil
+ :commands (eww eww-search-words)
+ :custom
+ (eww-search-prefix "https://duckduckgo.com/html/?q=")
+ (eww-auto-rename-buffer 'title)
+ (eww-browse-url-new-window-is-tab nil)
+ (eww-download-directory (expand-file-name "~/Downloads"))
+ (eww-bookmarks-directory (locate-user-emacs-file "eww-bookmarks/"))
+ (eww-restore-desktop t)
+ (eww-desktop-remove-duplicates t)
+ (eww-history-limit 150)
+ (eww-suggest-uris '(eww-links-at-point
+ thing-at-point-url-at-point))
+ (eww-header-line-format "%t — %u")
+ (url-privacy-level '(email os emacs lastloc))
+ (url-cookie-trusted-urls '())
+ :config
+ (setq-default shr-inhibit-images nil
+ shr-use-fonts t
+ shr-use-colors t
+ shr-folding-mode t
+ shr-bullet "• "
+ shr-image-animate nil
+ shr-max-image-proportion 0.6
+ shr-width nil
+ shr-cookie-policy nil)
+ (make-directory eww-bookmarks-directory t)
+
+ (with-eval-after-load 'general
+ (my-local-leader
+ :keymaps 'eww-mode-map
+ "B" '(eww-add-bookmark :wk "add bookmark")
+ "r" '(eww-readable :wk "reader mode")
+ "i" '(my/eww-toggle-images :wk "toggle images")
+ "y" '(my/eww-copy-as-org :wk "copy page as org")
+ "d" '(eww-download :wk "download"))))
+#+end_src
+
+** helper functions
+
+#+begin_src emacs-lisp
+(defun my/eww-new-buffer (url)
+ "Open URL in a fresh eww buffer (keep current)."
+ (interactive (list (read-from-minibuffer "URL: ")))
+ (eww url 4))
+
+(defun my/eww-toggle-images ()
+ "Toggle image rendering and reload page."
+ (interactive)
+ (setq-local shr-inhibit-images (not shr-inhibit-images))
+ (eww-reload)
+ (message "Images %s" (if shr-inhibit-images "off" "on")))
+
+(defun my/eww-copy-as-org ()
+ "Yank visible eww region/page as org-formatted text."
+ (interactive)
+ (require 'ol-eww)
+ (org-eww-copy-for-org-mode))
+#+end_src
+
+** shrface — org-like html rendering
+
+#+begin_src emacs-lisp
+(use-package shrface
+ :ensure t
+ :hook (eww-after-render . shrface-mode)
+ :custom
+ (shrface-bullets-bullet-list '("◉" "○" "✸" "✿"))
+ (shrface-href-versatile t)
+ :config
+ (shrface-basic)
+ (shrface-trial))
+#+end_src
+
+** shr-tag-pre-highlight — syntax-highlighted code blocks
+
+#+begin_src emacs-lisp
+(use-package shr-tag-pre-highlight
+ :ensure (:host github :repo "xuchunyang/shr-tag-pre-highlight.el")
+ :after shr
+ :config
+ (add-to-list 'shr-external-rendering-functions
+ '(pre . shr-tag-pre-highlight)))
+#+end_src
+
+** ace-link — avy-style link jumping
+
+#+begin_src emacs-lisp
+(use-package ace-link
+ :ensure t
+ :commands (ace-link-eww ace-link-info ace-link-help ace-link-org)
+ :config (ace-link-setup-default))
+#+end_src
+
+** link-hint — vimium-style hints
+
+#+begin_src emacs-lisp
+(use-package link-hint
+ :ensure t
+ :commands (link-hint-open-link link-hint-copy-link))
+#+end_src
+
+** engine-mode — search engine shortcuts (C-x / …)
+
+#+begin_src emacs-lisp
+(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"))
+#+end_src
+
* mu4e
#+begin_src emacs-lisp
@@ -5765,23 +6238,6 @@ this package provides the ability to display rgb and hex colors.
(setq denote-project-notes-identifier '(project stayem)))
#+end_src
-#+RESULTS:
-: t
-
-* snippy
-
-#+begin_src emacs-lisp
-;; (use-package snippy
-;; :ensure (:host github
-;; :repo "MiniApollo/snippy.git"
-;; :branch "main")
-;; :hook (after-init . global-snippy-minor-mode)
-;; :custom
-;; (snippy-global-languages '("global")) ;; Recomended
-;; :config
-;; (snippy-install-or-update-snippets)) ;; Autoupdate git repo
-#+end_src
-
* emacs-everywhere
#+begin_src emacs-lisp
@@ -5800,153 +6256,6 @@ this package provides the ability to display rgb and hex colors.
;; (profiler-stop)
#+end_src
-* ewm
-
-#+begin_src emacs-lisp
-(with-eval-after-load 'ewm
- (require 'ewm-transient)
-
- ;; ── Bugfix: guard against nil output in pointer functions ──
- ;; ewm-input--pointer-in-window-p crashes with "stringp, nil" when
- ;; called on a frame without ewm-output (e.g. the initial daemon frame
- ;; during startup when dashboard does switch-to-buffer).
- (defun ewm-input--pointer-in-window-p (window)
- "Return non-nil if pointer is inside WINDOW. Safe for non-EWM frames."
- (let* ((frame (window-frame window))
- (output (frame-parameter frame 'ewm-output)))
- (when output
- (let* ((output-offset (ewm--get-output-offset output))
- (edges (window-inside-pixel-edges window))
- (left (+ (car output-offset) (nth 0 edges)))
- (top (+ (cdr output-offset) (nth 1 edges)))
- (right (+ (car output-offset) (nth 2 edges)))
- (bottom (+ (cdr output-offset) (nth 3 edges)))
- (pointer (ewm-get-pointer-location))
- (px (car pointer))
- (py (cdr pointer)))
- (and (<= left px right)
- (<= top py bottom))))))
-
- (defun ewm-input--warp-pointer-to-window (window)
- "Warp pointer to center of WINDOW. Safe for non-EWM frames."
- (unless (minibufferp (window-buffer window))
- (let* ((frame (window-frame window))
- (output (frame-parameter frame 'ewm-output)))
- (when (and output (not (ewm-input--pointer-in-window-p window)))
- (let* ((output-offset (ewm--get-output-offset output))
- (edges (window-inside-pixel-edges window))
- (x (+ (car output-offset) (/ (+ (nth 0 edges) (nth 2 edges)) 2)))
- (y (+ (cdr output-offset) (/ (+ (nth 1 edges) (nth 3 edges)) 2))))
- (ewm-warp-pointer (float x) (float y)))))))
-
- ;; ── Modeline: use minimal style under ewm ────
- (when (bound-and-true-p maple-modeline-mode)
- (setq maple-modeline-style 'ewm-style)
- (force-mode-line-update t))
-
- ;; ── Settings ─────────────────────────────────────────────
- (setq ewm-mouse-follows-focus t)
- (setq ewm-input-config '((keyboard :repeat-delay 200 :repeat-rate 25)))
- (setq ewm-idle '(300 . "swaylock -f -c 333333"))
-
- ;; Per-host output config
- (pcase (system-name)
- ("md"
- (setq ewm-output-config
- '(("DP-1" :x 0 :y 0 :refresh 144))))
- ("sportmacher"
- (setq ewm-output-config
- '(("DSI-1" :width 800 :height 1280 :transform 3 :scale 1.0)))))
-
- ;; ── Programm-Launcher ──────────────────────────────────────
- (define-key ewm-mode-map (kbd "s-<return>")
- (lambda () (interactive) (start-process "kitty" nil "kitty")))
- (define-key ewm-mode-map (kbd "M-s-7")
- (lambda (command)
- (interactive (list (read-shell-command "$ ")))
- (start-process-shell-command command nil command)))
- (define-key ewm-mode-map (kbd "s-SPC")
- (lambda () (interactive) (start-process "vicinae" nil "vicinae" "toggle")))
- (define-key ewm-mode-map (kbd "s-b")
- (lambda () (interactive) (start-process "helium" nil "helium-browser")))
- (define-key ewm-mode-map (kbd "s-p")
- (lambda () (interactive) (start-process "vicinae" nil "vicinae" "vicinae://extensions/tinkerbells/pass/pass")))
- (define-key ewm-mode-map (kbd "s-v")
- (lambda () (interactive) (start-process "vicinae" nil "vicinae" "vicinae://extensions/vicinae/clipboard/history")))
- (define-key ewm-mode-map (kbd "s-s")
- (lambda () (interactive) (start-process "vicinae" nil "vicinae" "vicinae://extensions/vicinae/power")))
- (define-key ewm-mode-map (kbd "s-d")
- (lambda () (interactive) (start-process "vicinae" nil "vicinae" "vicinae://extensions/cashmere/deepl-improve/deepl-improve")))
- (define-key ewm-mode-map (kbd "M-s")
- (lambda () (interactive) (start-process "snipe" nil "snipe")))
- (define-key ewm-mode-map (kbd "s-<print>")
- (lambda () (interactive)
- (start-process-shell-command "screenshot" nil "grim -g \"$(slurp)\" - | wl-copy")))
- (define-key ewm-mode-map (kbd "<print>")
- (lambda () (interactive)
- (start-process-shell-command "screenshot-pb" nil
- "grim -g \"$(slurp)\" - | ssh pb@pb.cashmere.rs -- -ext png 2>/dev/null | sed 's/\\x1b\\[[0-9;]*m//g' | grep -oP 'https://\\S+/f/\\S+' | sed 's/$/?r=1/' | wl-copy")))
-
- ;; ── Window Management ────────────────────────────────────
- (define-key ewm-mode-map (kbd "s-h") #'windmove-left)
- (define-key ewm-mode-map (kbd "s-j") #'windmove-down)
- (define-key ewm-mode-map (kbd "s-k") #'windmove-up)
- (define-key ewm-mode-map (kbd "s-l") #'windmove-right)
- (define-key ewm-mode-map (kbd "s-q") #'kill-current-buffer)
- (define-key ewm-mode-map (kbd "s-f") #'ewm-toggle-fullscreen)
- (define-key ewm-mode-map (kbd "s-S-q") #'ewm-stop-module)
-
- ;; tab switching s-0..s-9 (s-0 = tab 10)
- (dotimes (i 10)
- (define-key ewm-mode-map
- (kbd (format "s-%d" i))
- (let ((n i))
- (lambda () (interactive) (tab-bar-select-tab (if (= n 0) 10 n))))))
-
- ;; ── Media Keys ───────────────────────────────────────────
- (define-key ewm-mode-map (kbd "<XF86AudioLowerVolume>")
- (lambda () (interactive) (start-process "vol-" nil "pamixer" "-d" "5")))
- (define-key ewm-mode-map (kbd "<XF86AudioRaiseVolume>")
- (lambda () (interactive) (start-process "vol+" nil "pamixer" "-i" "5")))
- (define-key ewm-mode-map (kbd "<XF86AudioMute>")
- (lambda () (interactive) (start-process "mute" nil "pamixer" "--toggle-mute")))
- (define-key ewm-mode-map (kbd "<XF86AudioMicMute>")
- (lambda () (interactive)
- (start-process-shell-command "micmute" nil
- "pactl list sources short | grep input | awk '{print $1}' | xargs -I{} pactl set-source-mute {} toggle")))
- (define-key ewm-mode-map (kbd "<XF86AudioPlay>")
- (lambda () (interactive) (start-process "play" nil "playerctl" "play-pause")))
- (define-key ewm-mode-map (kbd "<XF86AudioMedia>")
- (lambda () (interactive) (start-process "play" nil "playerctl" "play-pause")))
- (define-key ewm-mode-map (kbd "<XF86AudioNext>")
- (lambda () (interactive) (start-process "next" nil "playerctl" "next")))
- (define-key ewm-mode-map (kbd "<XF86AudioPrev>")
- (lambda () (interactive) (start-process "prev" nil "playerctl" "previous")))
- (define-key ewm-mode-map (kbd "<XF86MonBrightnessUp>")
- (lambda () (interactive) (start-process "bri+" nil "brightnessctl" "set" "+5%")))
- (define-key ewm-mode-map (kbd "<XF86MonBrightnessDown>")
- (lambda () (interactive) (start-process "bri-" nil "brightnessctl" "set" "5%-")))
-
- ;; ── Leader bindings under SPC e ──────────────────────────
- (my-leader
- "e" '(:ignore t :wk "ewm")
- "e a" '(ewm-launch-app :wk "launch app")
- "e n" '(ewm-next-surface-buffer :wk "next surface")
- "e p" '(ewm-prev-surface-buffer :wk "prev surface")
- "e f" '(ewm-toggle-fullscreen :wk "fullscreen")
- "e t" '(ewm-transient :wk "control panel")
- "e s" '(ewm-screenshot :wk "screenshot")
- "e i" '(ewm-show-state :wk "inspect state")
- "e d" '(ewm-debug-mode :wk "debug")
- "e l" '(ewm-lock-session :wk "lock")
- "e T" '(:ignore t :wk "tab")
- "e T n" '(tab-new :wk "new tab")
- "e T c" '(tab-close :wk "close tab")
- "e T l" '(tab-next :wk "next tab")
- "e T h" '(tab-previous :wk "prev tab")
- "e T r" '(tab-recent :wk "recent tab")))
-#+end_src
-
* arrow.el
#+begin_src emacs-lisp
@@ -5972,15 +6281,6 @@ this package provides the ability to display rgb and hex colors.
(add-to-list 'global-colorful-modes 'helpful-mode))
#+end_src
-
-* plz
-
-#+begin_src emacs-lisp
-(use-package plz
- :ensure t)
-#+end_src
-
-
* gleam
#+begin_src emacs-lisp
@@ -5993,16 +6293,68 @@ this package provides the ability to display rgb and hex colors.
'(gleam-ts-mode . ("gleam" "lsp"))))
#+end_src
-*** envrc
+* envrc
Automatic flake devshell activation.
+Async activation: defer per-buffer =envrc-mode= via idle timer so direnv
+=process-file= does not block buffer display.
+
#+begin_src emacs-lisp
(use-package envrc
:ensure t
- :init (envrc-global-mode))
+ :defer 1
+ :init
+ (require 'notifications)
+ (defvar my/envrc-notified (make-hash-table :test 'equal)
+ "Directories already notified about, to avoid spam.")
+ (defun my/envrc-notify (dir status)
+ "Desktop notification for DIR with STATUS symbol.
+Skip when no .envrc (STATUS `none'). Dedupe by .envrc root so
+opening another file in same project does not re-notify."
+ (when (and dir (memq status '(on error)))
+ (let ((root (or (locate-dominating-file dir ".envrc") dir)))
+ (unless (gethash root my/envrc-notified)
+ (puthash root t my/envrc-notified)
+ (ignore-errors
+ (notifications-notify
+ :title "direnv"
+ :body (format "%s — %s"
+ (abbreviate-file-name root)
+ (pcase status
+ ('on "environment loaded")
+ ('error "load failed")
+ (_ (format "%s" status))))
+ :app-name "emacs"
+ :urgency (if (eq status 'error) 'critical 'low)
+ :timeout 3000))
+ (message "envrc: %s [%s]" root status)))))
+ (defun my/envrc-maybe-async ()
+ "Enable `envrc-mode' asynchronously for current buffer."
+ (when (and (not (bound-and-true-p envrc-mode))
+ buffer-file-name
+ (not (file-remote-p buffer-file-name)))
+ (let ((buf (current-buffer)))
+ (run-with-idle-timer
+ 0.2 nil
+ (lambda ()
+ (when (buffer-live-p buf)
+ (with-current-buffer buf
+ (envrc-mode 1)
+ (let ((dir (and (boundp 'envrc--status)
+ (car-safe (bound-and-true-p envrc--process-env))
+ default-directory))
+ (status (bound-and-true-p envrc--status)))
+ (when status
+ (my/envrc-notify default-directory status))))))))))
+ :hook ((find-file . my/envrc-maybe-async)
+ (dired-mode . my/envrc-maybe-async))
+ :config
+ (when (bound-and-true-p envrc-global-mode)
+ (envrc-global-mode -1)))
#+end_src
-* bbj
+
+* COMMENT bbj
#+begin_src emacs-lisp
(use-package bbj
@@ -6023,11 +6375,33 @@ Automatic flake devshell activation.
(use-package tldr
:ensure t)
#+end_src
-* holo-layer
-Visual enhancement overlay for Emacs (cursor animation, window borders, etc).
-Installed via Nix emacs package set — no elpaca needed.
+* eshell
+
+#+begin_src emacs-lisp
+(use-package eshell
+ :ensure nil
+ :config
+ (setq eshell-command-aliases-list
+ '(("nh-switch" "nh os switch --quiet $*"))))
+#+end_src
+
+* atuin-eshell
+
+#+begin_src emacs-lisp
+(use-package eshell-atuin
+ :ensure t
+ :after eshell
+ :config
+ (eshell-atuin-mode))
+#+end_src
+
+* elfetch
+#+begin_src emacs-lisp
+(use-package el-fetch
+ :ensure t)
+#+end_src
* provide init
@@ -6035,4 +6409,3 @@ Installed via Nix emacs package set — no elpaca needed.
(provide 'init)
#+end_src
-