Eric MacAdie: Emacs Carnival: Are keyboards and M-x here to stay?
-1:-- Emacs Carnival: Are keyboards and M-x here to stay? (Post Eric MacAdie)--L0--C0--2026-10-26T03:17:55.000Z
-1:-- Emacs Carnival: Are keyboards and M-x here to stay? (Post Eric MacAdie)--L0--C0--2026-10-26T03:17:55.000Z
I'm preparing for a conversation with linkarzu. It would be fun to quote snippets from some of the other videos he has. I like the way he does a lot of reaction videos that build on what people have shared. He tends to listen to things at 1.25 or 1.5x speed. I'm not sure if I have the patience even for that, so it'd be nice to be able to just quickly skim the text. He also clips a lot of his own videos. It might be interesting to play around with my workflows for listening to snippets and clipping them.
I've grabbed YouTube captions and reformatted them before:
This time around, I want to get the VTT format so that I can possibly use them with subed-record to clip audio or video.
From a YouTube link, I want to run a single command that gets me a VTT file with that as the media source and with the captions. If there are any chapters in the description, it would be lovely to get them too. I can get them from the JSON, yay!
Now I can use M-x sacha-subed-youtube to open the VTT file, which is cached locally, and I can use M-x sacha-subed-youtube-region-as-quote after selecting a section. Well, actually, I have an Embark command s (for "subed") that I can run on a YouTube video link and a selected-mode shortcut for y that I can run when the region is selected. Results:
I Asked Prot If I Should Switch to Emacs
00:09:46: I think the value of Org is that it is in the context of Emacs, which can now do other things. It can be your email client. It can be your agenda. Of course, it can be your programming environment. All these sorts of things, like your RSS feed reader and so on. And in the context of all that, you also have Org. So for me, that's kind of the value. But otherwise, having Org elsewhere, it's great still, because the paradigm of Org, I think, is a portable paradigm.
Org Mode in Neovim Is Finally Here - Full org.nvim Demo
Code for subed + YouTube00:00:00: I think that I achieved like 100% of parity with the original Org Mode version on Emacs.
(defvar sacha-subed-youtube-dir "~/proj/youtube-captions"
"Directory with YouTube captions.")
(defvar sacha-subed-youtube-temp-dir "~/Downloads" "Snap installations need a workaround for permission.")
;;;###autoload
(defun sacha-subed-youtube (url)
"Download the automatic subtitles or check my cache."
(interactive "MURL: ")
(let* ((id (sacha-org-yt-id url))
(existing (car (directory-files sacha-subed-youtube-dir t
(concat "^" (regexp-quote id) ".*\\.vtt\\'")))))
(if existing
(progn
(find-file existing)
(subed-mpv-play-from-url url))
;; Download
(let ((directory (make-temp-file (expand-file-name "yt-dlp" sacha-subed-youtube-temp-dir) t))
filename
json
match
chapters
subtitles)
(with-current-buffer (get-buffer-create "*yt-dlp*")
(let ((default-directory directory))
(call-process
"yt-dlp" nil t nil
"--skip-download"
"--write-info-json"
"--sub-format"
"vtt"
"--write-subs"
"--write-auto-subs"
url)))
(when (null (directory-files directory t "\\.json$"))
(error "Could not get data."))
(setq json (json-read-file (car (directory-files directory t "\\.json$"))))
(setq chapters (alist-get 'chapters json))
(setq filename (expand-file-name
(concat id " - " (alist-get 'title json) ".vtt")
sacha-subed-youtube-dir))
(setq subtitles (subed-parse-file (car (directory-files
directory t
"\\.vtt$"))))
(delete-directory directory t)
;; add the chapters as comments
(mapc
(lambda (sub)
(when (and chapters
(>= (elt sub 1)
(* (alist-get 'start_time (car chapters)) 1000)))
(setf (elt sub 4) (alist-get 'title (pop chapters)))))
subtitles)
(subed-create-file
filename
subtitles
t)
(find-file filename)
(subed-mpv-play-from-url url)))))
(defun sacha-subed-youtube-id ()
"Return the YouTube ID."
(or (save-restriction
(save-excursion
(goto-char (point-min))
(subed-forward-subtitle-text)
(when (subed-record-get-directive "YOUTUBE_URL")
(sacha-org-yt-id
(subed-record-get-directive "YOUTUBE_URL")))))
(car (split-string (file-name-base (buffer-file-name)) " "))))
;; Now we just need a quick way to copy a YouTube URL.
;;;###autoload
(defun sacha-subed-youtube-url (&optional start-msecs end-msecs)
"Copy YouTube URL for the current timestamp."
(interactive)
(when-let*
((id (sacha-subed-youtube-id))
(s
(if end-msecs
(format "https://youtu.be/%s?t=%d&end=%d"
id
(/ (or start-msecs (subed-subtitle-msecs-start))
1000)
(1+ (/ end-msecs
1000)))
(format "https://youtu.be/%s?t=%d"
id
(/ (or start-msecs (subed-subtitle-msecs-start))
1000)))))
(when (called-interactively-p 'any)
(kill-new s)
(message "%s" s))
s))
;;;###autoload
(defun sacha-subed-youtube-play ()
"Play this video from YouTube."
(interactive)
(when-let*
((id (sacha-subed-youtube-id))
(s (concat "https://youtu.be/" id)))
(when (string= subed-mpv-media-file s)
(subed-mpv-play-from-url s))
(subed-mpv-jump-to-current-subtitle)
(subed-mpv-unpause)))
;;;###autoload
(defun sacha-subed-youtube-region-as-quote (beg end &optional label)
"Format this as a quote."
(interactive (list (region-beginning) (region-end) (when current-prefix-arg (read-string "Label: "))))
(let* ((id (sacha-subed-youtube-id))
(url (sacha-subed-youtube-url
(save-excursion
(goto-char beg)
(subed-subtitle-msecs-start))
(save-excursion
(goto-char end)
(subed-subtitle-msecs-stop))))
(base (file-name-base (buffer-file-name)))
(text (subed-subtitle-list-text (subed-subtitle-list beg end)))
(s (format
"%s\n#+begin_quote\n%s: %s\n#+end_quote\n"
(org-link-make-string
(concat "yt:" url)
(or label
(and (string-match (format "^%s - \\(.+\\)" (regexp-quote id)) base)
(match-string 1 base))
(sacha-page-title url)))
(org-link-make-string
(concat "vtime:"
(subed-msecs-to-timestamp
(save-excursion
(goto-char beg)
(subed-subtitle-msecs-start))
t)))
(with-temp-buffer
(insert text)
(let ((fill-column most-positive-fixnum))
(fill-region (point-min) (point-max))
(string-trim (buffer-string)))))))
(when (called-interactively-p 'any)
(kill-new s)
(message "%s" s))
s))
Some of this code hasn't been committed to subed yet, but I will commit it sometime this week.
My next steps might be to:
You can e-mail me at sacha@sachachua.com.
-1:-- subed.el and quoting snippets of YouTube videos from the auto captions (Post Sacha Chua)--L0--C0--2026-10-26T02:11:17.000Z
Martin Schell has an excellent post on building and using Org capture templates. What’s interesting about his approach is that his template prompts for certain information—customer ID and site location in his example—captures that information in some local variables, and uses those variables to customize the capture for the particular customer.
His two main tools for doing this are:
%^{PROMPT|A|B|...} placeholder to prompt for data.%(EXP) placeholder to execute the Elisp expression EXP.
He wrote some helper functions to deal with setting and accessing local variables. The first, ms/set-var, prompts the user for the value of a variable, creates the variable, sets its value, and returns the value. This is different from the %^{...} placeholder in that it captures the value of the user’s answer for later use. The other helper functions make use of the captured variables.
Take a look at Schell’s post to see the functions and how the whole thing hangs together. His needs may be more complicated than normal but the ideas he uses to solve them are handy and applicable to many—even simpler—situations.
I’ve used all the placeholders that uses in my own templates but the idea of capturing a variable for later reuse in the template is new to me. It’s a neat idea and definitely worth knowing.
-1:-- Variables In Org Capture (Post Irreal)--L0--C0--2026-10-25T14:39:20.000Z
While summarizing my history with Emacs, I was curious about just how many Emacs News posts I've written and how many links I've highlighted. 564 posts, > 34000 items, an average of 60 items per posts.
Analysis: It turned out that my emacs-news/index.org only went back to about 2016, missing a few months of 2015. To fill in the data, I needed to convert the old HTML files into Org format.
find -name \*news\*.html -exec zsh -c 'pandoc -f html -t org {} --wrap=none -o $(basename {} .html).org' \;
Then I inserted the old entries into my file.
insert-file-contents doesn't seem to move the point, but if I narrow to my Posts subtree (org-narrow-to-subtree), then I can go to the end of the narrowed part.
(defun sacha-emacs-news-insert-old-ones ()
(interactive)
(let ((dir "~/proj/static-blog/blog/2015/"))
(dolist (f
(sort
(directory-files-recursively dir "news.*org$")
:key #'file-name-base
:reverse t))
(insert "** " (substring (file-name-base f) 0 10) " Emacs News\n\n")
(insert-file-contents f)
(goto-char (point-max)))))
With all the entries in place, let's look at the list items that start with a link.
code for analysis;;;###autoload
(defun sacha-emacs-news-count-links ()
"Return the number of main links in this subtree."
(interactive)
(let ((count 0))
(save-excursion
(org-back-to-heading)
(while (re-search-forward "^ *- \\[\\[" (save-excursion (org-end-of-subtree)) t)
(incf count)))
(when (called-interactively-p 'any)
(message "%d" count))
count))
;;;###autoload
(defun sacha-emacs-news-analyze ()
"Count how many entries and items. Make a table of entries."
(interactive)
(with-current-buffer (find-file-noselect "~/sync/emacs-news/index.org")
(goto-char (point-min))
(let (results)
(org-map-entries
(lambda ()
(let ((title (org-entry-get (point) "ITEM")))
(when (string-match "\\([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\\) Emacs" title)
(push (list (match-string 1 title)
(sacha-emacs-news-count-links))
results)))))
(seq-uniq (sort results :key 'car :reverse t)))))
(append '(("Date" "Count") hline) (sacha-emacs-news-analyze))
set title "Number of links in Emacs News per week"
set xdata time
set timefmt "%Y-%m-%d"
set format x "%Y"
set xlabel "Year"
set ylabel "Count"
set grid
plot data using 1:2 with lines title "Items"
This probably tracks my availability more than anything else. The dip in 2017 and 2018 corresponds pretty well to the time that the kiddo needed more of my attention. Babies sleep a lot; toddlers, not so much.
I generally feel that there are so many interesting rabbit holes to go down each week, and the Emacs community is active enough that there's always one or two things I want to add to my to-do list to explore further. Putting together Emacs News is one of the highlights of my week. It takes me about 1 - 2 hours, and some of that is just because I get distracted trying out the cool stuff I come across.
Learning from similar newsletters: OCaml Weekly News has a single author as well and does a lot more summarization, but includes fewer links. This Week in Rust is a similar link-dump weekly newsletter. It's written by a larger team and it has more commentary. I think that for the Emacs community, I'm still leaning towards this large list of categorized links for easy skimming. As I get the hang of writing more, I'd like to write more blog posts myself that Emacs News can also link to. If your community doesn't already have a weekly or monthly newsletter, I highly recommend it as a way to bring the community together.
Here's the table, in case you're curious:
Data| Date | Count |
|---|---|
| 2026-10-19 | 74 |
| 2026-10-12 | 74 |
| 2026-10-05 | 72 |
| 2026-09-28 | 55 |
| 2026-09-21 | 50 |
| 2026-09-14 | 53 |
| 2026-09-07 | 73 |
| 2026-08-31 | 86 |
| 2026-08-24 | 75 |
| 2026-08-17 | 71 |
| 2026-08-10 | 66 |
| 2026-08-03 | 74 |
| 2026-07-27 | 74 |
| 2026-07-20 | 99 |
| 2026-07-13 | 50 |
| 2026-07-06 | 78 |
| 2026-06-29 | 60 |
| 2026-06-22 | 72 |
| 2026-06-15 | 66 |
| 2026-06-08 | 93 |
| 2026-06-01 | 56 |
| 2026-05-25 | 56 |
| 2026-05-18 | 57 |
| 2026-05-11 | 94 |
| 2026-05-04 | 88 |
| 2026-04-27 | 71 |
| 2026-04-20 | 91 |
| 2026-04-13 | 86 |
| 2026-04-06 | 84 |
| 2026-03-30 | 69 |
| 2026-03-23 | 61 |
| 2026-03-16 | 75 |
| 2026-03-09 | 83 |
| 2026-03-02 | 86 |
| 2026-02-23 | 96 |
| 2026-02-16 | 89 |
| 2026-02-09 | 87 |
| 2026-02-02 | 109 |
| 2026-01-26 | 59 |
| 2026-01-19 | 84 |
| 2026-01-12 | 86 |
| 2026-01-05 | 88 |
| 2026-01-04 | 59 |
| 2025-12-29 | 63 |
| 2025-12-22 | 99 |
| 2025-12-15 | 65 |
| 2025-12-08 | 74 |
| 2025-12-01 | 62 |
| 2025-11-24 | 71 |
| 2025-11-17 | 57 |
| 2025-11-10 | 76 |
| 2025-11-03 | 69 |
| 2025-10-27 | 64 |
| 2025-10-20 | 51 |
| 2025-10-13 | 57 |
| 2025-10-06 | 62 |
| 2025-09-29 | 64 |
| 2025-09-22 | 66 |
| 2025-09-15 | 71 |
| 2025-09-08 | 61 |
| 2025-09-01 | 74 |
| 2025-08-25 | 82 |
| 2025-08-18 | 69 |
| 2025-08-11 | 84 |
| 2025-08-04 | 53 |
| 2025-07-28 | 82 |
| 2025-07-21 | 70 |
| 2025-07-14 | 65 |
| 2025-07-07 | 56 |
| 2025-06-30 | 67 |
| 2025-06-23 | 63 |
| 2025-06-16 | 62 |
| 2025-06-09 | 50 |
| 2025-06-02 | 68 |
| 2025-05-26 | 61 |
| 2025-05-19 | 74 |
| 2025-05-12 | 58 |
| 2025-05-05 | 57 |
| 2025-04-28 | 96 |
| 2025-04-21 | 70 |
| 2025-04-14 | 99 |
| 2025-04-07 | 64 |
| 2025-03-31 | 83 |
| 2025-03-24 | 74 |
| 2025-03-17 | 94 |
| 2025-03-10 | 102 |
| 2025-03-03 | 102 |
| 2025-02-24 | 74 |
| 2025-02-17 | 85 |
| 2025-02-10 | 64 |
| 2025-02-03 | 84 |
| 2025-01-27 | 78 |
| 2025-01-20 | 80 |
| 2025-01-13 | 59 |
| 2025-01-06 | 66 |
| 2024-12-30 | 92 |
| 2024-12-23 | 85 |
| 2024-12-16 | 72 |
| 2024-12-09 | 82 |
| 2024-12-02 | 63 |
| 2024-11-25 | 75 |
| 2024-11-18 | 67 |
| 2024-11-11 | 59 |
| 2024-11-04 | 73 |
| 2024-10-28 | 65 |
| 2024-10-21 | 52 |
| 2024-10-14 | 72 |
| 2024-10-07 | 80 |
| 2024-09-30 | 56 |
| 2024-09-23 | 93 |
| 2024-09-16 | 85 |
| 2024-09-09 | 52 |
| 2024-09-02 | 67 |
| 2024-08-26 | 56 |
| 2024-08-19 | 62 |
| 2024-08-12 | 48 |
| 2024-08-05 | 45 |
| 2024-07-29 | 51 |
| 2024-07-22 | 56 |
| 2024-07-15 | 60 |
| 2024-07-08 | 68 |
| 2024-07-01 | 67 |
| 2024-06-24 | 77 |
| 2024-06-17 | 86 |
| 2024-06-10 | 82 |
| 2024-06-03 | 79 |
| 2024-05-27 | 71 |
| 2024-05-20 | 66 |
| 2024-05-13 | 91 |
| 2024-05-06 | 73 |
| 2024-04-29 | 84 |
| 2024-04-22 | 63 |
| 2024-04-15 | 94 |
| 2024-04-08 | 67 |
| 2024-04-01 | 95 |
| 2024-03-25 | 91 |
| 2024-03-18 | 78 |
| 2024-03-11 | 79 |
| 2024-03-04 | 84 |
| 2024-02-26 | 72 |
| 2024-02-19 | 69 |
| 2024-02-12 | 78 |
| 2024-02-05 | 70 |
| 2024-01-29 | 96 |
| 2024-01-22 | 83 |
| 2024-01-15 | 70 |
| 2024-01-08 | 99 |
| 2024-01-01 | 76 |
| 2023-12-25 | 77 |
| 2023-12-18 | 74 |
| 2023-12-11 | 66 |
| 2023-12-04 | 56 |
| 2023-11-27 | 85 |
| 2023-11-20 | 76 |
| 2023-11-13 | 62 |
| 2023-11-06 | 52 |
| 2023-10-30 | 59 |
| 2023-10-23 | 69 |
| 2023-10-16 | 91 |
| 2023-10-09 | 93 |
| 2023-10-02 | 82 |
| 2023-09-25 | 73 |
| 2023-09-18 | 72 |
| 2023-09-11 | 90 |
| 2023-09-04 | 79 |
| 2023-08-28 | 83 |
| 2023-08-21 | 78 |
| 2023-08-14 | 84 |
| 2023-08-07 | 49 |
| 2023-07-31 | 69 |
| 2023-07-24 | 46 |
| 2023-07-17 | 53 |
| 2023-07-10 | 52 |
| 2023-07-03 | 67 |
| 2023-06-26 | 59 |
| 2023-06-19 | 53 |
| 2023-06-12 | 41 |
| 2023-06-05 | 52 |
| 2023-05-29 | 49 |
| 2023-05-22 | 64 |
| 2023-05-15 | 66 |
| 2023-05-08 | 57 |
| 2023-05-01 | 73 |
| 2023-04-24 | 67 |
| 2023-04-17 | 66 |
| 2023-04-10 | 49 |
| 2023-04-03 | 76 |
| 2023-03-27 | 92 |
| 2023-03-20 | 65 |
| 2023-03-13 | 85 |
| 2023-03-06 | 64 |
| 2023-02-27 | 62 |
| 2023-02-20 | 64 |
| 2023-02-13 | 65 |
| 2023-02-06 | 67 |
| 2023-01-30 | 70 |
| 2023-01-23 | 85 |
| 2023-01-16 | 83 |
| 2023-01-09 | 83 |
| 2023-01-02 | 89 |
| 2022-12-26 | 89 |
| 2022-12-19 | 83 |
| 2022-12-12 | 92 |
| 2022-12-05 | 69 |
| 2022-11-28 | 90 |
| 2022-11-21 | 82 |
| 2022-11-14 | 64 |
| 2022-11-07 | 75 |
| 2022-10-31 | 74 |
| 2022-10-24 | 37 |
| 2022-10-17 | 54 |
| 2022-10-10 | 77 |
| 2022-10-03 | 81 |
| 2022-09-26 | 75 |
| 2022-09-19 | 75 |
| 2022-09-12 | 77 |
| 2022-09-05 | 67 |
| 2022-08-29 | 73 |
| 2022-08-22 | 50 |
| 2022-08-15 | 45 |
| 2022-08-08 | 65 |
| 2022-08-01 | 69 |
| 2022-07-25 | 60 |
| 2022-07-18 | 55 |
| 2022-07-11 | 75 |
| 2022-07-04 | 56 |
| 2022-06-27 | 51 |
| 2022-06-20 | 60 |
| 2022-06-13 | 67 |
| 2022-06-06 | 65 |
| 2022-05-30 | 38 |
| 2022-05-23 | 68 |
| 2022-05-16 | 65 |
| 2022-05-09 | 67 |
| 2022-05-02 | 82 |
| 2022-04-25 | 59 |
| 2022-04-18 | 71 |
| 2022-04-11 | 69 |
| 2022-04-04 | 63 |
| 2022-03-28 | 67 |
| 2022-03-21 | 71 |
| 2022-03-14 | 64 |
| 2022-03-07 | 68 |
| 2022-02-28 | 65 |
| 2022-02-21 | 77 |
| 2022-02-14 | 88 |
| 2022-02-07 | 49 |
| 2022-01-31 | 75 |
| 2022-01-24 | 74 |
| 2022-01-17 | 83 |
| 2022-01-10 | 57 |
| 2022-01-03 | 67 |
| 2021-12-27 | 47 |
| 2021-12-20 | 56 |
| 2021-12-13 | 57 |
| 2021-12-06 | 69 |
| 2021-11-29 | 75 |
| 2021-11-22 | 55 |
| 2021-11-15 | 56 |
| 2021-11-08 | 94 |
| 2021-11-01 | 79 |
| 2021-10-25 | 66 |
| 2021-10-18 | 77 |
| 2021-10-11 | 70 |
| 2021-10-04 | 91 |
| 2021-09-27 | 86 |
| 2021-09-20 | 63 |
| 2021-09-13 | 79 |
| 2021-09-06 | 63 |
| 2021-08-30 | 68 |
| 2021-08-23 | 64 |
| 2021-08-16 | 77 |
| 2021-08-09 | 69 |
| 2021-08-02 | 59 |
| 2021-07-26 | 77 |
| 2021-07-19 | 65 |
| 2021-07-12 | 48 |
| 2021-07-05 | 64 |
| 2021-06-28 | 46 |
| 2021-06-21 | 65 |
| 2021-06-14 | 37 |
| 2021-06-07 | 65 |
| 2021-05-31 | 65 |
| 2021-05-24 | 46 |
| 2021-05-17 | 65 |
| 2021-05-10 | 58 |
| 2021-05-03 | 47 |
| 2021-04-26 | 57 |
| 2021-04-19 | 73 |
| 2021-04-12 | 52 |
| 2021-04-05 | 70 |
| 2021-03-29 | 65 |
| 2021-03-22 | 82 |
| 2021-03-15 | 64 |
| 2021-03-08 | 70 |
| 2021-03-01 | 55 |
| 2021-02-22 | 84 |
| 2021-02-15 | 96 |
| 2021-02-08 | 74 |
| 2021-02-01 | 73 |
| 2021-01-25 | 71 |
| 2021-01-18 | 58 |
| 2021-01-11 | 66 |
| 2021-01-04 | 76 |
| 2020-12-28 | 72 |
| 2020-12-21 | 73 |
| 2020-12-14 | 92 |
| 2020-12-07 | 76 |
| 2020-11-30 | 65 |
| 2020-11-23 | 51 |
| 2020-11-16 | 47 |
| 2020-11-09 | 57 |
| 2020-11-02 | 72 |
| 2020-10-26 | 51 |
| 2020-10-19 | 76 |
| 2020-10-12 | 70 |
| 2020-10-05 | 57 |
| 2020-09-28 | 71 |
| 2020-09-21 | 81 |
| 2020-09-14 | 79 |
| 2020-09-07 | 61 |
| 2020-08-31 | 64 |
| 2020-08-24 | 73 |
| 2020-08-17 | 80 |
| 2020-08-10 | 89 |
| 2020-08-03 | 70 |
| 2020-07-27 | 56 |
| 2020-07-20 | 60 |
| 2020-07-13 | 55 |
| 2020-07-06 | 48 |
| 2020-06-29 | 43 |
| 2020-06-22 | 56 |
| 2020-06-15 | 63 |
| 2020-06-08 | 66 |
| 2020-06-01 | 116 |
| 2020-05-25 | 64 |
| 2020-05-18 | 83 |
| 2020-05-11 | 62 |
| 2020-05-04 | 77 |
| 2020-04-27 | 63 |
| 2020-04-20 | 63 |
| 2020-04-13 | 62 |
| 2020-04-06 | 49 |
| 2020-03-30 | 62 |
| 2020-03-23 | 58 |
| 2020-03-16 | 52 |
| 2020-03-09 | 50 |
| 2020-03-02 | 43 |
| 2020-02-24 | 53 |
| 2020-02-17 | 65 |
| 2020-02-10 | 60 |
| 2020-02-03 | 67 |
| 2020-01-27 | 68 |
| 2020-01-20 | 65 |
| 2020-01-13 | 61 |
| 2020-01-06 | 70 |
| 2019-12-30 | 51 |
| 2019-12-23 | 76 |
| 2019-12-16 | 71 |
| 2019-12-09 | 53 |
| 2019-12-02 | 51 |
| 2019-11-25 | 57 |
| 2019-11-18 | 55 |
| 2019-11-11 | 62 |
| 2019-11-04 | 70 |
| 2019-10-28 | 32 |
| 2019-10-21 | 59 |
| 2019-10-14 | 76 |
| 2019-10-07 | 63 |
| 2019-09-30 | 68 |
| 2019-09-23 | 88 |
| 2019-09-16 | 51 |
| 2019-09-09 | 55 |
| 2019-09-02 | 47 |
| 2019-08-26 | 61 |
| 2019-08-19 | 84 |
| 2019-08-12 | 79 |
| 2019-08-05 | 58 |
| 2019-07-29 | 79 |
| 2019-07-22 | 96 |
| 2019-07-15 | 78 |
| 2019-07-08 | 49 |
| 2019-07-01 | 86 |
| 2019-06-24 | 76 |
| 2019-06-17 | 63 |
| 2019-06-10 | 51 |
| 2019-06-03 | 92 |
| 2019-05-27 | 69 |
| 2019-05-20 | 86 |
| 2019-05-13 | 100 |
| 2019-05-06 | 72 |
| 2019-04-29 | 92 |
| 2019-04-22 | 88 |
| 2019-04-15 | 97 |
| 2019-04-08 | 88 |
| 2019-04-01 | 94 |
| 2019-03-25 | 85 |
| 2019-03-18 | 53 |
| 2019-03-11 | 29 |
| 2019-03-04 | 70 |
| 2019-02-25 | 80 |
| 2019-02-18 | 72 |
| 2019-02-11 | 52 |
| 2019-02-04 | 65 |
| 2019-01-28 | 29 |
| 2019-01-21 | 27 |
| 2019-01-14 | 41 |
| 2019-01-07 | 63 |
| 2018-12-31 | 49 |
| 2018-12-24 | 41 |
| 2018-12-17 | 40 |
| 2018-12-10 | 43 |
| 2018-12-03 | 42 |
| 2018-11-26 | 40 |
| 2018-11-19 | 42 |
| 2018-11-12 | 58 |
| 2018-11-05 | 38 |
| 2018-10-29 | 31 |
| 2018-10-22 | 37 |
| 2018-10-15 | 31 |
| 2018-10-08 | 28 |
| 2018-10-01 | 49 |
| 2018-09-24 | 33 |
| 2018-09-17 | 28 |
| 2018-09-03 | 84 |
| 2018-08-27 | 23 |
| 2018-08-20 | 24 |
| 2018-08-13 | 26 |
| 2018-08-06 | 25 |
| 2018-07-30 | 24 |
| 2018-07-23 | 27 |
| 2018-07-16 | 25 |
| 2018-07-09 | 35 |
| 2018-07-02 | 26 |
| 2018-06-25 | 36 |
| 2018-06-18 | 35 |
| 2018-06-11 | 33 |
| 2018-06-04 | 56 |
| 2018-05-28 | 28 |
| 2018-05-21 | 33 |
| 2018-05-14 | 29 |
| 2018-05-07 | 33 |
| 2018-04-30 | 41 |
| 2018-04-23 | 40 |
| 2018-04-16 | 31 |
| 2018-04-09 | 44 |
| 2018-04-02 | 20 |
| 2018-03-26 | 22 |
| 2018-03-20 | 29 |
| 2018-03-12 | 37 |
| 2018-03-04 | 55 |
| 2018-02-26 | 23 |
| 2018-02-19 | 39 |
| 2018-02-12 | 20 |
| 2018-02-05 | 48 |
| 2018-01-29 | 26 |
| 2018-01-23 | 48 |
| 2018-01-15 | 18 |
| 2018-01-09 | 30 |
| 2018-01-01 | 26 |
| 2017-12-26 | 30 |
| 2017-12-19 | 28 |
| 2017-12-11 | 20 |
| 2017-12-04 | 41 |
| 2017-11-27 | 20 |
| 2017-11-20 | 34 |
| 2017-11-13 | 35 |
| 2017-11-06 | 35 |
| 2017-10-30 | 28 |
| 2017-10-23 | 28 |
| 2017-08-21 | 40 |
| 2017-08-14 | 55 |
| 2017-08-07 | 29 |
| 2017-07-31 | 53 |
| 2017-07-24 | 52 |
| 2017-07-17 | 54 |
| 2017-07-10 | 47 |
| 2017-07-03 | 35 |
| 2017-06-26 | 50 |
| 2017-06-19 | 21 |
| 2017-06-12 | 24 |
| 2017-06-05 | 33 |
| 2017-05-29 | 32 |
| 2017-05-22 | 35 |
| 2017-05-15 | 35 |
| 2017-05-08 | 40 |
| 2017-05-01 | 33 |
| 2017-04-24 | 32 |
| 2017-04-17 | 35 |
| 2017-04-10 | 41 |
| 2017-04-03 | 35 |
| 2017-03-27 | 31 |
| 2017-03-20 | 40 |
| 2017-03-13 | 41 |
| 2017-03-06 | 38 |
| 2017-02-27 | 44 |
| 2017-02-20 | 35 |
| 2017-02-06 | 23 |
| 2017-01-30 | 30 |
| 2017-01-23 | 43 |
| 2017-01-16 | 32 |
| 2017-01-13 | 55 |
| 2017-01-09 | 34 |
| 2017-01-02 | 25 |
| 2016-12-26 | 37 |
| 2016-12-19 | 44 |
| 2016-12-12 | 36 |
| 2016-12-05 | 37 |
| 2016-11-28 | 27 |
| 2016-11-21 | 34 |
| 2016-11-14 | 50 |
| 2016-11-07 | 40 |
| 2016-10-31 | 35 |
| 2016-10-24 | 35 |
| 2016-10-17 | 36 |
| 2016-10-10 | 34 |
| 2016-10-03 | 41 |
| 2016-09-26 | 32 |
| 2016-09-19 | 39 |
| 2016-09-12 | 27 |
| 2016-09-05 | 32 |
| 2016-08-29 | 78 |
| 2016-08-22 | 19 |
| 2016-08-15 | 17 |
| 2016-08-08 | 19 |
| 2016-08-01 | 32 |
| 2016-07-25 | 33 |
| 2016-07-18 | 26 |
| 2016-07-10 | 32 |
| 2016-07-04 | 48 |
| 2016-06-27 | 32 |
| 2016-06-20 | 33 |
| 2016-06-13 | 34 |
| 2016-06-06 | 29 |
| 2016-05-30 | 39 |
| 2016-05-23 | 47 |
| 2016-05-16 | 43 |
| 2016-05-09 | 60 |
| 2016-05-02 | 45 |
| 2016-04-25 | 40 |
| 2016-04-18 | 35 |
| 2016-04-11 | 40 |
| 2016-04-04 | 42 |
| 2016-03-28 | 47 |
| 2016-03-21 | 42 |
| 2016-03-14 | 93 |
| 2016-02-22 | 72 |
| 2016-02-15 | 55 |
| 2016-02-15 | 55 |
| 2016-02-08 | 57 |
| 2016-02-01 | 55 |
| 2016-01-25 | 55 |
| 2016-01-18 | 37 |
| 2016-01-11 | 51 |
| 2015-12-28 | 55 |
| 2015-12-21 | 33 |
| 2015-12-14 | 45 |
| 2015-12-07 | 50 |
| 2015-11-30 | 56 |
| 2015-11-23 | 47 |
| 2015-11-16 | 54 |
| 2015-11-09 | 40 |
| 2015-11-02 | 25 |
| 2015-10-26 | 32 |
You can e-mail me at sacha@sachachua.com.
-1:-- Analyzing Emacs News: 564 posts over 11 years, > 34k items, ~60/post (Post Sacha Chua)--L0--C0--2026-10-25T13:15:33.000Z
These are my minimalist themes. They use few colours and will appear mostly monochromatic in many contexts. Styles involve the careful use of typography, such as italics and bold italics.
If you want maximalist themes in terms of colour, check my ef-themes
package. For something in-between, which I consider the best “default
theme” for a text editor, opt for my modus-themes.
doric-themesBelow are the release notes.
doric-meadow and doric-mountain themesdoric-meadow is a light theme with yellow and green colours, while
doric-mountain has a similar albeit dark palette. They expand the
collection of the Doric themes to cover even more styles and
preferences.
I improved the inter-theme consistency of all colours that apply to
diffs, such as those shown in diff-mode, ediff, and magit buffers.
Before, some themes were needlessly more subtle than others while
certain colours could be hard to discern in some situations. For
example, if the diff-hl package was in use and the fringe was set to
a thin line.
Other packages also benefit from this review, such as my pulsar and
lin.
The highlight of matching parentheses produced by show-paren-mode is
now distinct from the active region. The same is true for the current
line, per hl-line-mode.
doric-themes-define-theme should not fail in certain casesI fixed an issue that was reported to me by Amin Bandali where the
doric-themes-define-theme was failing to instantiate a theme in the
macroexpanded code.
bg-accent and fg-accent palette entries are renamedThis is relevant for those who rely on doric-themes-with-colors. The
new names are bg-accent-primary and fg-accent-primary. The old
ones will not longer work.
🔷 Consider making a donation.
-1:-- Emacs: doric-themes version 1.4.0 (Post Protesilaos)--L0--C0--2026-10-25T00:00:00.000Z
When you hover the mouse pointer over a link in Org, Emacs displays a tooltip that shows the link's URL.0 This mimics the behavior of a web browser that displays the URL in the status bar.
Within Emacs, I try to avoid the mouse as much as possible. I already configured Emacs so that pressing ENTER on a link follows the link.1 But this method doesn't provide a way to preview the link before following it. I wondered, "What if Emacs displayed a tooltip whenever point is on a link?"
The answer came to me immediately; "It would be darn annoying."
So I decided that the user should invoke a function to "display tooltip at point." It didn't take long for me to find a solution on Stack Exchange, "farynaio/org-link-copy."2 The function's primary purpose is to add the URL to the kill-ring. But it also shows the URL in a message that indicates the URL was copied. It works perfectly.
However, the tricky part was binding a key to the function. It was
suggested that one could bind C-x C-l3 to the
function with this statement:
(define-key org-mode-map (kbd "C-x C-l") 'farynaio/org-link-copy)
Unfortunately, when I included it in my init file, it generated the error "Symbol's value as variable is void: org-mode-map" even while it worked after Org was enabled.
Eventually, after trying (unsuccessfully) to add the assignment to
org-mode-hook, I found it easy to include this statement above the
define-key statement: (require 'org-keys).
This provides a definition of org-mode-map, which is provided
in org-keys.el.
As Emacs is very keyboard-oriented, this little addition allowed my fingers (and thumbs) to stay on the keys.
Recall that my original objective was to display a tooltip, not a message. However, a tooltip would appear at the location of the mouse pointer, not at point, even if the mouse is hovering over another program's window. Just try it yourself:
(tooltip-show-help "MSG")
But this disadvantageous behavior of tooltip-show-help
could be useful if you wanted to locate the mouse pointer or signal
the status of some long-running asynchronous Emacs process. Setting
the message
to "****\n****\n****\n****\n****\n****\n****\n****" might
make the mouse obvious enough to notice. Feel free to play with that!
Trying to locate the mouse pointer leads me to another issue: how to locate point. So far, I've been using my overwrite-mode indicator to show me point.4 But I hope to devise a more effective solution soon. Perhaps.
0 If you don't see this, you might need to enable tooltip-mode.
1 Setting the variable org-return-follows-link to non-nil achieves
this.
2
See https://emacs.stackexchange.com/a/60555. The function is org-link-copy.
3 I chose to bind the function to "s-SPC".
-1:-- A Point-Based Tooltip for Emacs (Post Raymond Zeitler)--L0--C0--2026-10-24T21:28:21.479Z
: Ha, I accidentally left in some raw transcription output. Serves me right for not scrolling down. Also, I've added a summary image.
Next Thursday, I'm going to be chatting with linkarzu about Emacs and life. He's more of a Vim person than an Emacs person, so I'll try to share things that are generally useful. He's called it: "Why Sacha Chua Still Lives in Emacs After 20+ Years."
(America/Toronto, -0400) = Thu Oct 29 0800H EDT / 0700H CDT / 0600H MDT / 0500H PDT / 1200H UTC / 1300H CET / 1400H EET / 1730H IST / 2000H +08 / 2100H JST
I am not sure my eyebrows have ever done that, but linkarzu does like his thumbnails, so we'll let that pass. =)
I put together some sketches to help me organize my thoughts. His channel uses dark mode, so let's experiment with dark mode in my sketches too. I want to think about things so that the relevant resources are readily available and so that I can express myself more concisely. I can put together a presentation or write a blog post by myself, so I'm more curious about the kinds of things he wants to explore in the conversation.
I'll probably need to do a brief introduction to set the context so that people know where I'm coming from. Let's disclaim my professional irrelevance up front.
Image text and links
About me 2026-10-24-03
It turns out that I've been using Emacs for at least 25 years, which is pretty cool. I am 43, so that's more than half my life.
Let's start with a summary so that we don't worry about running out of time and so that people don't have to listen to two hours before getting to the good stuff. I want people to be able to get something out of this, whether they're a Vim diehard or experimenting with Helix, or even if they're passionate about things other than text editors. Here are some of the things I'm learning about that I think are important.
Here's a brief overview of my history with Emacs.
The details are a little bit fuzzy, but fortunately I've written about some things in previous blog posts. I'd like to flesh this out with an outline and maybe even some sentences or paragraphs later on. I love the way that different themes recur. I find myself building on things that I started exploring years ago. For example, my current interest in hybrid speech interfaces echoes all the way back to M-x doctor and Emacspeak from the beginning.
From "Unix Power Tools":
If you have time to waste (and I mean really waste), Emacs has things to keep you occupied. … If you think you might be in need of a psychotherapist, save your money and try ESC-x doctor RETURN.
I was not actually looking for a psychotherapist. I think I was seventeen or eighteen, and I was trying to go through all the interesting computer science books in my university's library in the Philippines. I thought it was funny that someone put in that kind of Easter egg. The more I dug around in the source code, the more I was amazed by the things that people had added to Emacs. Anyway, it's interesting how the fun, inconsequential stuff turned out to be the thing that caught my attention.
linkarzu mentioned wanting to talk about artificial intelligence. I don't know nearly as much as lots of other people do. Maybe I can just share the things that I've been experimenting with, and also what I care about from a community perspective. I would like to help figure out how to deal with this turbulence and come out the other side happier.
Text from sketch
Large language models and other things 2026-10-24-02
You can e-mail me at sacha@sachachua.com.
-1:-- Upcoming chat with linkarzu about using Emacs for 25 years (Post Sacha Chua)--L0--C0--2026-10-24T13:39:16.000Z
This month’s Emacs Asia-Pacific (APAC)
virtual meetup is scheduled for Saturday, October
24, 2026 with BigBlueButton and #emacs
on Libera Chat IRC. The timing will be 1400 to 1500 IST.
The meetup might get extended by 30 minutes if there is any talk, this page will be updated accordingly.
If you would like to give a demo or talk (maximum 20 minutes) on GNU
Emacs or any variant, please contact bhavin192 on Libera Chat with
your talk details:
-1:-- Announcing Emacs Asia-Pacific (APAC) virtual meetup, Saturday, October 24, 2026 (Post Emacs APAC)--L0--C0--2026-10-23T21:38:52.000Z
Sacha Chua has a really nice post on dictating into Emacs. She starts by quoting Matt Weber on how he writes better than he speaks. Chua can relate and so can I. We both feel the same way. As I’ve said before, “…my written thoughts are always much better organized than my spoken ones…”.
Being who she is, Chua resolved to fix this by arranging to dictate her posts into Emacs. As you can imagine, she encountered all sorts of problems along the way but was mainly able to find solutions for them. You can check her post—and Weber’s—to see how they’re doing that.
When I read Chua’s post, I thought, “maybe I should do that too” but then I realized that I don’t want to dictate my text. The truth is, I enjoy writing a lot more than I do speaking and I especially enjoy writing with Emacs. Although it’s not always obvious, I do a lot of revising on my posts as I write them and I usually try to make a final editing pass at least one day after I’ve written them. Speaking, on the other hands, feels more like “one and done” to me. I know that I could dictate a post and then edit the result but it’s not the same. And in any event, as I say, I enjoy writing a lot more than I enjoy talking.
I suppose, as Chua says, the day may come when ever encroaching old age, disease, or accident may make typing hard or impossible and I may be forced to consider dictation but until then, I’m happy to continue typing my text.
-1:-- Dictating Into Emacs (Post Irreal)--L0--C0--2026-10-23T15:20:11.000Z
Matt is experimenting with dictating his blog posts. He says:
I write really well. I have very good grammatical skills, for the most part, when I’m typing. But when I speak, I don’t speak like that. My grammar is not very good. I say words out of order a lot. I have filler words. And I say things that don’t make any sense.
This is totally me too! It's one of the reasons why I want to get used to dictation, because it'll give me a lot more practice in saying things, which can only help when I'm making videos or talking to people.
While I’m talking, the text that’s outputted has no punctuation or paragraphs or anything like that.
Oh yeah, no, that's mostly a matter of picking the right speech recognition engine. For example, I'm using a local instance of Orukeet, and it seems to do a reasonable job of dividing my thoughts into properly-capitalized and punctuated sentences in both English and French. In fact, I have some code in Emacs that wraps it to one sentence per line (as part of sacha-whisper-insert which uses sachac/whisper.el). This allows me to use Org shortcuts to move lines around in order to reorganize them into something that makes more sense.
Some people use large language models to clean up raw transcripts and remove false starts or filler words.
I don't quite trust large language models to leave my wording alone.
I have some code that uses replace-regexp-in-string to take out some of my filler words and fix commonly misrecognized words that I've saved with substitute.el.
I'm not sure if I was ever any good at multitasking, but nowadays at 43 years old, I find that talking and typing at the same time has gotten a little bit harder for me. I type much more slowly if I want to narrate things at the same time, like when I'm recording a video or live streaming. When I'm just thinking on my own, I type quickly enough that my computer sometimes jumbles the keystrokes up because it doesn't handle keyboard interrupts well when it's busy. It's not just my fingers; it sometimes happens even when I was trying to use wdotool to simulate typing. Speech recognition is surprisingly reliable, even though I have to correct it from time to time. The broad strokes are there, and I save the recordings so that I can fix misrecognized words.
I also love the way that speech recognition opens up so many more possibilities for commands I would never be able to memorize keyboard shortcuts for. "Remind me today to…" is now one of my favourite ways to capture an Org Mode task.
Besides, I think it's a good idea to plan ahead just in case I can't type very well in the future. All sorts of things can happen: accidents, disease, old age. I wrote about that a bit more in Emacs Carnival Oct 2026: Exploring keys and voice input in Emacs.
All that to say: exciting times ahead. I think it would be pretty amazing if this sort of thing allows more people to write and share. Looking forward to reading (and possibly listening to) what people have been learning about!
You can e-mail me at sacha@sachachua.com.
-1:-- Dictating blog posts into Emacs (Post Sacha Chua)--L0--C0--2026-10-22T19:13:18.000Z
I've just made a very small update to
blogmore.el to fix a small bug,
and also to update some deprecated code.
The blogmore-new command inserts the result of expanding a template into
the buffer when making a new post, and I've not touched that template since
I started writing blogmore.el. This includes not updating it when I
switched much of the frontmatter-handling code over to proper YAML parsing.
The problem here was that the title didn't get quotes around it, which
became an issue if there was something like a : in the title; any
subsequent attempt to modify frontmatter properties would throw an error.
So this release ensures that the title and the date get quoted right away. I guess, at some point, I should rework this code so that it uses the frontmatter-handling code to set up those properties and, instead, just uses the template for the main body.
I've also changed all uses of things like when-let and if-let to use
when-let* and if-let*. It seems that, as of Emacs 31.1, the former are
deprecated.
-1:-- blogmore.el v5.4.0 (Post Dave Pearson)--L0--C0--2026-10-22T15:49:15.000Z
-1:-- I Updated My Emacs Number Package (Post Eric MacAdie)--L0--C0--2026-10-21T18:48:49.000Z
As you all know by now, I really hate being spied on by web sites and other on-line entities. I’m pretty sure this misbehavior won’t be legal much longer but I’m equally sure that the on-line advertising industry won’t go gentle into that good night and will find ways to nibble around the edges of whatever laws get passed.
Happily, there’s Magic Lasso which pretty much solves the problem for Safari users. That would be the end of the story except that I use XWidgets with mu4e to read HTML emails and Elfeed entries through the wonderful elfeed-webkit. They work nicely but now Magic Lasso is no longer filtering their content so cookies and trackers are being accumulated. For the longest time I couldn’t find where they were being stored. I finally stumbled on its location and discovered that it was a simple text file that could simply be deleted.
The problem was that I had to remember to do it and more specifically, where the file was located. I finally stopped being stupid and wrote a simple function to delete the file:
(defun jcs-kill-cookies ()
"Delete the cookies from Webkit"
(interactive)
(delete-file ".emacs.d/url/cookies")
(message "Cookies deleted."))
It’s almost too simple to bother showing.
While I was at it, I also called jcs-kill-cookies when I quit elfeed so I don’t have to remember to do it manually. All of this is trivial, of course, but it’s another example of sanding down your workflow to make it as simple as possible. Now things pretty much take care of themselves. I suppose I should figure out a way to delete cookies after email too but for now I’m happy that the problem is mostly solved.
-1:-- A Small Improvement (Post Irreal)--L0--C0--2026-10-20T15:30:32.000Z
-1:-- Applying Emacs Eglot safe code fixes on save (Code Actions) (Post James Cherti)--L0--C0--2026-10-19T16:02:16.000Z
There are a bunch of Eglot-related resources in the Coding category today. Nice to see people working on lots of cool stuff!
Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, Mastodon #emacs, Bluesky #emacs, Hacker News, lobste.rs, programming.dev, lemmy.world, lemmy.ml, planet.emacslife.com, YouTube, the Emacs NEWS file, Emacs Calendar, and emacs-devel. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at sacha@sachachua.com. Thank you!
You can e-mail me at sacha@sachachua.com.
-1:-- 2026-10-19 Emacs news (Post Sacha Chua)--L0--C0--2026-10-19T12:43:05.000Z

by congerdesign via Pixabay
At work, I write my documentation with Org Mode and then export it into Confluence using a space-ID and a parent page ID. Both values depend on the customer to whom the documentation refers. Instead of creating every documents from scratch, I was searching for something which supports me by providing a task-specific template. I also needed a solution to automatically set customer-related properties in the template. Once I learned about Org-Capture, I created a template that asks me for a customer and sets any customer-related value in my template automatically. Because Org-Capture templates support the evaluation of Elisp, extending them is only limited by your Elisp skills 😃.
One of the things I especially enjoy about Emacs is that there’s always something new to discover as Capture, a feature for quickly create notes without leaving the current context. Assuming you are taking notes during a project meeting and want to create a new TODO according to a new project tasks - Where to put your new task? Instead of thinking about the target for your TODO, you can call M--x org-capture, fill the template and Capture stores the TODO in a file set in org-capture-templates. The main benefit for me is that I don’t have to worry about where my new note will end up while I’m working on something else, like a meeting.
With Capture Emacs provides a powerful template generator which provides a lot of expansion to set values automatically. One expansion is %^{PROMPT|Default|Some Value} which prompts you to select one of the provided values and replaces the expansion by it. If you leave the input empty, Capture replaces the expansion with the first value (Default). Because the value is not stored, I needed something else to meet my requirements.
During my investigation I discovered a blog post Org Capture Tricks of David Zuber who extended his templates with Elisp functions to store values in variables which can be used within the template. Thanks to this knowledge and the capabilities of Capture, I was able to create a template for all my documentation needs.
In this post I’d like to introduce you to Org Capture and share how I solved my problem.
This guide was tested with the following software:
If you want to test Emacs settings without using your default Emacs config, copy your .emacs.d and start Emacs with emacs --init-directory </path/to/.emacs.d-copy. For testing, I recommend removing anything in your copy that isn’t relevant to your test case, including settings in init.el, bookmarks and so forth.
Let’s start by adding the following snippets into your init.el:
As default Capture stores your file in ~/org. To store your notes created by Capture in a dedicated directory replace the default value of org-directory as follows.
(setq org-directory "~/Documents/Workbench/org-files")The following code adds an entry in the menu after executing M-x org-capture.
(setq org-capture-templates
'(("t" "Todo" entry (file "todo.org") "* TODO %?\n %i\n %a")))Alongside file Capture supports many other targets such as file+headline to create your note in a dedicated tree. For further details please refer to Org Manual - Template Elements.
(setq org-capture-templates
'(("t" "Todo" entry (file+headline "todo.org" "Header1") "* TODO %?\n %i\n %a")))To apply the change run M-x eval-buffer or M-x restart-emacs.
To create your first note enter M-x org-capture and select the new template. Once you entered C-c C-c to save your task, Capture creates todo.org in ~org-directory_. Instead of saving your new TODO in a file, you can execute org-refile with C-c C-w to move your entry to a dedicated heading. Without any adjustments in your Emacs config, you can only use todo.org as refile targets.
To add new refile targets, you may add them into org-refile-targets as follows. Each entry of org-refile-targets starts with a specification of the files which shall be considered, following how to find refile targets within these files. The next code block adds the current buffer (nil), org-agenda-files and an absolute file path as refile targets. Each :maxlevel . N limits the search scope within the target to headings up to level N.
(setq org-refile-targets
'((nil :maxlevel . 4) (org-agenda-files :maxlevel . 3) (("~/Dokumente/Emacs-Test/org-files/test.org") :maxlevel . 2)))If you are curious about Refile, you should check out its documentation (Emacsdocs - Refile and Copy) or one of the various blog posts which are covering many details, such as Organizing Notes With Refile by Arron Bieber.
To get a description of org-refile-targets you can also enter C-h v org-refile-targes RET. In general, you can find anything you need to know about variables, functions and other topics by using C-h.
Once you know how to use Capture, we can go ahead and create our template.
Assuming you want to create a template to document a customer environment. This documentation shall include the database management system and another file loaded by the template.
Do not bother about the use case - Only the features of Capture which we are using are relevant.
Firstly add a new entry into org-capture-templates.
(here) sets the cursor position as target.(setq org-capture-templates
'(("t" "Todo" entry (file+headline "todo.org" "Tasks") "* TODO %?\n %i\n %a")
("s" "My New Template" entry (here) (file "my_new_template"))))Then create the template in org-directory.
* Site Documentation %(ms/set-var "Set project" 'project '("PR1" "PR2")) %^G
:PROPERTIES:
:CUSTOMER: %(ms/get-customer project)
:END:
Site location: %^{Host Location|Cloud|On-Premise|Hybrid}
Database: %(ms/set-var "RDBMS" 'rdbms '("PostgreSQL" "MSSQL" "Oracle"))
Database port: %(ms/get-db-port rdbms)
#+begin_src bash
%[~/Dokumente/Emacs-Test/org-files/script.sh]
#+end_src
%?%(EXP) evaluates an Elisp expressions and replace it with the result. We will use it later to call our functions.%^G prompt for tags, with completion all tags in all agenda files.%^{PROMPT|A|B|C} prompts you to select A, B or C with A as default value.%[FILE] loads content from a file. Expansion in the file are resolved after the file has been loaded.%? sets the cursor after you have completed the template.To get a full list of possible % placeholders you should visit orgmode.org -Template Expansion.
To demonstrate the usage of variables in the included files, we create a simple script that is loaded by the template which assigns the selected project to VAR1.
#!/usr/bin/env bash
VAR1=projectNext create an Elip script including the functions called in the template:
ms/get-customer returns CUST1 or CUST2 depending on project.ms/get-db-port returns a port depending on the value of rdbms.ms/set-var prompts the user to choose one of the entries in options and assign the selected value to variable. options is a list passed to the function.;;; org-capture-summary-tools.el --- Collection of functions loaded in config.org
;;; Commentary: Adapted from https://storax.github.io/blog/2016/05/02/org-capture-tricks/
;;; Code:
(defun ms/get-customer (project)
"Return Customer according to PROJECT."
(if (equal (downcase project) "PR1")
(message "CUST1")
(message "CUST2")))
(defun ms/get-db-port (rdbms)
"Return Confluence parent page id according to RDBMS."
(cond
((equal (downcase rdbms) "postgresql") (message "5432"))
((equal (downcase rdbms) "mssql") (message "1433"))
((equal (downcase rdbms) "oracle") (message "1521"))))
(defun ms/set-var (prompt variable options)
"PROMPT for one of OPTIONS, save it to VARIABLE and insert it."
(make-local-variable variable)
;; COLLECTION (2nd argument): Gets options as a list of strings.
;; REQUIRE-MATCH (4th argument): Set to non-nil (t) to force user input.
;; DEF (7th argument): Returns first element of list as default.
(set variable (completing-read (concat prompt ": ") options nil t nil nil (car options))))Finally load org-capture-summary-tools.el in init.el and run M--x eval-buffer to make its content callable within our template.
(setq capture-tools (concat user-emacs-directory "packages/custom/org-capture-summary-tools.el"))
(load capture-tools)Now we can test our new template. For this call M--x org-capture, select it with s and enter the values in the user prompts. The next codeblock contains the buffer content after entered PR1 as project, PostgreSQL as database, Cloud as site location and TAG1 as tag.
* Site Documentation PR1 :TAG1:
:PROPERTIES:
:CUSTOMER: CUST2
:END:
Site location: Cloud
Database: PostgreSQL
Database port: 5432
#+begin_src bash
#!/usr/bin/env bash
VAR1=project
#+end_srcFinally, you can either save your buffer as a new heading at the cursor position or move it elsewhere entering C-c C-w.
After we discovered the basic usage of Capture we created a template including prompts to set multiple values based on a single prompt. With the capabilities of Capture in relation with Elisp you have anything you need to adapt the template to your personal requirements.
Thank you for your attention. I hope if this article helps you and wish you every success with your projects.
-1:-- Using Org-Capture Templates with Elisp Evaluation to Set Values Depending on User Input (Post Martin Schell)--L0--C0--2026-10-18T15:00:00.000Z
Randy Ridenour has an interesting post about debugging your init file. The main problem, he says, is that he sometimes makes a change that results in unbalanced parentheses. Often he doesn’t discover this until the next time he reboots Emacs, something he doesn’t do often.
He says that his usual method for finding the problem was to do what amounts to a binary search on his init file: He comments out half the init file. If the problem occurs, he repeats the process with the commented potion; otherwise he continues with the uncommented half. But then he discovered the built-in function check-parens that checks for unbalanced delimiters in the current buffer. That’s a nifty solution to Ridenour’s problem that I didn’t know about. Another tool in the toolbox.
The other thing about his post is that it reminded me of a package I wrote about in 2015 from Artur Malabarba: elisp-bug-hunter. I haven’t used it in a while but every time I do, it works perfectly and invariably finds the error often without any further action. What it does is automate the binary search that Ridenour used to hunt down his unbalanced parentheses but it works for any problem.
If the problem throws an error, elisp-bug-hunter will find it without any user input but for more complicated situations, you can specify a predicate to indicate whether the current run succeeded or not. I load it early in my init so it will be available if there is a problem.
So the take away from this post is two really useful debugging tools: check-parens for finding unbalanced delimiters and elisp-debug-hunter. Everyone should know these tools.
-1:-- Debugging Init.el Files (Post Irreal)--L0--C0--2026-10-18T14:49:57.000Z
People say that language is for communication, and every day Hacker News proves these people wrong.
I mean, it’s not just Hacker News, but it’s the place I encounter this phenomenon most often: Somebody will be discussing something, and then somebody else will respond with a short sentence that’s mostly acronyms.
The fascinating thing about these responses is that if you know what the acronyms mean, then what’s being stated is often trivial (i.e., something that everybody who knows what those acronyms mean already knows). And if you don’t know what the acronyms mean, then the sentence is completely opaque.
(Often seen, for instance, when discussing network issues, and somebody responds with an alphabet soup involving acronyms like “BGP”, “AP”, “BMP” and “FRX”.)
So these utterances function two ways: For the people who’re on the “inside”, they’re saying “I know this stuff, like you other smart people…”. For the people who’re on the outside, this expresses “… and you’re a moron”.
I guess that’s a form of communication after all.
But for fun, I just whipped up a new command for Emacs called shr-etla (for obvious reasons) and put it on Microsoft Github. Here we have it in action on a totally random Hacker News comment:
It works by sending the paragraph over to an LLM, and it also tells the LLM what web site it came from. This context is vital, because many abbreviations expand to many, many things, and the LLM needs to know what kind of person it’s trying to decode.
(Googling for abbreviations is usually pretty useless — 74% of the time you get a bunch of other meanings; usually an American company or other that’s been SEO-ing the results. (“SEO” means “Search Engine Optimisation”, which is a form of terrorism.)
Of course, all of this doesn’t help much in many cases:
I guess!?
(caveat accipiens — like all things involving LLMs, this Emacs command is just a toy. The LLM has been instructed to return JSON with the results, and it does so in 99% of the cases, but in some cases it’ll return something else, and then you just have to run the command again. Or it’ll have done something else, like translate things into Kyrgyz; who knows? But that’s just part of the fun.)
-1:-- Hacker News Decoder Ring for Emacs (Post Lars Ingebrigtsen)--L0--C0--2026-10-18T10:59:04.000Z
-1:-- [RFC] a small patch for the brave on emacs32 (Post Org Mode requests)--L0--C0--2026-10-18T06:24:30.000Z
-1:-- [FR] LaTeX preview between #+begin_equation/#+end_equation (Post Org Mode requests)--L0--C0--2026-10-17T17:32:33.000Z
I usually write my posts the evening before I publish them but yesterday, as they say, life intervened. I went to a wedding and by the time I got home it was too late and I was too tired to concentrate on writing a post. Therefore, I’m writing this the next morning so things are a bit rushed.
Marcin Borkowski (mbork) has an interesting problem whose solution is harder than you think it would be. The problem is pretty simple: Every Sunday morning when he writes his weekly review, he needs to update all the clock tables in his journal. That usually takes about a minute and mbork had the idea that he could use an idle timer to run the update. Specifically, he reasoned that if Emacs were idle for a minute it probably meant that he was otherwise engaged and that Emacs would be idle for another minute so why not fire off his update?
Of course, he wanted to do this only once a week on Sundays so it was a bit more complex than just setting the idle timer. He came up with the idea of setting a (normal) timer for 3 AM on Sunday that would set the idle timer. That didn’t quite work because of a technical issue but he got things working with yet another timer.
You should read his post for the details. His solution is interesting and shows that sometimes even simple things can be harder than we imagined.
-1:-- Updating Clock Tables (Post Irreal)--L0--C0--2026-10-17T16:04:54.000Z
It’s been a month since I first plugged my Glove80 into Hedwig and started using it, so I thought it was time for a follow-up.
First things first: this keyboard is the most comfortable thing I’ve ever typed on, to the point that I cringe now when I place my fingers on a regular keyboard in the office. If I wasn’t a keyboard snob earlier with my Kinesis, I definitely am now.
It’s not just the ergonomics, which I will get to below. I didn’t realize how important the switches are. I thought I’d miss the clicky sound and feel of my old brown switches, and sometimes I do for a moment, but it’s only for a few seconds. A whole morning of typing in Emacs? Nah. The Cherry Blossom switches that come with this keyboard are a pleasure to work with. They’re not only silent, but also super easy to press. It’s like whispering to my keyboard instead of shouting at it.
The real reason I ended up with the Glove80 was my RSI, and I’m happy to say it’s gone. That’s probably why 90% of people buy a keyboard that looks “crazy” like this one in the first place. Having the two halves separate also makes my shoulders and arms feel better. The keyboard parts are very light, so rotating them a bit into a slight “V” to fit the natural angle of my arms and wrists is automatic as I move around a bit, especially when I stand at my desk. With the Kinesis, which is much heavier and bulkier, this was a whole thing, not to mention the cables that ended up tangling around my coffee cup in the morning. This is much better.
Another important factor is the keywells. My fingers fit into those as soon as I took the keyboard out of the box. I know the keywells aren’t specific to the Glove80, but it was the first time I experienced them, and it makes you wonder why they don’t exist on all keyboards. It also helps you find the right position instantly. The palm rests fit, the slight height differences in the wells tell your pinkies where they should go, the thumbs naturally extend to the thumb row… it all felt right the second I placed my hands on the Glove80, and a month later it’s jarring not to have this when I switch to another keyboard.
This brings me to the key positions and the columnar layout, which was (and still is, to an extent) the hardest part to get used to. For the most part, the regular keys for QWERTY typing work. The issue is more with the function keys in their factory settings and the conflict, in my case, with what Emacs wants me to use. I’m mostly used to the common shortcuts now, but my right hand still does most of the work with the left Ctrl, and I have to consciously force myself to use the right Ctrl key for keys on the left half (like x or s) and vice versa1.
The main issue I have with the keyboard now is gaming. This keyboard isn’t meant for WASD (see the layout in the picture below), which is hard-coded into any gamer’s DNA. For now, I switch back to the Kinesis when I play any action games. I could probably just create another layer for gaming, shifting the keys to maybe RDFG, but that’s just weird. Besides, games are more in the keyboard shouting category than the whispering one.
Other small annoyances… let’s see.
The legs of this keyboard. They are essentially screws with pads (the “boot” of the leg), and even though I use the nuts and rubber rings that came with the keyboard — which do help — the screws still move a bit from time to time, which results in the keyboard being a bit wobbly. It doesn’t happen often, and it takes a few seconds to fix, but I feel like there needs to be a better solution. I like to use a high tented angle with my Glove80, which means that when I want to use the case to take the keyboard somewhere, I have to unscrew the two long legs — the ones under the thumb rows — so they fit in the case, and then screw them back in and tighten them, which means I’m avoiding taking it with me, which is a shame. Here’s what these look like up close:
Two more nitpicky things: switching back to the Kinesis is actually confusing now, which I didn’t expect. I now type worse on the Kinesis than I used to. Not exactly bad, when you think about it: the whole point was to move away from standard keyboards, but the world still runs on the old standard. I now expect to find common function keys under my thumbs (where they should be!), but they aren’t, and that leads to typos at work because I don’t take the keyboard with me as I mentioned above. I’m fine on my laptop keyboard though; I guess it’s different enough from the Glove80 that my hands register it as a whole different mode and don’t get confused.
The other tiny small thing is not the keyboard’s fault at all: hard drive password at boot on Linux. There’s no Bluetooth at boot (it’s locked behind the encryption if you encrypt your hard drive), so I need to physically plug it in with the USB. This is a tiny little thing, but I find that passwords in general are frozen in my finger memory, and I have to watch myself type them now to get them right. The solution here is probably using new passwords that will work with this keyboard — which in turn means I will have the reverse problem when I can’t use this keyboard.
All in all though, this keyboard is fantastic. A few more months, and I’m sure I’ll incorporate more tricks to help with some of the issues I described above. I’m glad I got it. It didn’t just resolve my RSI issues, but it’s also making me a better typist in general.
Xah’s fly keys for the Glove80 (he only has a Dvorak layout for it) inspire me to make some modifications. For example, Xah has a shift key that acts as shift when held down, but as tab when tapped. This works well because the shift key is meant to function when you hold it, and the tab key is for tapping. ↩︎
-1:-- Glove80: One Month in (Post TAONAW - Emacs and Org Mode)--L0--C0--2026-10-17T13:18:45.000Z
-1:-- Auto format code on save with Emacs Eglot (Post James Cherti)--L0--C0--2026-10-17T12:48:43.000Z
I live in Emacs, and usually only restart it when absolutely necessary. When I do restart, I occasionally get the dreaded message that there’s something wrong with my init file. I’m then prompted to start with the --debug-init switch, which I rarely find helpful. It’s been my experience that the error is often from having unbalanced parens somewhere, and I’ve always found them by brute force—commenting out half of the file, restarting, commenting out another half, restarting, etc. This is a tedious job with an init file that is thousands of lines long.
Recently, I stumbled on the check-parens function. Run it from the beginning of the buffer, and it will stop at the outermost open paren that contains the error. I don’t know how many hours this would have saved me in the past, but better late than never.
Tagged: Emacs
-1:-- Finding Unbalanced Parens in Emacs (Post Randy Ridenour)--L0--C0--2026-10-17T10:46:00.000Z
-1:-- [TASK] Re: Suggestion: Document `org-extend-today-until` in Clock Table section (Post Org Mode requests)--L0--C0--2026-10-17T10:39:35.000Z
-1:-- Using timers to perform weekly tasks (Post Marcin Borkowski)--L0--C0--2026-10-17T05:55:56.000Z
Raw link: https://www.youtube.com/watch?v=vKGf3lP6eRQ
[ The stream will be recorded. You can watch it later. ]
Today, Saturday, 17 October 2026, at 14:30 Europe/Athens, I will work on my modus-themes for Emacs.
About a year ago I turned them into a framework that theme developers can rely on. Lots of themes have been built on top of Modus since then.
What I want to do this year is extract the core Modus that other themes need into its own library. This way, we can have a separation of concerns between what is the framework proper and what are modus-themes (modus-operandi, modus-vivendi, etc.).
I expect to work on this for 3 hours or more.
I will have the chat open in case someone has a question about what I am programming or ANYTHING else they might want to ask me.
🔷 Consider making a donation.
-1:-- Emacs live extracting the Modus themes core at 2026-10-17 14:30 Europe/Athens (Post Protesilaos)--L0--C0--2026-10-17T00:00:00.000Z
-1:-- Emacs Eglot for Python with .dir-locals.el, python-lsp-server (Pylsp), and ruff (Post James Cherti)--L0--C0--2026-10-16T16:30:14.000Z
Years ago when I first starting using Emacs seriously, one of my go to sites for inspiration and ideas was Dirk-Jan C. Binnema’s EMACS-FU. He always had good ideas, not the least of which is the wonderful email client mu4e that I and many others are still using today. Thirteen years ago, Binnema stopped blogging although his site was still active. Despite the fact that he was not blogging he was working on mu/mu4e and other projects.
Now, after all those years, he’s published another post on EMACS-FU. This times it’s about his just released new version (1.14.4) of mu4e. Most of the post is concerned with the mu4e changes in the latest release. A (very) broad summary of these changes are:
By far the most exciting news for me is about Binnema’s work on xwidgets. As most of you probably know, Emacs’ support of xwidgets has been broken for a while because of changes to the xwidgets library that removed a feature that Emacs depended on. Binnema has fixed that so now (as of Emacs 31.1) Emacs can support up-to-date xwidgets libraries again. That’s a real boon for me because I use xwidgets for both email in mu4e and for Elfeed.
In any event, I’m thrilled to see Binnema’s return even if it amounts to no more than a visit. I do hope, however, that I won’t have to wait 13 years for his next post.
-1:-- EMACS-FU! (Post Irreal)--L0--C0--2026-10-16T15:04:45.000Z
This year I will join EmacsConf 2026 for a panel discussion. The details are not available yet, though I expect them to be made public in the coming days. You will be able to find them here: https://emacsconf.org/2026/talks/community/. Follow the links on that page to also learn about the full programme of the conference.
I am happy to be given this opportunity and am looking forward to it! Until then, I wish everybody happy hacking with Emacs.
🔷 Consider making a donation.
-1:-- I will participate in EmacsConf 2026 (Post Protesilaos)--L0--C0--2026-10-16T00:00:00.000Z
I just finished adding AGENTS.md and CLAUDE.md to each of my Emacs
packages. I copied the files from the emacs.git trunk. Those files
inform LLMs about the project’s policy to reject LLM-generated
contributions.
The reason for this change is that (i) I have assigned copyright to the Free Software Foundation, (ii) all my code is distributed under the terms of the GNU General Public License, and (iii) my packages are designed to be compatible with core Emacs (all my packages can, in principle, be part of emacs.git).
I do not want to merge changes that can jeopardise the legal standing of my code base and, more generally, choose to remain aligned with what the Emacs developers are doing.
🔷 Consider making a donation.
-1:-- Emacs: all my packages have an AGENTS.md and CLAUDE.md to restrict use (Post Protesilaos)--L0--C0--2026-10-16T00:00:00.000Z
Nudged by Ihor Radchenko's suggestion, I added some JavaScript to translate the graphical SVG schedule on the EmacsConf wiki to your local time zone. I'll post the schedule on the wiki next week, but in the meantime, you can take a look at the draft schedule in the organizers' notebook.
Let me test it by setting my local time zone to UTC with sudo timedatectl set-timezone Etc/UTC…
Here's the horizontal view in our backstage index, showing the status of talks so far.
I still need to make sure that my time zone is set to America/Toronto when I'm generating the SVGs, but at least now people can see the times translated to their local time zone.
The Javascript code is in https://emacsconf.org/script.js as addLocalTimeToSVGs.
The Emacs Lisp code to generate these SVGs is in emacsconf-schedule.el. emacsconf-schedule-svg is the starting point.
You can e-mail me at sacha@sachachua.com.
-1:-- Added Javascript time zone translation to the EmacsConf schedule images on the wiki (Post Sacha Chua)--L0--C0--2026-10-15T18:48:04.000Z
A month ago, I wondered idly whether translation gadgets (for quick word lookup) exist, and while everybody said “just use your phone, dumbo”, that’s just too sensible for me. And way too inefficient.
So while I pondered what to do about it, I bought some keyboards to see whether that could be the solution.
Small keyboards (and the one above is as small as they come) is such a cursed category. Either the keys are just too horrible or the range is awful, or a combination of the two. So I wasn’t expecting the above to work (a DarkWalker Wireless Bluetooth Keyboard, M4 Mini Keyboard), but… the keys don’t seem awful, really?
Let’s see how it works, though — I’ve paired it with my laptop, and I’m going to write the word “cheville”:
Yeah. It doubles up key presses like crazy. And this is paired with my laptop, so the distance between the keyboard and the Bluetooth receiver is just a few centimetres.
Trying several times, I have yet to make it through “cheville” correctly even once — in addition to doubling keystrokes, it also loses some here and there.
It also seems to go completely AWOL if you let it sleep after pairing? Which may be a Linux problem for all I know. The range isn’t awful — I get five metres easily.
But while it’s a nice form factor, it’s completely unusable. So onto the next:
This is a Phistone Mini Keyboard with Triple Mode, so it’s got a 2.4GHz USB dongle in addition to the Bluetooth. Which, in my experience, usually works better on these types of things.
But man, that’s one ugly keyboard… and it works! I’m not getting any missed keystrokes or any doubled ones. So let’s start implementing something!
After typing away a bit (the results are on Microsoft Github), I’ve now got something that seems to work.
Let’s see… here’s my normal position in my reading chair, and I have the li’l ugly keyboard on the table next to me, and there’s a screen over there that normally displays the cover artwork of the album that’s playing on the stereo… So let’s see how efficient this is:
Hey! This actually kinda sorta has possibilities…
The Emacs over there is querying the Microsoft Azure Translation API behind the scenes — I tried a couple of other services first.
DeepL just returns one word, for instance, which is pretty useless when looking up words (because most have several meanings):
(deepl "cheville") => (:translations [(:text "dowel" :detected_source_language "FR")])
I was looking for the more normal meaning of “ankle” here…
Anyway, Azure API is free, and returns a list of resulting words and word classes, so that’s pretty ace:
(aztrans "cheville") => (:source "cheville" :translations ((:target "ankle" :type "NOUN") (:target "peg" :type "OTHER") (:target "pin" :type "NOUN")))
Thanks, Microsoft! Not everything you make is a crime against humanity!
(I mean… that I know of…)
The Emacs package works by grabbing the evdev device for the li’l keyboard, so that X doesn’t see any of the events. And then it just pops to a new window configuration and displays the input, and then finally queries the Azure API before popping back to where you were…
So this works, but that keyboard really is too big to be on my table here permanently. Does anybody know of a keyboard that’s more like that first keyboard, size wise, while working as well as this larger keyboard? Or perhaps not that small — something halfway between these two keyboards would be ideal. And not more keys than are needed — the small number of keys on that M4 keyboard is nice.
-1:-- UX IS US (Post Lars Ingebrigtsen)--L0--C0--2026-10-15T16:14:13.000Z
Over at techhub.social, lispwizard has a short post (or whatever techhub.social calls them) on saving window configurations to registers. He notes how easy it is to save multiple configurations and switch between them on the fly.
Saving window configurations and other data in registers is an extraordinarily powerful technique that all Emacs users should learn and master. There’s a problem, though, when you want to do this programmatically: which register should you use? How can you be sure that your choice won’t conflict with a register that the user has chosen manually for some other task? There are several situations in which I want to run an Emacs function in a single window—Elfeed is an example—and I do that by saving the current window configuration, deleting the other windows, running the required function, and then restoring the previous window configuration but how do I choose a “safe” register to save the window configuration in?
The answer is a trick I learned from Magnar Sveen years ago. Instead of using a letter to name the register, you use a symbol like :saved-windows. That works because when you name a register with a letter, Emacs uses a pointer to its symbol as the register name. Using a symbol name like :saved-windows works because the result is also a symbol pointer.
If you have the need to save something in a register programmatically, this is a good way of doing it. You don’t have to worry about conflicts because the user can not use symbol names for registers interactively.
-1:-- Saving Window Configurations (Post Irreal)--L0--C0--2026-10-15T15:02:25.000Z
Raw link: https://www.youtube.com/watch?v=mBAhOCyYKlc
[ The stream will be recorded. You can watch it later. ]
In a couple of hours I will resume the work I am doing on my Emacs
denote package. There are general refinements I wish to make, but
also to continue iterating on the new denote-data-mode, which is an
optional in-memory cache for Denote.
I will have the chat open in case someone has a question about what I am programming or ANYTHING else they might want to ask me.
🔷 Consider making a donation.
-1:-- Emacs live continuing ‘denote’ maintenance and refinements (2026-10-15 16:30 Europe/Athens) (Post Protesilaos)--L0--C0--2026-10-15T00:00:00.000Z
Yesterday I wrote about James Cherti’s Org-ibullets package. Today I’m writing about another package that he announced at essentially the same time: wizard.el. It’s a package of some carefully written Emacs functions dealing mostly with editing and navigation.
Cherti describes his functions as using the builtin Emacs functions rather than third party packages or writing complex logic de nuevo. It’s surprising how far you can get with just what Emacs provides out of the box.
You can look at Cherti’s post or the source code for a list of the functions but their broad categories are:
Cherti has more to say about the functions in those categories so be sure to take a look at his announcement. If one of your goals is to reduce the number of third part dependencies in your configuration, this package may interest you.
-1:-- Wizard Tools (Post Irreal)--L0--C0--2026-10-14T16:13:24.000Z
Org Social is so simple it looks like it was born before the internet. A single plain text file, an editor and a host are enough to interact with other people.
To "create an account", you just create a plain text file in any editor. Your "database" is your own hierarchy of posts. Your "algorithm" is however you decide to sort other people's posts. There is no "character limit". Your "server" is any hosting or web server. Your "interface" is your favorite editor. And if you want to "delete your account", you just delete the file. And of course, the syntax is richer than Markdown and just as easy to learn: Org Mode. You share and edit wherever you want, and you use it as a microblog, a journal, a forum, a bulletin board or a blog. Your file, your rules.
Behind it there is infrastructure that enriches the interaction and connects it with other systems and formats (the Fediverse, RSS, clients...). Each piece adds one more level of convenience, but none of them is essential: they are 100% optional! And that is what I want to talk about.
I'd like to take it apart piece by piece, explaining what each one does and why it exists.
Everything starts and ends here. A file called social.org, in UTF-8, with LF line endings.
The header is your profile. They are Org Mode keywords:
#+TITLE: My journal
#+NICK: Alice
#+DESCRIPTION: I write about Emacs and coffee
#+AVATAR: https://alice.com/avatar.jpg
#+LINK: https://alice.com
#+FOLLOW: https://bob.com/social.org
#+FOLLOW: friend https://carol.net/social.org
Below it, in a * Posts section, go your posts. Each one is a heading with an RFC 3339 timestamp, which works as a unique identifier:
** 2025-05-01T12:00:00+0100
:PROPERTIES:
:LANG: en
:TAGS: emacs org-social
:END:
Today I discovered Org Social and I can't stop writing.
And you already have a social network.
Anyone can curl your file, read it with their own eyes or with a client or script, and follow you by adding your URL to their #+FOLLOW:.
The interaction itself lives inside the format. There are no magic buttons, there are properties:
:REPLY_TO: <url>#<id> pointing to the original.:MOOD: ❤ and an empty body.:INCLUDE: with a reference to someone else's post.:POLL_OPTION:.[[org-social:https://bob.com/social.org][Bob]].Since it is plain text, you can edit all of it by hand if you feel like it.
When you are done, you save the file and upload it to your server.
Every decentralized social network has a weak spot, whichever one comes to mind. I covered this in There is no such thing as full decentralization, but in short, you always have to decide which of these five things the network sacrifices:
Org Social sacrifices discovery. Nobody can find you unless you give them your URL, just like an email address. And that makes some interactions impossible to reconstruct. For example, what happens if someone you don't follow joins a thread you started? If you don't follow them, you never find out.
That's why we need upper layers that mitigate the problem, although you should only use them if you think you need them. It may be that everyone taking part follows each other and needs nothing else. Otherwise, you will need a Relay: an index.
You can check each feed directly with curl or with your browser. However, just like with your list of RSS/Atom feeds, what we all end up doing for convenience is using a client. It reads every source, sorts them, filters them and merges them into a list that is easier to read and that you can also navigate. The same goes for Org Social.
There is a client for Emacs, another for iOS, another for Android (in alpha as I write this), command line implementations and a public web viewer that needs no account or client. Clients build a timeline by merging all the feeds you follow, and they let you reply, react, share and vote in polls. But every change happens in your own file, not on a central server, and reading is volatile, since it is built on the fly.
:quality(85)/https://andros.dev/media/blog/2026/10/org-social-screenshot.png)
The client also acts as a translator between Org Social and the interface you choose. And it is a good way in for people who are already used to X/Twitter or Mastodon, or who don't want to bother learning the syntax.
The Relay is a server that crawls the network of social.org files and builds an index: users, mentions, replies, groups and threads. It does this by reading the #+FOLLOW: lines of every feed it knows and downloading the files it finds. You can also register your feed manually so it reads it.
It doesn't store content, only addresses. It notes who mentions whom, what replies to what and what belongs to each thread.
Now you can write a client or a script that asks the Relay for the information that fills in the parts where you were blind before:
# Who mentioned me?
curl "https://relay.org-social.org/notifications/?feed=..."
# Give me the full thread of this post
curl "https://relay.org-social.org/replies/?post=..."
# Find everything about Emacs
curl "https://relay.org-social.org/search/?q=emacs"
And that's not all: anyone can run their own Relay, locally or on a server. Their instance will connect to other public Relays and they will exchange information, so the whole network knows which feeds exist and how they relate.
It also offers other features, such as real-time notifications, RSS/Atom feeds, post search and bridges to ActivityPub/Mastodon. Yes, you can follow Mastodon accounts from Org Social, or keep up with Org Social from your favorite RSS/Atom reader.
Needing your own hosting just to start taking part in the network is a tough requirement. That's why there is a free and open hosting service, integrated with the clients, that puts your social.org online with a single click.
You pick a nick and get two addresses:
https://host.org-social.org/alice/social.org.If you use a client, all of this is automatic and you never even see the secret URL, since it is stored in your configuration. It all happens behind the scenes.
If you are curious, signing up is a single request:
curl -X POST https://host.org-social.org/signup \
-H "Content-Type: application/json" \
-d '{"nick": "alice"}'
And overwriting your file, another one:
curl -X POST https://host.org-social.org/upload \
-F "vfile=YOUR_VFILE" \
-F "file=@social.org"
It doesn't lock you in either. You can ask it for a permanent redirect (HTTP 301) to your new URL and your followers won't even notice. Their clients follow the redirect on their own, although it's a good idea to let them know so they update their #+FOLLOW:. It is a way in, not a requirement.
You can also run it on your own server, with public sign-up disabled and your own domain.
And it has an advantage that seasoned app developers will have guessed already: it makes syncing between devices easy. You can, for example, write in the mobile client and carry on in the Emacs one with simple HTTP requests. No old protocols, no third-party APIs.
Do you use an RSS/Atom reader? Then you already know that the more feeds you follow, the slower it gets.
It doesn't matter how fast and parallel the downloads are. There will always be dropped connections, timeouts, errors and redirects. And if a feed is very large, the client has to parse all of it to keep what it cares about.
In my case, I follow 250 feeds. Download and parse them every time I scroll? Not viable. Cache them? Then the problem becomes when to invalidate the cache.
This is where the Mirror comes in. It is a server that keeps an up-to-date copy of every feed and saves you the parsing, sorting, thread building and mention consolidation. It exposes a simple REST API that returns JSON ready to use in a client.
You can think of it as a client of clients, or a general-purpose client without an interface. Internally, it works non-stop to keep the most recent copy and to prepare the processed information so it is easy to consume.
For example, if I want to build the timeline with Bob's and Carol's posts from the last 14 days:
curl --get "https://mirror.org-social.org/api/posts/" \
--data-urlencode "following=https://bob.com/social.org" \
--data-urlencode "following=https://carol.net/social.org" \
--data-urlencode "sinceDays=14"
It doesn't replace the Relay. In fact, it needs it to know which feeds exist and which ones have changed. And most importantly: it is read-only. It never edits or deletes anything at the source. To publish, you still write to your own file. Its only way in is a webhook that a client can call to say a feed has changed and the Mirror should update it:
curl -X POST "https://mirror.org-social.org/api/webhook/refresh/" \
-H "Content-Type: application/json" \
-d '{"feed": "https://alice.com/social.org"}'
It is optional. The client calls it right after publishing or uploading a new version of the file to your hosting, so the Mirror updates its copy straight away instead of waiting for the next refresh cycle.
The Relay already sends real-time notifications with Server-Sent Events, which clients can listen to in order to tell you about mentions, replies and reactions. But phones don't let an app keep connections open in the background, so there you need push notifications. It is the most specific layer of all, since each platform, iOS and Android, has its own notification system with its own certificates and tokens.
If this layer goes down, all you lose is the convenience of getting alerts on your phone while the app is closed.
An Org Social post has an address in the format https://<host>/social.org#<timestamp>. For example, my first post: https://host.org-social.org/andros/social.org#2025-08-14T09:11:00+0200. It works fine between Org Social clients, but what happens if you want to share it on another social network? Nothing pretty: the whole social.org file gets shared.
Live Preview turns a post into a web page. You give it the post URL, it downloads the original social.org (straight from the source, not through the Relay), finds the post and generates an HTML card with its Open Graph metadata: nick, avatar, text and date. Ready to share on any social network.
For example, the URL above becomes:
https://preview.org-social.org/?post=https%3A%2F%2Fhost.org-social.org%2Fandros%2Fsocial.org%232025-08-14T09%3A11%3A00%2B0200
:quality(85)/https://andros.dev/media/blog/2026/10/org-social-live-preview.png)
And if I share it on social media, it looks like this:
:quality(85)/https://andros.dev/media/blog/2026/10/org-social-live-preview-whatsapp.png)
Clients also use this link format in their share option.
The same need gave birth to another very interesting feature: generating an HTML blog from your social.org, if it is hosted on host.org-social.org.
For example, my Org Social web version would be preview.org-social.org/blog/andros.
:quality(85)/https://andros.dev/media/blog/2026/10/org-social-live-preview-blog.png)
You write in Org Mode, upload the file to the hosting (with a simple curl if you like) and your HTML blog is generated automatically, ready to share. It isn't perfect, since you don't control the design, but it saves you from running a static site generator.
In the end, they are not layers, but pieces orbiting around your social.org.
flowchart TB
File["social.org"]
Client["Client"]
Relay["Relay"]
Host["Host"]
Mirror["Mirror"]
Push["Push"]
Preview["Live Preview"]
Client -- "reads and writes" --> File
Client -- "uploads and syncs" --> Host
Host -- "hosts" --> File
Host -- "registers feeds" --> Relay
Relay -- "indexes" --> File
Client -- "mentions, threads and search" --> Relay
Mirror -- "which feeds exist and which change" --> Relay
Mirror -- "copies" --> File
Client -- "JSON timeline and webhook" --> Mirror
Relay -- "real-time notifications" --> Push
Push -- "phone alerts" --> Client
Preview -- "reads" --> File
Client -- "share link" --> Preview
| Piece | What it gives you | If you remove it |
|---|---|---|
social.org |
Your identity and your posts | There is no network |
| Client | Reading and writing comfortably | You read with curl and edit by hand |
| Relay | Discovery, mentions, threads and bridges | You only see who you follow |
| Host | Hosting without a server and syncing between devices | You use your own hosting |
| Mirror | Ready-to-use JSON, no Org parsing | The client downloads and parses every file |
| Push | Alerts on your phone | You find out when you open the app |
| Live Preview | Sharing posts outside the network | You share the raw file URL |
Whatever you use, the only essential piece is your social.org.
If every Org Social server shut down tomorrow, what would you lose? Convenience. And what would you keep? Your file, intact, on your disk. You upload it anywhere else and keep posting.
Few social networks can say the same.
Help me keep writing Every coffee gives me a push toward the next article. Sure, it's on me!
Send an email to comment+article-6c165f77@andros.dev to leave a comment. The subject will be ignored.
-1:-- Org Social: the social network that survives its own infrastructure (Post Andros Fenollosa)--L0--C0--2026-10-14T15:12:18.000Z
To my delight I found that emacs-multi-compile will combine the of all matches compilation commands!
I added this to my setup
(add-to-list 'multi-compile-alist
'((mes/is-in-git-project) . (("git clean -n" "git clean -ndx" (project-root (project-current)))
("git clean -f" "git clean -fdx" (project-root (project-current))))))
and those commands are now shown together with the ones I defined for Haskell project (see my previous post) when both match.
To add the git commands to multi-compile-list I needed a function to determine
if the current buffer is in a git working tree. I failed to find something
fitting so I ended up adding this little function.
(defun mes/is-in-git-project ()
"Check if we're currently in a git project."
(when-let* ((proj-dir (project-root (project-current))))
(vc-git-root proj-dir)))
I'm using display-buffer-alist to control how Emacs places buffers. For
compilation mode I had the following actions.
(display-buffer-reuse-mode-window display-buffer-in-side-window)
(mode compilation-mode)
(window-height . 0.25)
(side . bottom)
(post-command-select-window . t)
That last command made switching buffers with consult-buffer a bit irritating
as previewing would interrupt the buffer switching by selecting the compilation
window. However, I really want the compilation window selected after a
compilation. Or rather, as I realised after reflecting a bit, I want the
compilation window selected if there are errors.
Compilation mode has a way of running functions when a compilation finishes so I
defined two functions and added them to compilation-finish-functions.
(defun mes/close-successful-compilation (buf msg)
"Close compilation buffer after success."
(when (and (buffer-live-p buf)
(with-current-buffer buf (eq major-mode 'compilation-mode))
(string-match "finished" msg))
(run-with-timer 3 nil
(lambda ()
(kill-buffer buf)))))
(defun mes/select-failed-compilation (buf msg)
"Select compilation buffer after failed compilation."
(when (and (buffer-live-p buf)
(with-current-buffer buf (eq major-mode 'compilation-mode))
(string-match "abnormally" msg))
(select-window (get-buffer-window buf))))
(add-to-list 'compilation-finish-functions #'mes/close-successful-compilation)
(add-to-list 'compilation-finish-functions #'mes/select-failed-compilation)
With that a successful compilation is closed automatically after 3s and on a failed compilation the compilation buffer is selection.
-1:-- Emacs salmagundi, 2026-10-11 (Post Magnus)--L0--C0--2026-10-13T19:46:00.000Z
The ever-officious minions have staged what they’re calling an intervention. They told me that I’ve milked this illness shtick long enough and that it was time to get back to work. They were unmoved by my protestations that I was not yet 100 percent and merely pointed at my computer. They obviously don’t understand the meaning of “minion”. Anyway, I’m trying to get things back to normal.
James Cherti has announced a simple package that trades Org mode’s heading stars for UTF-8 bullets. If you’re like me, your first thought is, “Doesn’t that already exist?” Sort of. What already exists is the no longer maintained org-bullets. Cherti’s package is called org-ibullets (note the i).
Cherti explains the differences from org-bullets in his announcement. The TL;DR is that he’s optimized org-ibullets by eliminating redundant computations, eliminated spurious syntax warnings that org-bullets could produce, and made some improvements at the source level.
As I said, the package is simple. It can be used right out of the box but you can customize the list of bullets and their face if you desire. This seems like a very nice package if you’d like to make your Org buffers look a little nicer.
-1:-- Org-ibullets (Post Irreal)--L0--C0--2026-10-13T15:45:11.000Z
It's October. New month, new agent-shell update. If you missed the last post, have a look at the 0.78 update. While the latest changes are focused primarily on bugfixes and polish, we have a handful of new features too.
As usual, this post showcases highlights, but please check out the full list of changes if you're after the nitty-gritty.
agent-shell?agent-shell brings your favorite AI agents to Emacs, keeping you at home: familiar buffers and bindings, no terminal emulation. All powered by ACP, the Agent Client Protocol.
agent-shellI introduced agent-shell just over a year ago, with support for two agents: Claude and Gemini. Today, it speaks to 23.
Development hasn't let up either. Improvements and new features land every month.
Thank you to all users, contributors, and sponsors.
A couple more join the family, bringing it to 23 agents. As usual, they're all reachable via M-x agent-shell, or go straight to the new ones:
M-x agent-shell-junie-start-agent.M-x agent-shell-openclaw-start-agent.
With the ability to prompt at any time (see my last post), you can queue prompts or steer agents straight from the persistent shell prompt. Animating the prompt ties it all together: it's now obvious at a glance that the agent is busy, and that anything else you submit will queue.
Check out agent-shell-prompt-busy-frames for relevant customizations (more on that shortly).

Thanks to @cark for the proposal.
The header's busy indicator has been animated for some time now. Amongst the existing customizations, agent-shell-show-busy-indicator could turn it off altogether. Having said that, there's now a middle ground: use static text via agent-shell-busy-indicator-frames.
Just set it to a string like so:
(setopt agent-shell-busy-indicator-frames "(busy)")
If you'd like to use smarter logic, you can now set it to a function as follows:
(setopt agent-shell-busy-indicator-frames
(lambda ()
(if (agent-shell-session-id)
"(busy)"
"(connecting)")))
agent-shell-prompt-busy-frames takes all the same forms, so the prompt's own animation can also be a list of frames, a static string, or a function.
With the new agent-shell-prompt-queue-merge setting (enabled by default), pending prompts are joined (separated by blank lines) and submitted as a single turn. Queued prompts are typically follow-ups to the same instruction, so the agent now sees them together rather than acting on the first before reading the rest.
If you prefer the previous behaviour, disable it with:
(setopt agent-shell-prompt-queue-merge nil)
Thank you @Gleek for the proposal.
While on the subject of queueing, cancelling now offers to continue with the queue rather than quietly dropping it, and killing a shell now warns you about the pending work you'd lose.
@ and / completion used to bundle two things: how to complete, and when to pop up. Turning the popping off lost completion altogether, leaving corfu and company users out.
They're now decoupled. The completion functions are always registered, so completion-at-point, corfu and company work either way. Two options decide whether completion pops as you type: agent-shell-file-completion-enabled for @ and agent-shell-command-completion-enabled for /. For example, listing files can be slow in a large project, so to only complete @ on demand while / keeps popping:
(setopt agent-shell-file-completion-enabled nil)
Trailing spaces (and @ mention quoting) are now only inserted on a finished completion, fixing the stray spaces corfu and company users were seeing.
Thanks to @bcc32 for proposing the split and separate @ and / options in #537, and to @jcubic for filing it and flagging the stray spaces.
C-M-a and C-M-e (beginning-of-defun and end-of-defun) now move between blocks. Thank you @OSadovy for #876.
Chat mode draws "Me" and "Agent" labels (or rather "Claude", "Codex" etc.). If the current style isn't your jam, agent-shell-chat-label-function now lets you draw them yourself. It's handed an alist with :text and :role (either user or agent), and must return a single-line propertized string.
The display property is honored, so labels can also be rendered as images. Thanks to @nhojb for the idea.
Transcripts now open with YAML frontmatter, including a start timestamp as follows:
---
agent: "Claude"
started: "2026-10-11T09:30:12+01:00"
working_directory: "~/stuff/active/code/agent-shell"
session_id: "0f3c9a1e-5b7d-4e2a-9c81-6d4f2b8e7a35"
model: "claude-opus-4-5"
---
Thank you @vermiculus for #854.
While on topic, transcripts also got a little sturdier, thanks to @mrcnski's proposal.
M-x yank-media supportM-x yank-media now pastes images straight from the system clipboard (#853), no external utilities needed, the same way Org mode handles it. Pasted image previews also survive being queued or steered, thanks to @Gleek for #852.
Codex gained agent-shell-openai-default-config-options for setting config options at session start, reasoning effort included. For example:
(setopt agent-shell-openai-default-config-options
'(("reasoning_effort" . "high")))
Thank you @ianthetechie for #855.
Tool call output can be fairly long. A single chunky line was enough to bog a buffer down. Long tool output lines are now limited (#844 by @Gleek), and fallback titles are truncated so they no longer flash chunkily as they stream.
File summaries also count distinct files now, so reading the same file three times counts as one file rather than three (#863, also by @Gleek).
While we have a handful of new features, the bulk of this release went into fixes.
Rendering:
M-x agent-shell-copy-as-markdown no longer mangles bullets carrying bold text (#867).Performance and responsiveness:
path:line references are now found in linear time (#848 by @catern), and failed searches leave point where it was.visual-wrap-prefix-mode enabled no longer hangs Emacs (#842).Elsewhere:
display-buffer-overriding-action is preserved when displaying asynchronously (#846 by @bcc32), and docstring command binding specifiers are fixed (#845, also by @bcc32).tramp-remote-path and direct async processes rather than a bug.New shells now show the last few release posts on the welcome screen, so they are a little more discoverable.

A few additions for package authors building on agent-shell.
agent-shell-initialized-p returns non-nil once the handshake, authentication, session and any default options are all done. Until then, subscribe to the new init-finished event via agent-shell-subscribe-to (#860).
Agent config options are now reachable from elisp rather than the UI alone:
(agent-shell-config-options)
(agent-shell-config-option :category "thought_level")
(agent-shell-config-option-value :id "effort")
(agent-shell-set-config-option-value :id "effort" :value "high"
:on-success (lambda (_) (message "Set")))
agent-shell-last-activity-time returns when the shell last saw a submitted prompt or an incoming notification (#856), handy for idle tracking.M-x agent-shell-save-traffic writes the session's ACP traffic to a .traffic file and reveals it in dired, simplifying the bug-filing process.agent-shell-gc joins the lot, bringing buffer garbage collection (auto-kill) to agent-shell.
If you peeked at the commit logs for the period, you'll see it's been a busy few weeks. Since the last post, 125 commits shipped, 33 issues have been closed and 15 pull requests merged. As of this writing, the backlog sits at 4 open issues and 6 open PRs (versus 16 and 6 last time around).
Zooming out a little, here's how the backlog has tracked:

As usual, this chart was generated using the /github-activity skill shared in my emacs-skills repo.
Four open issues is the lowest it's been since the project's first week, which I'm pleased about.
All of this requires daily attention.
agent-shell.Tokens keep your agents running. Sponsors keep agent-shell running.
agent-shell is built and maintained by me, an indie dev, while the tools it competes with have well-funded teams behind them. Vendor-neutral tooling matters more than ever.
Using agent-shell at work? Ask your employer to sponsor too. Companies can contribute at a scale individuals can't.
Thank you to all contributors for these improvements!
display-buffer-overriding-action when displaying asynchronously (@bcc32)agent-shell stands on acp.el to speak ACP and shell-maker for its shell foundations, so fixes landing there reach you just the same:
agent-shellAs of this writing, we're 88% of the way to the 150 monthly sponsors goal. If agent-shell is part of your daily workflow, please consider becoming a monthly sponsor (or ask your employer to).
Not in a position to sponsor? A GitHub star helps with exposure and brings in new users.
-1:-- agent-shell 0.87 updates (Post Alvaro Ramirez)--L0--C0--2026-10-13T00:00:00.000Z
-1:-- org-ibullets.el - Display Emacs org-mode heading stars as UTF-8 bullets (Post James Cherti)--L0--C0--2026-10-12T17:30:47.000Z
-1:-- wizard.el - A Lightweight suite of useful Emacs functions (Post James Cherti)--L0--C0--2026-10-12T17:19:21.000Z
People have been having lots of fun playing Emacs Golf over in r/emacs. Check out the "Fun" category for some examples of people comparing how few keystrokes they need in order to get something done.
Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, Mastodon #emacs, Bluesky #emacs, Hacker News, lobste.rs, programming.dev, lemmy.world, lemmy.ml, planet.emacslife.com, YouTube, the Emacs NEWS file, Emacs Calendar, and emacs-devel. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at sacha@sachachua.com. Thank you!
*
You can e-mail me at sacha@sachachua.com.
-1:-- 2026-10-12 Emacs news (Post Sacha Chua)--L0--C0--2026-10-12T11:58:28.000Z
Please note that planet.emacslife.com aggregates blogs, and blog authors might mention or link to nonfree things. To add a feed to this page, please e-mail the RSS or ATOM feed URL to sacha@sachachua.com . Thank you!