diff options
| author | cashmere <cashmere@autistici.org> | 2026-08-11 01:07:16 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-08-11 01:07:16 +0200 |
| commit | 819e0ab1cb942dc6b80a5141723bad04ee2dfa61 (patch) | |
| tree | 58b6cb87d6f5e23c7db3252ccf1d0c84cfb529cb | |
| parent | 822e857b51fc78d63e0f5b60aa6530d6c9d01b58 (diff) | |
| download | emacs-819e0ab1cb942dc6b80a5141723bad04ee2dfa61.tar.gz | |
md: 2026-08-11T01:07:20+02:00
| -rw-r--r-- | config.org | 160 | ||||
| -rw-r--r-- | lisp/my-mode-line.el | 184 |
2 files changed, 290 insertions, 54 deletions
@@ -435,7 +435,7 @@ Selects the compilation window so the cursor lands in it." (setq erc-fill-wrap-align-prompt nil) (setq erc-fill-static-center 14) - ;; track -- activity NOT in modeline (channels list clutters doom-modeline) + ;; track -- activity NOT in mode-line (channel list would clutter it) (setq erc-track-position-in-mode-line nil) ;; keep large buffer for soju history replay @@ -610,6 +610,15 @@ Temporarily disables notifications during the fetch." pass-otp-uri-copy pass-view-copy-password pass-view-copy-token)) + (advice-add cmd :after #'my/elfeed-yank-to-clipboard))) + (with-eval-after-load 'passage + (dolist (cmd '(passage-copy + passage-copy-field + passage-copy-username + passage-copy-url + passage-otp-token-copy + passage-otp-uri-copy + passage-view-copy-password)) (advice-add cmd :after #'my/elfeed-yank-to-clipboard)))) #+end_src @@ -1083,6 +1092,96 @@ automatically encrypted before save and can be decrypted in-place with (add-hook 'org-mode-hook (lambda () (setq-local auto-save-default nil)))) #+end_src +** age + +#+begin_src emacs-lisp +(use-package age + :ensure t + :demand t + :custom + (age-program "rage") + (age-default-identity "~/.config/passage/identities") + (age-default-recipient + '("age188928acysrh9zpc3rk6q4lqmf3qlpjx3s26m6k3p6kah63gcws7sx3jeej" + "age1mhvdwh6cvd70hm5dcptc4xca2xnkf383g6706ful6kmrudwdysvq0eytym")) + :config + (setenv "PINENTRY_PROGRAM" "pinentry-gnome3") + (setenv "PASSAGE_DIR" (expand-file-name "~/pass")) + (age-file-enable)) +#+end_src + +#+begin_src emacs-lisp +(defun my/org-crypt-migrate-to-age (title) + "Migrate all :crypt: headings in the current buffer to a new .org.age file. +TITLE names the new denote-style file in ~/org." + (interactive "sTitle for new age file: ") + (unless (derived-mode-p 'org-mode) + (user-error "Not an org buffer")) + (save-excursion + (org-decrypt-entries) + (let ((collected (generate-new-buffer " *crypt-migration*")) + (count 0)) + (org-map-entries + (lambda () + (let ((beg (org-entry-beginning-position)) + (end (org-entry-end-position))) + (with-current-buffer collected + (insert (buffer-substring-no-properties beg end) "\n")) + (setq count (1+ count)))) + "crypt") + (if (zerop count) + (progn (kill-buffer collected) + (user-error "No :crypt: headings found")) + (let ((target (expand-file-name + (format "%s--%s__crypt.org.age" + (format-time-string "%Y%m%dT%H%M%S") + (replace-regexp-in-string "[^a-z0-9]+" "-" (downcase title))) + "~/org"))) + (with-current-buffer collected + (org-mode) + (write-file target) + (save-buffer)) + (goto-char (point-max)) + (org-map-entries + (lambda () + (delete-region (org-entry-beginning-position) (org-entry-end-position))) + "crypt") + (save-buffer) + (message "Migrated %d crypt headings to %s" count target)))))) +#+end_src + +** passage + +#+begin_src emacs-lisp :tangle yes +(use-package passage + :ensure (:host github :repo "anticomputer/passage.el") + :after age + :custom + (auth-source-passage-filename "~/pass") + :config + (with-eval-after-load 'evil + (evil-define-key 'normal passage-mode-map + "y" #'passage-copy + "yn" #'passage-copy-username + "yu" #'passage-copy-url + "yf" #'passage-copy-field + "J" #'passage-goto-entry + "U" #'passage-browse-url + "a" #'passage-insert + "G" #'passage-insert-generated + "R" #'passage-rename + "x" #'passage-kill + "E" #'passage-edit + "j" #'passage-next-entry + "k" #'passage-prev-entry + "gr" #'passage-update-buffer + "o" #'passage-otp-options + "]]" #'passage-next-directory + "[[" #'passage-prev-directory + (kbd "RET") #'passage-view + "q" #'passage-quit))) +#+end_src + ** org-appear #+begin_src emacs-lisp @@ -3471,61 +3570,14 @@ Native =undo-redo= (via =evil-undo-system='undo-redo=); just raise the undo limi (display-time-mode 1) #+end_src -** doom-modeline +** mode-line-maker #+begin_src emacs-lisp -(defvar-local my/doom-modeline--buffer-title nil) - -(defun my/doom-modeline--update-title-cache () - "Update the cached #+title for the current buffer." - (setq-local my/doom-modeline--buffer-title - ;; `org-collect-keywords' runs the org element parser, which - ;; errors (rx range error) outside org-mode buffers. - (and (derived-mode-p 'org-mode) - (cadar (org-collect-keywords '("TITLE")))))) - -(defun my/doom-modeline--buffer-title () - "Return the cached #+title, computing it if necessary." - (unless (local-variable-p 'my/doom-modeline--buffer-title) - (my/doom-modeline--update-title-cache)) - my/doom-modeline--buffer-title) - -(defun my/doom-modeline-set-buffer-title (&rest _) - "Override doom-modeline's cached file name with the Org #+title. -If no TITLE keyword is found, leave doom-modeline's default name." - (when-let* ((title (my/doom-modeline--buffer-title))) - (setq doom-modeline--buffer-file-name - (propertize title - 'face 'doom-modeline-buffer-file - 'mouse-face 'mode-line-highlight - 'help-echo (concat (or buffer-file-truename (buffer-name)) - "\nmouse-1: Previous buffer\nmouse-3: Next buffer") - 'local-map mode-line-buffer-identification-keymap)))) - -(defun my/doom-modeline--invalidate-title-cache () - "Invalidate the cached title so it gets recomputed on next update." - (kill-local-variable 'my/doom-modeline--buffer-title)) - -(use-package doom-modeline - :ensure t - :init (doom-modeline-mode 1) +(use-package mode-line-maker + :ensure (:host github :repo "rougier/mode-line-maker") :config - (setq doom-modeline-hud t - doom-modeline-irc t - doom-modeline-irc-buffers nil - doom-modeline-mu4e t - doom-modeline-buffer-file-name-style 'relative-to-project - doom-modeline-buffer-encoding nil - doom-modeline-time t - doom-modeline-time-live-icon t) - - ;; Replace the channel-list segment with the icon-only segment in ERC buffers. - (doom-modeline-def-modeline 'special - '(eldoc bar window-state window-number modals matches buffer-info remote-host buffer-position word-count parrot selection-info) - '(compilation objed-state misc-info battery irc debug minor-modes input-method indent-info buffer-encoding major-mode process time)) - - (advice-add #'doom-modeline-update-buffer-file-name :after #'my/doom-modeline-set-buffer-title) - (add-hook 'before-save-hook #'my/doom-modeline--invalidate-title-cache)) + (require 'my-mode-line) + (my/mode-line-install)) #+end_src * soph @@ -5237,7 +5289,7 @@ opening another file in same project does not re-notify." :commands (zfs)) #+end_src -* org-tree-slide +* COMMENT org-tree-slide #+begin_src emacs-lisp (use-package org-tree-slide diff --git a/lisp/my-mode-line.el b/lisp/my-mode-line.el new file mode 100644 index 0000000..b3179fa --- /dev/null +++ b/lisp/my-mode-line.el @@ -0,0 +1,184 @@ +;;; my-mode-line.el --- Custom mode-line on top of mode-line-maker -*- lexical-binding: t; -*- + +(require 'mode-line-maker) + +(defface my/mode-line-evil-normal + '((t (:background "#89b4fa" :foreground "#1e1e2e" :weight bold))) + "Face for the evil normal state indicator.") + +(defface my/mode-line-evil-insert + '((t (:background "#a6e3a1" :foreground "#1e1e2e" :weight bold))) + "Face for the evil insert state indicator.") + +(defface my/mode-line-evil-visual + '((t (:background "#cba6f7" :foreground "#1e1e2e" :weight bold))) + "Face for the evil visual state indicator.") + +(defface my/mode-line-evil-replace + '((t (:background "#f38ba8" :foreground "#1e1e2e" :weight bold))) + "Face for the evil replace state indicator.") + +(defface my/mode-line-evil-operator + '((t (:background "#fab387" :foreground "#1e1e2e" :weight bold))) + "Face for the evil operator state indicator.") + +(defface my/mode-line-evil-motion + '((t (:background "#94e2d5" :foreground "#1e1e2e" :weight bold))) + "Face for the evil motion state indicator.") + +(defface my/mode-line-evil-emacs + '((t (:background "#b4befe" :foreground "#1e1e2e" :weight bold))) + "Face for the evil emacs state indicator.") + +(defface my/mode-line-irc-face + '((t (:foreground "#94e2d5" :weight bold))) + "Face for the IRC activity indicator.") + +(defface my/mode-line-mu4e-face + '((t (:foreground "#f9e2af" :weight bold))) + "Face for the unread mail indicator.") + +(defface my/mode-line-time-face + '((t (:foreground "#89dceb"))) + "Face for the time segment.") + +(defface my/mode-line-state-saved-face + '((t (:foreground "#a6e3a1"))) + "Face for the saved buffer state icon.") + +(defface my/mode-line-state-modified-face + '((t (:foreground "#fab387" :weight bold))) + "Face for the modified buffer state icon.") + +(defface my/mode-line-state-read-only-face + '((t (:foreground "#f38ba8"))) + "Face for the read-only buffer state icon.") + +(defvar-local my/mode-line--buffer-title nil) + +(defun my/mode-line--update-title-cache () + "Update the cached #+title for the current buffer." + (setq-local my/mode-line--buffer-title + (and (derived-mode-p 'org-mode) + (cadar (org-collect-keywords '("TITLE")))))) + +(defun my/mode-line--buffer-title () + "Return the cached #+title, computing it if necessary." + (unless (local-variable-p 'my/mode-line--buffer-title) + (my/mode-line--update-title-cache)) + my/mode-line--buffer-title) + +(defun my/mode-line--invalidate-title-cache () + "Invalidate the cached title so it gets recomputed on next update." + (kill-local-variable 'my/mode-line--buffer-title)) + +(defun my/mode-line-buffer-name () + "Buffer name for the mode line. +Uses the Org #+title if present, otherwise the file path relative to +the project root, falling back to the plain buffer name." + (or (my/mode-line--buffer-title) + (if-let* ((file buffer-file-name) + (project (project-current)) + (root (project-root project))) + (file-relative-name file root) + (buffer-name)))) + +(defun my/mode-line--nerd-icons-p () + (and (bound-and-true-p ek-use-nerd-fonts) + (require 'nerd-icons nil t))) + +(defun my/mode-line-buffer-icon () + "Nerd icon for the current buffer, or nil." + (when (my/mode-line--nerd-icons-p) + (or (and buffer-file-name + (ignore-errors (nerd-icons-icon-for-file buffer-file-name))) + (ignore-errors (nerd-icons-icon-for-mode major-mode))))) + +(defun my/mode-line-evil-state () + "Colored single-letter indicator for the current evil state." + (when (and (boundp 'evil-state) evil-state) + (let* ((tag (upcase (substring (symbol-name evil-state) 0 1))) + (face (intern-soft (format "my/mode-line-evil-%s" evil-state)))) + (propertize (format " %s " tag) 'face (or face 'my/mode-line-evil-normal))))) + +(defun my/mode-line-buffer-state () + "Buffer state icon: lock (read-only), pencil-alert (modified), save (saved)." + (cond + (buffer-read-only + (if (my/mode-line--nerd-icons-p) + (propertize (nerd-icons-mdicon "nf-md-lock") + 'face 'my/mode-line-state-read-only-face) + "%%")) + ((buffer-modified-p) + (if (my/mode-line--nerd-icons-p) + (propertize (nerd-icons-mdicon "nf-md-content_save_alert") + 'face 'my/mode-line-state-modified-face) + "**")) + (t + (if (my/mode-line--nerd-icons-p) + (propertize (nerd-icons-mdicon "nf-md-content_save") + 'face 'my/mode-line-state-saved-face) + "--")))) + +(defun my/mode-line-irc () + "IRC activity indicator: icon plus number of buffers with unread activity." + (when (bound-and-true-p erc-modified-channels-alist) + (let ((icon (and (my/mode-line--nerd-icons-p) + (nerd-icons-mdicon "nf-md-message_badge_outline")))) + (propertize (format "%s %d" (or icon "irc") + (length erc-modified-channels-alist)) + 'face 'my/mode-line-irc-face)))) + +(defun my/mode-line-mu4e () + "Unread mail indicator from mu4e-alert, or nil when mu4e is not in use." + (when (bound-and-true-p mu4e-alert-mode-line) + (let ((icon (and (my/mode-line--nerd-icons-p) + (nerd-icons-mdicon "nf-md-email_alert_outline")))) + (propertize (format "%s %s" (or icon "mail") + (string-trim mu4e-alert-mode-line)) + 'face 'my/mode-line-mu4e-face)))) + +(defvar my/mode-line--clock-icons + ["nf-md-clock_time_twelve" "nf-md-clock_time_one" "nf-md-clock_time_two" + "nf-md-clock_time_three" "nf-md-clock_time_four" "nf-md-clock_time_five" + "nf-md-clock_time_six" "nf-md-clock_time_seven" "nf-md-clock_time_eight" + "nf-md-clock_time_nine" "nf-md-clock_time_ten" "nf-md-clock_time_eleven"] + "Nerd icon clock faces indexed by hour modulo 12.") + +(defun my/mode-line-time () + "Time with a live clock icon showing the current hour." + (when (bound-and-true-p display-time-string) + (let* ((hour (% (string-to-number (format-time-string "%I")) 12)) + (icon (and (my/mode-line--nerd-icons-p) + (nerd-icons-mdicon (aref my/mode-line--clock-icons hour)))) + (time (string-trim display-time-string))) + (propertize (if icon (concat icon " " time) time) + 'face 'my/mode-line-time-face)))) + +(defun my/mode-line-install () + "Install the custom mode-line built with mode-line-maker. +The mode-line lives at the top of the window via `header-line-format'; +the bottom mode-line is hidden." + (setq global-mode-string (delq 'display-time-string global-mode-string)) + (setq-default mode-line-format nil) + (setq-default header-line-format + (mode-line-maker + '((:eval (my/mode-line-evil-state)) " " + (:eval (my/mode-line-buffer-state)) mode-line-remote " " + (:eval (my/mode-line-buffer-icon)) " " + (:eval (propertize (my/mode-line-buffer-name) + 'face 'mode-line-buffer-id + 'mouse-face 'mode-line-highlight + 'help-echo (concat (or buffer-file-truename (buffer-name)) + "\nmouse-1: Previous buffer\nmouse-3: Next buffer") + 'local-map mode-line-buffer-identification-keymap)) " " + mode-line-position) + '("" (:eval (my/mode-line-irc)) " " + (:eval (my/mode-line-mu4e)) " " + mode-line-process " " + (:eval (my/mode-line-time)) " " + mode-name " "))) + (add-hook 'before-save-hook #'my/mode-line--invalidate-title-cache)) + +(provide 'my-mode-line) +;;; my-mode-line.el ends here |
