Rahul Juliato: Test-Driving Fonts in Emacs with a Transient Menu

Intro

I usually stick with some favorite font for a while, then, for some reason, the bug bites: some new activity, different hardware or work, and I want to change the default font.

I have this old file with content like this:

(set-face-attribute 'default nil :family "3270 Nerd Font Mono")
(set-face-attribute 'default nil :family "GoMono Nerd Font")
(set-face-attribute 'default nil :family "Hack Nerd Font Mono")
(set-face-attribute 'default nil :family "Iosevka Nerd Font Mono")
(set-face-attribute 'default nil :family "JetBrainsMono Nerd Font")
;; ... and a bunch more

What I do (shame on me) is put the cursor at the end of a line, C-x C-e, switch back to the buffer or tab I was working on, look around for a while, switch back to the file, move down one line, C-x C-e again... You get the idea. It works, but it's clunky, and changing the size means editing the line and adding :height to it.

I wanted a way to scrub through my font list and increase or decrease the font height using only the arrow keys, while I check live what feels right.

That's a perfect job for a transient menu, which has shipped with Emacs since version 28, written by the one and only Jonas Bernoulli, author of nothing less than Magit.

This post is a walkthrough of how you can implement such functionality yourself, and hopefully a guide along your Elisp learning journey.

TL;DR: jump straight to the full source, put it in your init file, edit the font list, and run M-x font-cycle.

Font Candidates

First, we build a list of font families:

(defvar font-cycle-families
  '("3270 Nerd Font Mono"
	"GoMono Nerd Font"
	"Hack Nerd Font Mono"
	"Hurmit Nerd Font Mono"
	"Inconsolata LGC Nerd Font"
	"Iosevka Nerd Font Mono"
	"IosevkaTerm Nerd Font Mono"
	"JetBrainsMono Nerd Font"
	"Lekton Nerd Font Mono"
	"M+CodeLat50 Nerd Font"
	"Maple Mono NF"
	"MartianMono Nerd Font"
	"SauceCodePro Nerd Font"
	"UbuntuMono Nerd Font Mono"
	"VictorMono Nerd Font Mono"
	"ZedMono Nerd Font Mono")
  "Font families to cycle through with `font-cycle'.")

If you don't know the exact family name of a font you have installed, and how it is "perceived" by Emacs, you can use M-x describe-font with completion, or M-: (font-family-list).

If a family on that list is not installed (a typo, another machine, a font you uninstalled), set-face-attribute won't complain. Emacs just falls back to some other font, and you end up judging the wrong thing. So, before cycling, we keep only the families Emacs can actually find:

(defun font-cycle--installed ()
  "Return the members of `font-cycle-families' installed on this system."
  (seq-filter (lambda (family) (find-font (font-spec :family family)))
			  font-cycle-families))

Left and Right (fonts) Cycling

Cycling is an index and a mod. The mod is what makes it wrap around: going right from the last font takes you to the first one, and going left from the first takes you to the last.

(defvar font-cycle--index 0
  "Position of the current font in the installed families.")

(defun font-cycle--select (index)
  "Use the installed family at INDEX, wrapping around both ends."
  (let ((fonts (font-cycle--installed)))
	(unless fonts
	  (user-error "None of `font-cycle-families' is installed"))
	(setq font-cycle--index (mod index (length fonts)))
	(set-face-attribute 'default nil
						:family (nth font-cycle--index fonts))))

(defun font-cycle-next ()
  "Switch to the next font."
  (interactive)
  (font-cycle--select (1+ font-cycle--index)))

(defun font-cycle-previous ()
  "Switch to the previous font."
  (interactive)
  (font-cycle--select (1- font-cycle--index)))

Note that mod in Elisp follows the sign of the divisor, so (mod -1 16) is 15, not -1. This is what we want for going left.

Up and Down (height) Cycling

For the size, we change the :height of the default face. Its unit is 1/10 of a point, so a step of 10 is one point:

(defun font-cycle--resize (delta)
  "Change the default face height by DELTA (1/10 pt)."
  (set-face-attribute 'default nil
					  :height (+ (face-attribute 'default :height) delta)))

(defun font-cycle-bigger ()
  "Increase the font size by 1pt."
  (interactive)
  (font-cycle--resize 10))

(defun font-cycle-smaller ()
  "Decrease the font size by 1pt."
  (interactive)
  (font-cycle--resize -10))

Keeping the Winner

When I finally find a combination I like, I don't want to remember and retype it. So one more command builds the exact line for my init file and puts it in the kill ring for later yanking:

(defun font-cycle--form ()
  "Return the `set-face-attribute' call for the current font."
  (format "(set-face-attribute 'default nil :family %S :height %d)"
		  (face-attribute 'default :family)
		  (face-attribute 'default :height)))

(defun font-cycle-copy ()
  "Copy the current font setting as Elisp to the kill ring."
  (interactive)
  (kill-new (font-cycle--form))
  (message "Copied: %s" (current-kill 0)))

Then it's just a C-y in init.el.

The Transient Menu

Now let's glue everything together. Each suffix marked with :transient t runs its command and keeps the menu open, so you can press the arrows as many times as you want. y and q don't have it, so they close the menu.

(transient-define-prefix font-cycle ()
  "Cycle through `font-cycle-families' and adjust the font size."
  [:description font-cycle--description
   ["Font"
	("<right>" "next" font-cycle-next        :transient t)
	("<left>" "previous" font-cycle-previous :transient t)]
   ["Size"
	("<up>" "bigger" font-cycle-bigger       :transient t)
	("<down>" "smaller" font-cycle-smaller   :transient t)]
   ["Done"
	("y" "copy as elisp" font-cycle-copy)
	("q" "quit" transient-quit-one)]])

The :description of the outer group is a function, and transient calls it again every time the menu is redrawn. That gives us a header that always tells which font is on screen, its size, and where we are in the list:

(defun font-cycle--description ()
  "Header showing the current font, its size and position in the list."
  (format "%s  %dpt  [%d/%d]"
		  (propertize (face-attribute 'default :family) 'face 'bold)
		  (/ (face-attribute 'default :height) 10)
		  (1+ font-cycle--index)
		  (length (font-cycle--installed))))

Run M-x font-cycle and you get something like this at the bottom of the frame:

IosevkaTerm Nerd Font Mono  16pt  [7/16]
Font             Size            Done
 <right> next     <up>   bigger   y copy as elisp
 <left>  previous <down> smaller  q quit

Since the menu lives at the bottom, everything above it is your real work: the buffers you were editing, your theme, your splits. Tap right, look, tap right again. If you like it, bump it up a point or two. When you're done, y and paste.

If you want it one key away:

(keymap-global-set "C-c f" #'font-cycle)

Full Source

;;; font-cycle.el --- Test-drive fonts with a transient menu  -*- lexical-binding: t; -*-

(require 'transient)

(defvar font-cycle-families
  '("3270 Nerd Font Mono"
	"GoMono Nerd Font"
	"Hack Nerd Font Mono"
	"Hurmit Nerd Font Mono"
	"Inconsolata LGC Nerd Font"
	"Iosevka Nerd Font Mono"
	"IosevkaTerm Nerd Font Mono"
	"JetBrainsMono Nerd Font"
	"Lekton Nerd Font Mono"
	"M+CodeLat50 Nerd Font"
	"Maple Mono NF"
	"MartianMono Nerd Font"
	"SauceCodePro Nerd Font"
	"UbuntuMono Nerd Font Mono"
	"VictorMono Nerd Font Mono"
	"ZedMono Nerd Font Mono")
  "Font families to cycle through with `font-cycle'.")

(defvar font-cycle--index 0
  "Position of the current font in the installed families.")

(defun font-cycle--installed ()
  "Return the members of `font-cycle-families' installed on this system."
  (seq-filter (lambda (family) (find-font (font-spec :family family)))
			  font-cycle-families))

(defun font-cycle--select (index)
  "Use the installed family at INDEX, wrapping around both ends."
  (let ((fonts (font-cycle--installed)))
	(unless fonts
	  (user-error "None of `font-cycle-families' is installed"))
	(setq font-cycle--index (mod index (length fonts)))
	(set-face-attribute 'default nil
						:family (nth font-cycle--index fonts))))

(defun font-cycle-next ()
  "Switch to the next font."
  (interactive)
  (font-cycle--select (1+ font-cycle--index)))

(defun font-cycle-previous ()
  "Switch to the previous font."
  (interactive)
  (font-cycle--select (1- font-cycle--index)))

(defun font-cycle--resize (delta)
  "Change the default face height by DELTA (1/10 pt)."
  (set-face-attribute 'default nil
					  :height (+ (face-attribute 'default :height) delta)))

(defun font-cycle-bigger ()
  "Increase the font size by 1pt."
  (interactive)
  (font-cycle--resize 10))

(defun font-cycle-smaller ()
  "Decrease the font size by 1pt."
  (interactive)
  (font-cycle--resize -10))

(defun font-cycle--form ()
  "Return the `set-face-attribute' call for the current font."
  (format "(set-face-attribute 'default nil :family %S :height %d)"
		  (face-attribute 'default :family)
		  (face-attribute 'default :height)))

(defun font-cycle-copy ()
  "Copy the current font setting as Elisp to the kill ring."
  (interactive)
  (kill-new (font-cycle--form))
  (message "Copied: %s" (current-kill 0)))

(defun font-cycle--description ()
  "Header showing the current font, its size and position in the list."
  (format "%s  %dpt  [%d/%d]"
		  (propertize (face-attribute 'default :family) 'face 'bold)
		  (/ (face-attribute 'default :height) 10)
		  (1+ font-cycle--index)
		  (length (font-cycle--installed))))

(transient-define-prefix font-cycle ()
  "Cycle through `font-cycle-families' and adjust the font size."
  [:description font-cycle--description
   ["Font"
	("<right>" "next" font-cycle-next        :transient t)
	("<left>" "previous" font-cycle-previous :transient t)]
   ["Size"
	("<up>" "bigger" font-cycle-bigger       :transient t)
	("<down>" "smaller" font-cycle-smaller   :transient t)]
   ["Done"
	("y" "copy as elisp" font-cycle-copy)
	("q" "quit" transient-quit-one)]])

(provide 'font-cycle)
;;; font-cycle.el ends here

What Could be Improved

A few extras, if you want to practice some Elisp:

  • Replace the index and mod with Emacs' built-in ring.el.
  • Show the visible columns and lines in the header, so you know how much fits on screen with each font and size.
  • Start cycling from the font you are currently using.
  • Add a key that restores the font and size you had before opening the menu.
  • Cycle themes, or a second list for the variable-pitch face, the same way.
  • Turn it into a publishable package: defcustoms, an autoload, proper headers, and a clean checkdoc and byte-compile.

Honorable Mentions

Font picking seems to be in the air lately. If you'd rather use something ready-made, or just want to see other takes on the idea, check these out:

  • show-font, by Protesilaos, on GNU ELPA: previews installed fonts in a dedicated buffer, and show-font-tabulated lists every font rendered in its own typeface.
  • flick, "a quick and simple font picker", by Vegas Harmon, recently shared on r/emacs.
  • This r/emacs comment, from a recent discussion on the same subject.

Wrapping Up

There's nothing specifically clever here: a list, an index, a mod, and a transient menu holding it all together.

I hope this educational post helps you with the basics of using transient, and maybe teaches you something new about Emacs Lisp along the way.

As for me, I'm currently on font 7 of 16. Ask me again next month.

-1:-- Test-Driving Fonts in Emacs with a Transient Menu (Post Rahul Juliato)--L0--C0--2026-10-31T23:00:00.000Z

Irreal: Highlighting The Active Window

Three years ago, I wrote about marking the active window (update here ) so that it’s easy to pick it out from the other, inactive windows. I did that by making the mode line of the active window a distinct color instead of the slightly different shade of gray from the other windows. That’s worked out really well for me and I’ve never looked back.

Now Ray Zeitler over at Ray on Emacs takes another look at the problem and found a really nice solution. It’s a bit more in your face than mine so if you’re looking for something that makes the active window really stand out, you should take a look at his post. The TL;DR is that instead of changing the active window, he changes the background color of all the other windows.

Zeitler found his solution on a 10 year old Stack Exchange post so it’s pretty certain to work no matter what version of Emacs you’re using. The code is very simple and you can simply copy it to your init.el if you’re interested.

As I said, this solution seems a little aggressive to me so I doubt I’ll be implementing it but others may disagree. One thing for sure, it’s going to make your active window stand out. It’s a small enough change that you can try it out—say by copying it to your scratch buffer and executing it—to see if it’s something you’d like.

-1:-- Highlighting The Active Window (Post Irreal)--L0--C0--2026-10-31T14:42:26.000Z

Sacha Chua: emacs.news

Now you can use emacs.news to get to Emacs News. That should save some clicking/typing. It redirects to my blog category at the moment, so the URL doesn't show emacs.news after you get there, but I've added it to the note at the top of that category page to help people find out about the shortcut.

Thanks to @xenodium for the nudge!

View Org source for this post

You can e-mail me at sacha@sachachua.com.

-1:-- emacs.news (Post Sacha Chua)--L0--C0--2026-10-31T13:46:49.000Z

Einar Mostad: Adapting to text-based browser

I stumbled across Andros Fenollosa's blog a while ago while thinking about doing more of my web browsing with EWW. He has written a post where he defines a standard for websites that read in the terminal he calls Termweb. It's well thought through and makes a lot of sense.

Eww isn't always a terminal browser, although it can be very useful in a TTY on a server to read up on the documentation while also using Emacs as a terminal multiplexer with config files you edit in another window and eshell, shell or ansi-term in a third window within the TTY. When I am in a graphical Wayland seesion, I use EWW in a GUI Emacsclient frame which means I can get different fonts and pictures as well as just text which you would get in EWW in Emacs in a terminal. (Of course, Lynx can show pictures in a graphical terminal as well through sixel and it is probably possible to hack EWW to use sixel like Lynx or use the abilities of newer terminals like Alacritty and Kitty to show pictures, but GUI Emacs has a few advantages more around keybindings, toolbars and the modeline as well as just pictures and fonts which is why most people prefer it when in a graphical session over terminal Emacs.)

I use the Emacs package org-static-blog to make my blog from org files. I also made a function to push changes to Codeberg pages via git. Org-static-blog already follows most of Fenollosa's advice. I additionally made a CSS stylesheet for graphical browsers when I started using it. I set a dark background, text that would contrast, and a few settings for the heading, footer and code blocks to stand out while still staying within a mostly monochromatic look. I think a coherent style, ie less CSS applied to more tags, IDs and classes, is better than tweaking everything individually.

Org-static-blog already makes HTML that renders well in a text-based browser and I chose to have my header on top, not on the side, when I made the CSS to make it look as similar as possible in a text-based browser as it looks in a graphical browser. Today I had a look at it again in EWW and I realised that the header and footer (or preamble and postamble as org-static-blog calls it) blends to much into the rest of the content of the page when read in a text based browser, so I added a horizontal line after the header and before the footer to separate it out. It looks good in EWW, but a bit unnecessary in a graphical browser. The obvious solution was to tweak the CSS to not show hr-tags inside the preamble and postamble. Then the header and footer look like they used to on graphical browsers, but they look better than before on text based browsers. After looking further at the header and footer, I felt that it did actually look quite stylish with the hr visible in my graphical browser as well, so I removed the extra CSS to remove it again.

My goal isn't to make my blog look good in text based browsers or graphical browsers, but both. Using a simple structure and following most of the rules by Andros Fenollosa helps to achieve both goals at once. If you have not had a look at your own site in EWW or Lynx and you have a bit of time to tweak some HTML or CSS, I recommend reading Fenollosa's post and have a go at tweaking things to follow most of his advice. Every reader you have that use EWW or a terminal text browser will thank you for making your site more readable.

-1:-- Adapting to text-based browser (Post Einar Mostad)--L0--C0--2026-10-31T11:22:00.000Z

Philip Kaludercic: On Whether Emacs Is Here To Stay?

This is my contribution to the October 2026 edition of the Emacs Blog Carnival. The theme is “Where and how will you be using emacs in the next year, two years, five years?”.

To step back and siuatate what we are talking about: Last year, I described Emacs as an Emancipatory Computing System: Emacs is a (1) uniform UI, (2) a transparent+accessible program and (3) a collaborative “community”. For me, these three attributes constitute the attractive and worthwhile aspects of Emacs. The fact that we got here is not a natural consequence of technical decisions. In fact, I’d argue that left to anarchy, Emacs would naturally devolve, but it is only due to the concious and intelligent mantainance of the core that the entropy of a system like Emacs can be counterbalanced.

Presently, I can see all these points being under threat in the coming years:

  1. The uniform UI, meaning text-based UI that can be navigated and manupulated with the same commands, or at least custom commands that ensure intuitive consistency (i.e. C-k might not kill a physical line in a special-mode-derived buffer, but k still “kills” a logical entry) has been suffering for the past few years with custom UIs like transient and I remain weary of what the new Canvas functionality in the upcoming Emacs 32 release will spur people to implement. With the exception of edge-cases like games (which just like with the built-in games in Emacs are more demonstrations than actual features) all feature that were to use a Canvas image for user input will have to re-implement navigation and input in a intuitive way while dealing with the issue that “inuitive” means different thing to differnt people, depending on what subset of navigation and manipulation keybindings you are used to.

  2. The transparency of Emacs has also been strained in the past few years: Initiatives such as Tree-Sitter in the core make it more difficult to make quick-paced changes to a language (which is the price you pay for improved and faster parsing) and even if you do change and re-build the grammar it is to my knowledge not possible to hot-swap a grammar without restarting the Emacs process since the grammar is packaged as shared objects.

    Outside of the core there are some projects that again that also try to speed up Emacs by implementing functionality outside of Emacs. One such example is lsp-bridge or Emacs application framework where the main logic of the program is written in a seperate Python programs.

    Of course it has always been the case that Emacs, as a kind of shell, integrated the surrounding system and invites the user to invoke shell commands both to interface with the outside system but also to manipulate the data within Emacs. I’d argue the difference with the previous examples is that we are not talking about already-existing generic and abstract tooling of our system like tr or make, but bespoke software that morally constitutes the same program but doesn’t enjoy the transparancy of both reading and changing the source code of the program as is the case with a traditional Elisp script.

  3. Regarding collaborative community, it is worth pointing out that this is not necessarily just the online community consisnting mostly of a few dozen to perhaps a few hundred people. It might as well be a network of friends or colleges where knowledge and worked is shared. In fact I have more hope for the latter, as the former is strained both by the conflict of pro-LLM vs anti-LLM people (leaving many by the wayside) but also the annoyance of figures in the public space that are clearly motivated by the need to constantly self-advertise.

    Specifically regarding LLMs, I think it is conservative to say that while LLMs can deal with the works on humans, it feels off for humans to deal with the output of an LLM. Their priorities, style and attention is plainly atypical for humans as at least currently it seems they share less of a need for abstraction and conciseness (which makes sense to me, as the current economic model of AI services is to sell tokens). This is all fine for a no-coder who generates scripts to implement features they desire, but for shared code where you cannot expect that no human won’t want to read and modify it themselves it becomes tricky – one option is not to care, but not caring about others is the detriment of a community.

So as for myself, and my relation to Emacs, it is of course difficult for me to imagine computing without it. In fact I get annoyed by a lot of other software, especially web services and mobile applications, due to my expectations. At the same time, I am aware that the compfort of Emacs has held me back from trying out software that lacked good support within Emacs — on some level I would be better off ignorant. Especially nowadays I find it more difficult than ever to predict the future of computing. I can anticipate that at my day-job (context: I am a PhD student in PL research working with Rocq) I will be continuting to use Emacs along with Proof General for the forseable future. But on a much larger and high-level scheme, the question of how we use computers in general and what role traditioanl software will play remains open to me.

(As an aside: The (software) complexity crisis has been worsening outside of specific, mostly autarchic circles (Emacs, while not immute, is has arguably stood it’s ground better) and the solution has never been to write more code faster — but let us also not forget that specifically with the LLM situation, code generation isn’t and shouldn’t be the end-game, instead I still belive that it is a marketing approach to sell the usage of generative AI in situation that can be well trained, so as to fund the continuted research and development — while also making a lot of money (the two are the cynical flipsides of each-other). Nevertheless I am entierly willing to concede that we might be dealing with a non-backwards compatible paradigm shift on how we compute, and that if we overcome the “LLM-generating-code” phase we could live in a future where the CPU becomes the co-processor to a LLM-centric user interface where something like Emacs wouldn’t be relevant anymore.)

If a space will continue to exist where something like Emacs is an adequate and useful tool to interface with computers, I am confinent that Emacs will continue to exist. But beyond that I hope that as both local communities and the global community we can come together in a shared understanding of what it is that we value and ensure that we do not undermine it or better yet forster them.

-1:-- On Whether Emacs Is Here To Stay? (Post Philip Kaludercic)--L0--C0--2026-10-31T00:44:03.000Z

Protesilaos: Emacs live new Halloween theme for ‘ef-themes’ at 2026-10-31 14:00 Europe/Athens

Raw link: https://www.youtube.com/watch?v=_sUctSSXBhU

[ The stream will be recorded. You can watch it later. ]

This is a spontaneous live stream. I plan to develop at least one new theme for my ‘ef-themes’ package which will be in the spirit of Halloween. Depending on my progress, I will do two themes, actually.

I will keep the chat open, in case there are any questions related to this or anything else—I always answer everything.

The ef-halloween-dark prototype

I made some more tweaks and published the dark theme. There is more to do, but this is a good start. The light theme needs more work. Maybe during another live stream.

commit 3a9898e23da62d14b57faeae1af9ae05bb3c59f3
Author: Protesilaos <info@protesilaos.com>
Date:   Sat Oct 31 21:07:01 2026 +0200

  Add PROTOTYPE of ef-halloween-dark
  
  I did most of this during the live stream earlier today:
  <https://protesilaos.com/codelog/2026-10-31-emacs-live-ef-themes-halloween/>.

 ef-halloween-dark-theme.el | 242 +++++++++++++++++++++++++++++++++++++++++++++
 ef-themes.el               |   1 +
 2 files changed, 243 insertions(+)

The ef-halloween-light prototype

And this is from the day after.

commit dd0c2ee019e79d5541e1a3e563cd27c8a744c1ae
Author: Protesilaos <info@protesilaos.com>
Date:   Sun Nov 1 07:51:01 2026 +0200

  Add ef-halloween-light PROTOTYPE

 contrasts.org               |  36 +++++++
 ef-halloween-light-theme.el | 242 ++++++++++++++++++++++++++++++++++++++++++++
 ef-themes.el                |   1 +
 3 files changed, 279 insertions(+)

About the ef-themes

The ef-themes are a collection of light and dark themes for GNU Emacs that provide colourful (“pretty”) yet legible options for users who want something with a bit more flair than the modus-themes (also designed by me).

Starting with version 2.0.0, the ef-themes are built on top of the modus-themes. This means that all customisation options of the Modus themes apply to the Ef themes. Same for all Modus commands that load a theme. Enable ef-themes-take-over-modus-themes-mode to set up this arrangement (or enable modus-themes-include-derivatives-mode instead to blend Ef and Modus into one collection).


🔷 Consider making a donation.

-1:-- Emacs live new Halloween theme for ‘ef-themes’ at 2026-10-31 14:00 Europe/Athens (Post Protesilaos)--L0--C0--2026-10-31T00:00:00.000Z

Protesilaos: Emacs: ef-halloween-dark is in ef-themes.git

I designed the basis of this theme during a live stream earlier today: https://protesilaos.com/codelog/2026-10-31-emacs-live-ef-themes-halloween/. Then I followed it up with refinements to colour values and palette mappings. The light counterpart of this theme will be the subject of further development work, maybe tomorrow or next week.

When I include “light” or “dark” in the theme’s name, it means that I have envisaged it as part of a pair. Most themes are not designed as pairs though, as certain colours cannot work in both cases.

Always click to enlarge the image for best results.

ef-halloween-dark

ef-halloween-dark theme sample

ef-halloween-dark theme git sample

ef-halloween-dark theme mail sample

ef-halloween-dark theme org sample

Coming in version 2.3.0 (next stable release)

I may still tweak it further, but the character is already set. It captures the style I had in mind and I am happy with the result.

Remember that since version 2.0.0, the ef-themes are built on top of my modus-themes. This means that they are highly customisable, support a wide range of packages and face groups, and are extensively tested down to the finest details.

Enjoy!


The ef-themes are a collection of light and dark themes for GNU Emacs that provide colourful (“pretty”) yet legible options for users who want something with a bit more flair than the modus-themes (also designed by me).


🔷 Consider making a donation.

-1:-- Emacs: ef-halloween-dark is in ef-themes.git (Post Protesilaos)--L0--C0--2026-10-31T00:00:00.000Z

Nicolas Cavigneaux: Revisiting My Doom Emacs Configuration

My Emacs printed this after starting:

Doom loaded 286 packages across 71 modules in 1.713s

Another machine running Arch, apparently less powerful, started Emacs in under a second. I wondered whether macOS was responsible.

I had no controlled comparison between the machines. Different builds, packages and caches could all matter. But I did have a configuration I could inspect, and it turned out to contain work I was asking Emacs to do before I needed it.

The startup investigation became part of a wider review of my Doom configuration. I keep its source in config.org; config.el is generated from it. Reading the prose beside each customization made the review more useful: I could compare what I said I wanted with what the installed code actually did.

The checks described below used Emacs 31.0.91. The source links point to the installed package revisions, rather than assuming a newer checkout would behave the same way.

What Doom’s startup message measures

The first useful finding was in Doom’s own reporting code. Its package count comes from this expression:

(- (length load-path) (length (get 'load-path 'initial-value)))

That counts added search-path entries. It does not mean that 286 packages had been fully loaded. The time is Doom’s doom-init-time, measured from Emacs’s before-init-time, rather than an external stopwatch covering the whole application launch. The implementation makes both definitions explicit.

All times below use Doom’s timer. Temporary graphical instances ran my configuration with instrumentation in a temporary directory; my main session stayed open. Four dedicated reference starts reported 1.489, 1.397, 1.303 and 1.264 seconds, in that order. Another start of the original configuration, recorded while setting up the functional check, reported 1.618 seconds. These repeats did not reproduce the initial 1.713-second observation; I did not establish why that launch was slower.

Native compilation was available. The recorded reference starts had no garbage collections during the measured interval, and zero recorded GC time. Doom already defers collection during interactive startup, so raising a GC threshold again would not address the work I was seeing.

To locate that work, I timed load, require and process calls in separate instrumented runs. Those timings add overhead, so I used them to identify candidates, keeping the simpler reference runs for the startup comparisons.

Loading before I needed it

One require stood out: claude-code-ide, the package connecting Claude Code to Emacs. One instrumented run attributed about 467 ms to it, including its dependencies.

The package entry point requires its MCP integration. Following that into the handlers reaches diagnostics, which in turn attempts to load Flycheck. My timings showed those loads happening during startup. The time attributed to claude-code-ide already included the nested work; adding each dependency’s time to it would count the same work more than once.

I wanted the integration available from my leader menu. I did not need it loaded while Emacs was opening an unrelated buffer.

The durable change was one line:

 (use-package! claude-code-ide
+  :defer t
   :init

With :defer t, use-package skips adding the package to its implicit :load list, so the declaration no longer requires it at startup. That does not, by itself, create a way to load it later. Here, all nine commands I had bound had autoloads, including the menu command. Calling one could load the library containing its definition. The :defer handler distinguishes this from a numeric :defer, which schedules a load after an idle delay.

I kept the keybindings and my Ghostel backend selection in :init. They need to exist before the package loads. The call to claude-code-ide-emacs-tools-setup stayed in :config, to register the Emacs tools after loading.

That arrangement needed checking in a fresh instance. A package already loaded in my main session could hide a missing autoload. The check confirmed that the library was absent at startup, all nine bindings were available, and invoking the menu loaded the package. The Emacs MCP tools were registered and the backend was still Ghostel. This did not exercise a complete conversation with the Claude CLI.

The startup measurements were encouraging:

Configuration Starts Doom-reported time
Original, dedicated reference runs 4 1.264 to 1.489 s
Original, functional-check setup 1 1.618 s
Temporary variant deferring Claude 2 0.863 and 0.903 s
Real configuration, menu probes 2 0.767 and 0.799 s
Real configuration, final checks 2 0.768 and 0.873 s

The menu probes failed their functional assertions while the check was being developed. Their startup times still belong in the record: doom-init-time was set before those assertions ran. The later successful check establishes the behavior described above; a short startup time on its own would not establish that the integration still worked.

These are local observations from the review on 30 October 2026, with a small number of trials and no controlled cold-cache protocol. The table is not a promise that this edit always saves the difference between the fastest and slowest entries. It was enough evidence to keep the change, alongside the functional check.

The cost has also moved: the first use of the integration now has loading work to do. I did not measure that first-use latency separately.

Mise was another visible source of startup work, with 16 to 23 synchronous calls in the instrumented runs. Disabling it on its own produced one measurement of 1.267 seconds, within the dedicated reference range. Combining that with deferred Claude loading produced 0.781 seconds, also within the range reached by the deferral alone. Neither single trial established a repeatable benefit from disabling mise. I kept it enabled; only Claude’s loading was durably deferred in this startup experiment.

My configuration could now start in under a second on this Mac. That answered the practical question that sent me looking, even though the comparison with Arch remained uncontrolled.

The mode my hooks were missing

Startup had an obvious number attached to it. Some other customizations needed a different check: opening the kind of buffer I actually use.

In 2020, I wrote about binding M-RET to insert an Elixir pipe. I still want that shortcut. The function moves to the end of the line, inserts a new line and adds |> . But my customization targeted elixir-mode, while my enabled Doom module also uses elixir-ts-mode.

The current hook covers both:

(add-hook! '(elixir-mode-hook elixir-ts-mode-hook)
  (local-set-key
   (kbd "M-RET")
   #'bounga/insert-elixir-pipe-operator))

I checked it in an Elixir Tree-sitter buffer by invoking the binding: foo became foo followed by a newline and |> . The Elixir docset was also available there. On the Ruby side, checks in both ruby-mode and ruby-ts-mode confirmed the Ruby/Rails docsets and subword-mode; M-f stopped after My in MyLongClass.

Finding the hook names in the file would not have told me that. The customization can look perfectly familiar while missing the buffer where I expect to use it.

I also removed an explicit elixir-format hook before saving. My Doom configuration enables (format +onsave +lsp). The installed format module enables Apheleia on first-file use, and Apheleia maps both Elixir modes to mix-format, with Doom also supporting formatting through an available LSP formatter. The old hook was a separate formatting path that I no longer needed.

In an isolated directory with Elixir 1.20.2 and OTP 29, saving a buffer containing foo( 1,2 ) through Apheleia produced foo(1, 2) in both modes, in the buffer and on disk. No LSP server was involved, so this exercised Apheleia’s mix-format path.

A smaller naming error showed the same need to inspect the actual integration. I had written after! tramp-mode. Doom’s after! waits for a feature to load; the relevant feature is tramp. Changing it to after! tramp made the remote-path customization run in the targeted check. A familiar mode name was the wrong name for that job.

Letting an explicit mail choice stand

The mail changes deserve more care because I had already published my automatic signing and encryption setup. That article described the policy I wanted, but the implementation needed correction.

The old send hook chose between signing and signing plus encryption without first checking for an existing MML security instruction. It could replace a choice I had made explicitly while composing the message.

The current policy is:

Message state Automatic action
An explicit MML security instruction is present Leave it alone
No explicit instruction, and every recipient has a usable encryption key Request signing and encryption
No explicit instruction, and the key check fails Request signing only

The hook now looks for an existing secure tag in the message body before applying its default. Tests reproduce the earlier replacement of explicit signing, encryption and combined instructions, then confirm that the new hook preserves each one.

Reading Emacs’s own message helpers exposed another duplication. The body of my recipient parser matched message-all-recipients. My availability predicate followed the built-in message-all-epg-keys-available-p, including its epg-list-keys check. Giving those functions my own namespace had not given me different behavior.

Emacs also provides message-sign-encrypt-if-all-keys-available, but it does nothing when the key check fails. My policy includes signing in that case. I kept my own hook for that fallback and for preserving an explicit choice, replaced the parser with message-all-recipients, and switched the key check to mml-secure-find-usable-keys for encryption.

Having a key in a search result is insufficient. The MML implementation checks the relevant user ID and encryption capability, excluding expired or revoked candidates and disabled keys. An empty recipient list now fails my predicate as well.

This still does not establish that a key belongs to the person I mean, or replace the trust policy used when encrypting. Availability, usability and trust are separate questions. The hook requests an action; it cannot guarantee that signing or encryption will succeed when the message is sent.

The checks used simulated keys, covering To, Cc and Bcc, missing keys and unusable candidates. Across the two targeted suites, ten of fifteen tests failed against the earlier functions; all fifteen passed against the current definitions extracted from config.org. No real keyring was read and no mail was sent. The suites test the decisions in my code; they do not exercise an end-to-end encrypted exchange.

Deciding when to stop

The terminal setting needed no replacement. I removed the old vterm-shell assignment after moving to Ghostel. I did not replace it with an explicit ghostel-shell: its default reads SHELL, with /bin/sh as a fallback, and selects the shell I want.

I also made the fullscreen request explicit with fullboth instead of toggling it. I had not reproduced a fullscreen failure; the edit made the requested state explicit. It would be misleading to count it alongside the mail bug as one more thing that had been broken.

The distinction matters to how I read the resulting diff. Some lines fix observed behavior. Others follow a change in what I run, such as replacing vterm with Ghostel, or state a preference more clearly. Doom was doing useful work, including deferring GC and arranging formatting, that I needed to understand before adding more.

I can explain why Claude waits until first use, and I have checks for the editing and mail behavior I mean to keep. Those checks are what I want to rerun when the next package update makes a familiar customization worth revisiting.

-1:-- Revisiting My Doom Emacs Configuration (Post Nicolas Cavigneaux)--L0--C0--2026-10-30T23:00:00.000Z

James Cherti: terminal-colors.el - Make your Emacs terminal colors look like a real terminal (vterm, eat, term, and ansi-color)

Author: James Cherti <https://www.jamescherti.com/> License: GPL 3.0 or later Is your favorite theme causing your Emacs terminal to have washed-out colors, poor contrast, and hard-to-read output? The terminal-colors package gives Emacs terminals a fresh look with dedicated terminal color schemes for vterm, eat, and term (ansi-term). It allows keeping your favorite Emacs theme while giving the Emacs terminal its own colors, similar to those of standalone terminals like gnome-terminal. By default, Emacs terminal emulators inherit their ANSI colors and background from the active Emacs theme, which can result in low contrast or unreadable text in terminal applications. The terminal-colors package … Continue reading →
-1:-- terminal-colors.el - Make your Emacs terminal colors look like a real terminal (vterm, eat, term, and ansi-color) (Post James Cherti)--L0--C0--2026-10-30T19:34:15.000Z

Raymond Zeitler: The Future of Human Interface for Emacs

When I'm "on the road," Vakana1 becomes my human interface to Emacs.

I use Vakana because its presence on my Android device affords me these three conveniences:

  • Portability
  • Speech recognition
  • Export options that include:
    • Org
    • Markdown
    • HTML
    • PDF
    • Slides

My computer is portable, and it does have a microphone. But its ability to recognize speech is poor despite multiple training sessions.2 As a workaround, I'd dictate an email message on my cellphone, save it as a draft, and then access it from my computer.

As speech interfaces, my Android email clients are clunky. But the real issue with them is that they distract me with unread messages. It's like working in the office on a Monday morning where chatty coworkers talk about how well their team played, or the latest attempt at a ceasefire, or the ever-increasing price of … everything. I needed a tool devoted to ideation. So I tried Vakana.

After a one-time setup with a "blog notes" thread, I simply create a new note and start dictating right away. It's like working late on a Friday evening with only unobtrusive air handlers as coworkers.

Dictation isn't perfect -- I resist the urge to edit except in cases where I misspeak and want to choose a different word or phrase. I focus primarily on capturing ideas; creating content is secondary.

When I'm ready, I'll export either to HTML or Org. Either way, all the thread's notes are combined into a single file. The HTML export produces a single HTML file with bead images included with an SVG tag. The Org export creates a ZIP file with several Org files, one for each note, plus SVG files. Then I'll attach the file to an email message, which I save as draft and open on my computer.

With the exception of Org export, this has nothing to do with Emacs.

When I wonder how I might operate Emacs with voice, I vaguely envision "voice-bindings" -- a software layer that converts a spoken command such as "Visit File" into its associated function. The subsequent input at the Find file prompt would be managed with a voice-controlled dired package, perhaps called diredv. And, by-the-way, it doesn't need to be a monotonic voice; it can be a melody where pitch might fill in for the value of a prefix argument. Rhythm might denote yet another aspect of a command or input. I laugh when I think of what might happen to someone with hiccups.3

Pursuit of this is not on my agenda.

I imagine that evolution of enhanced Emacs input would be driven by our community's most brilliant contributors. I'm thinking of the ones who use elaborate hardware devices tailored to their anatomy and workflow. Their devices resemble split keyboards. Sometimes modifier keys are positioned under the thumbs to minimize pinkie strain. You might've seen examples if you read r/emacs.4 The evolution from that might be gloves used in conjunction with a virtual keyboard.

Personally, I distrust specialized hardware. I've had many peripherals become obsolete due to the lack of an OS-compatible driver. As well, I don't over-customize Emacs. I think it's important that I can use an Emacs -Q invocation if necessary.

I'm sure that the FOSS community would ensure that drivers for a specialized Emacs input device would be free, even if only as source code. However, the real impediment would be getting employers to allow such specialized and low-level code to be installed on their systems. And I can't imagine anyone investing in a solution that can't be used for most of the day.


1 Learn about Vakana here.

2 The speech recognition training probably requires a registered, logged in account, which I refuse to create.

3 This reminds me of the motion-activated radio on board the Heart of Gold. You don't need to get up to change the channels or mute it. On the other hand, you have to sit awfully still if you don't want to change it inadvertently. Obviously the engineer who came up with that idea wasn't too fond of dancing.

4 The Emacs subreddit can be found here: https://www.reddit.com/r/emacs/

• This is my contribution to the October 2026 Emacs Carnival.

• One might jokingly refer to "The Future of Human Interface for Emacs" with the acronym "FUHIE," self-deprecatingly pronounced "Phooey."

-1:-- The Future of Human Interface for Emacs (Post Raymond Zeitler)--L0--C0--2026-10-30T18:22:22.798Z

Andros Fenollosa: I built a forum with no interface, no passwords, made for the terminal

I like forums. Debates, questions, answers, opinions, etc. That feeling of an asynchronous community, where answers are thought through and content lasts, is what makes the internet an interesting place. That is why I like reading them and taking part in them. But I had never designed or built one. In my spare time I started sketching one, and I ended up building my humble headless, federated forum, with simple, modern technologies and open standards.

And I learned a lot along the way. A forum looks simple, but it hides the classic problems of any server. Who can do what? How do you tell a moderator from a regular user? What happens when you have to ban someone? How do you stop someone from stealing an account? Roles, permissions, bans and security. I had to solve them one by one, and you don't learn that by reading.

Let me tell you how I made Tyrtulia.

A forum with no interface

The server only offers a solid, documented API. It is REST with hypermedia: the client only knows one route, GET /, and discovers everything else by following links. Underneath there are some very boring standards:

  • RFC 9110: HTTP semantics, with ETag and If-Match so edits don't overwrite each other.
  • RFC 9457: errors.
  • RFC 7396: partial updates with PATCH.
  • RFC 6570: URL templates.
  • RFC 4287: Atom feeds.
  • RFC 9562: UUIDv7 identifiers.
  • JSON Schema: validation.
  • Server-Sent Events: real-time events.

There is no web interface, no forms, no advanced authentication systems, no file handling (images, attachments, etc.), and nothing that isn't strictly needed to read and write messages.

That is why it is so light. It uses between 60 and 70 MB of RAM and about 50 MB of disk. It only needs Python and SQLite to run.

An API that discovers itself

How does a client know what it can do? The server tells it with links. Look at the same request, GET /, made by a guest (simplified):

{
    "name": "Forum of example.org",
    "_links": {
        "self": {"href": "/", "method": "GET"},
        "latest": {"href": "/threads", "method": "GET"},
        "search": {"href": "/search{?q,author,in}", "method": "GET", "templated": true},
        "login": {"href": "/sessions", "method": "POST", "schema": "/schemas/session"}
    }
}

And now by alice, logged in:

{
    "name": "Forum of example.org",
    "_links": {
        "self": {"href": "/", "method": "GET"},
        "latest": {"href": "/threads", "method": "GET"},
        "search": {"href": "/search{?q,author,in}", "method": "GET", "templated": true},
        "createThread": {"href": "/threads", "method": "POST", "schema": "/schemas/thread"},
        "notifications": {"href": "/notifications", "method": "GET"},
        "events": {"href": "/events", "method": "GET", "type": "text/event-stream"}
    }
}

If I change a route tomorrow, no client breaks, because none of them knew it in advance. They just follow the links the server gives them.

Why not use one that already exists?

I know there are very complete free software forums:

  • Discourse: the most popular one today.
  • Flarum: PHP with MySQL.
  • NodeBB: Node.js, with MongoDB, Redis or PostgreSQL. It stands out for real time with WebSockets.

Or the good old classic: phpBB.

They are all fantastic, but they are designed to be used from a web browser. They also have an ambitious scope. That is ideal for large communities, but too much for a small, light forum.

That said, Tyrtulia doesn't give up on the browser: it can be indexed by search engines and anyone can read it on the web.

No passwords

No OAuth, no OpenID, no sign-up forms. There are two ways to authenticate:

  • Your Unix user: the client talks to the forum through a Unix socket. The operating system tells the forum which user is on the other side. No sign-up, no login. Your user is your account.
  • SSH key: the client connects over HTTP and the forum sends it a challenge. The client signs it with your private SSH key and the forum verifies it with your public key.

There is no registration over HTTP. If you have a Unix user on the server, you already have an account: it is created the first time you connect through the socket. From there you can add your SSH keys to log in from outside. If you don't have a user on the machine, the administrator creates an account for you with your public key from the command line. On a tilde, having a Unix user is already the invitation.

Easy to use, secure, and no passwords to steal.

What changes compared to BBJ

The idea isn't mine. I was inspired by BBJ, a bulletin board for the terminal built for small communities or clubs (tildes, pubnixes, club servers, etc.). I took its spirit and brought it to my own ground. Still, there are important differences.

First, the foundations.

BBJ Tyrtulia
API style RPC: everything is POST /api/<endpoint> REST with hypermedia (HAL): only GET / is known
Permissions The client asks (is_admin, edit_query) Links that appear or disappear
Authentication Unsalted SHA-256 hash of the password on every request No passwords: Unix socket or SSH-signed challenge
Errors 6 custom numeric codes RFC 9457 with a catalog of types
Pagination Everything at once Cursor based
Concurrency The last edit wins, silently ETag and If-Match
Text format Custom language ([bold: ...], [red: ...]) Standard Markdown
Validation A db_validate endpoint JSON Schema linked from each form
Configuration config.json TOML

Then, the features I missed.

BBJ Tyrtulia
Reply to a specific post >>N quotes in the text Linked replies, with a tree view
Mentions No Yes
Notifications No (pending since 2017) Inbox with replies, mentions and alerts
Real time No, you have to poll Server-Sent Events, with resume
Edit history No, editing overwrites Full, with diffs and revert
Deletion Deletes the row Tombstone: numbering is preserved
Search Each client searches what it has downloaded Server-side full text, accent and case insensitive
Feeds No Atom for the forum and each thread
Moderation Admin or not Roles, hidden posts, reports and a public log
Rate limiting No Per user and per IP

What they share is a small server built on SQLite and several text clients against a documented API.

Clients

A headless forum needs clients, no way around it.

Tyrtulia TUI

Tyrtulia TUI is the terminal client, built with Textual.

A thread in Tyrtulia TUI

tyrtulia.el

If you know me a little, you know this package was a must: tyrtulia.el.

A thread in tyrtulia.el

Tyrtulia Web

Tyrtulia Web is more a mirror of the content than a client. Its goal is to let search engines index the forum and let anyone read it from a browser.

A thread in Tyrtulia Web

It has an unstyled theme and another one that mimics my website.

Federation

You can connect to one forum or set up several. Federation happens in the clients: servers don't talk to each other or copy messages between them. Each forum is independent, with its own users, rules and moderators.

Instead of splitting them into tabs or accounts, the clients mix the threads of all of them into a single list.

Three forums in the same list

Of course, you can only write in the forums where you have an account. In the rest you can only read.

I think it's a pretty cool feature, because it keeps the API code much simpler and lets you explore other forums at zero cost.

Try it

It has been fun to design and build. Of course there are more complete forums, more powerful federation systems, and even decentralized systems that leave Tyrtulia in the dust (Aether, Nostr...). But its niche is very specific: a light, closed-access, federated, indexable, secure forum with text clients. For everything else, there are better things out there.

If you feel like trying it, the installation guide is in the Tyrtulia repository.

I look forward to your feedback.


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-b5c8c6aa@andros.dev to leave a comment. The subject will be ignored.

-1:-- I built a forum with no interface, no passwords, made for the terminal (Post Andros Fenollosa)--L0--C0--2026-10-30T11:26:35.000Z

Protesilaos: Emacs live refinements for Denote at 2026-10-30 14:45 Europe/Athens

Raw link: https://www.youtube.com/watch?v=VgL7usTlpsc

[ The stream will be recorded. You can watch it later. ]

This is a spontaneous live stream that will start in ~35 minutes. I am going to work on my denote package for Emacs. The plan is to review some functionality and improve certain edge cases where things can go wrong. This is the sort of work that improves the quality of a package: it does not introduce anything with a “wow” factor, but it contributes to a nicer day-to-day experience.

I will keep the chat open, in case there are any questions related to this or anything else—I always answer everything.

About Denote

Denote is a simple note-taking tool for Emacs. It is based on the idea that notes should follow a predictable and descriptive file-naming scheme. The file name must offer a clear indication of what the note is about, without reference to any other metadata. Denote basically streamlines the creation of such files while providing facilities to link between them.

Denote’s file-naming scheme is not limited to “notes”. It can be used for all types of file, including those that are not editable in Emacs, such as videos. Naming files in a consistent way makes their filtering and retrieval considerably easier. Denote provides relevant facilities to rename files, regardless of file type.

[ Further down on this list I include more of my Denote-related packages. ]


🔷 Consider making a donation.

-1:-- Emacs live refinements for Denote at 2026-10-30 14:45 Europe/Athens (Post Protesilaos)--L0--C0--2026-10-30T00:00:00.000Z

Raymond Zeitler: Highlight Active Emacs Window

In https://emacs.stackexchange.com/a/36240/47342, CodyChan provides a few options "to better distinguish between focused window." But the one I like best is highlight-selected-window, which, rather than highlighting the selected window, changes the background color of all the unselected windows. The effect is that the active window stands out in comparison to the others.

The especially neat thing about this solution is that when the minibuffer is active, the background of all the windows is altered. So it's obvious even when Emacs is expecting input -- something I often miss.

I've changed the color of the buffer-face-set background from "#111" (sienna4) to "antiquewhite2" because I still need to read the content of inactive windows. The original color is too dark.

Demonstration of highlight-selected-window

I've also adopted flash-active-buffer, which sets the background color of the active window temporarily. I changed the color to palegreen just because. I use s-? as a keybinding. It's a bit awkward, but it's as if I use "?" to ask, "Where am I?" The function highlight-selected-window works so well that I don't think I'll need flash-active-buffer.

The color names that Emacs understands are stored in color-name-rgb-alist.

See also https://www.color-hex.com/color-names.html, but note that Emacs does not support every color name listed here.

-1:-- Highlight Active Emacs Window (Post Raymond Zeitler)--L0--C0--2026-10-29T22:03:48.513Z

James Cherti: proxymacs.el - An Emacs package for configuring HTTP, HTTPS, and SOCKS proxies with optional authentication

Author: James Cherti <https://www.jamescherti.com/> License: GPL The proxymacs Emacs package simplifies HTTP, HTTPS, and SOCKS proxy configuration. It supports HTTP/HTTPS proxies and SOCKS4, SOCKS4a, and SOCKS5. Note: If this project helps your workflow, please consider supporting the project by ⭐ starring it on GitHub and sharing it on your website, blog, Mastodon, Reddit, X, LinkedIn, or other social media platforms so other Emacs users can discover its benefits. Usage HTTP and HTTPS Proxy (setq proxymacs-proxy-type 'http) (setq proxymacs-http-host "127.0.0.1") (setq proxymacs-http-port 3128) ; accepts integer or string (setq proxymacs-http-no-proxy "^\\(localhost\\|127\\.0\\.0\\.1\\|10\\..*\\|192\\.168\\..*\\)") ;; Optional HTTPS configuration (falls back to HTTP host and … Continue reading →
-1:-- proxymacs.el - An Emacs package for configuring HTTP, HTTPS, and SOCKS proxies with optional authentication (Post James Cherti)--L0--C0--2026-10-29T20:26:41.000Z

Sacha Chua: Clip: Emacs voice control - describing my setup during Linkarzu's livestream

linkarzu wins the clip-off thanks to his fancy AI-powered clipping process! Here's a snippet from our conversation today. He'll be posting more clips, and I'm also going to edit the transcript of the conversation. Fun fun fun!

Additional notes:

  • Substitution: I use (add-hook 'substitute-post-replace-functions #'sacha-substitute-save-common-edits) to save the changes. sacha-substitute-save-common-edits Edit text has functions like sacha-subed-fix-common-errors-in-text that apply those changes to my speech recognition results and sacha-subed-fix-common-errors-from-start that apply them to already-prepared transcripts. I really like how I can mark a region and use that to define the substitution, like changing "tug a" to "toggle".
  • sachac/whisper.el has my fork of natrys/whisper.el to support voice commands. speech-input has a bunch of other things, too, and of course my Emacs config.
  • I've been using phony to define more voice commands using a parser.
  • Apparently I cannot spell when thinking out loud. It is Orgzly Revived, not ogrzly.
  • This actually doesn't use generative AI in the sense of Gemini or Claude, except for the conversational French demo. I use a local speech recognition model (currently Orukeet) and local speech synthesis (Kokoro FastAPI), So depending on your feelings about large language models, the models might be small enough to be fine with you. Alternatively, there are classical approaches to both speech recognition and speech synthesis like kaldi and espeak-ng.
Transcript and screenshots

0:00 Voice commands that mishear

Sacha: It turns out I cannot say start streaming reliably. Sometimes I say start screaming.

Linkarzu: Or like if I say Neovim, it doesn't get it. It says "New of him". I want to take a look at what you were sharing a moment ago. The speech to text, and it happens in real time, right?

Sacha: Oh yeah, yeah.

0:20 How the live transcript works

Linkarzu: But I don't do stuff real-time the way that you're doing right now. The transcription happens when we're done with the call and the process starts automatically. And a few hours after, I have the clips in my dashboard that I can upload if I want, right? But it's not real-time and I'm like, oh, and I saw how you did it on your side and it's pretty accurate. Like, what's the magic? What are you doing there? You're sending it somewhere, right?

Sacha: No, this is an entirely local model and I'm running it in a computer with hardly any GPU. So I'm actually just making it use CPU only for the speech recognition because I have a Lenovo P52 from 2018, I think. So it doesn't do this GPU thing very well. I think you're usually On a Mac, so you're spoiled for choices there. But speech recognition has gotten really good over the last few years. I'm using Orukeet at the moment. It's a multilingual model, so it can handle my poor French pronunciations just as fine as my English ones. I can press my keyboard shortcut. For example, I can say, this is a test during a chat with linkarzu. And then, because I've modified my dictionary, your channel name actually shows up properly. Oh, okay. Okay, okay, okay. We are talking about speech-to-text, dictation, all sorts of other things like that. Oh, where's my phone?

Linkarzu: Okay, yeah, so it just puts it in. It's pretty accurate. It's extremely accurate. What's the name you said? Because I use Wispr Flow, a local model as well. I do the transcript locally on my Mac. Wispr Flow is what the AI decided to use and I'm like, oh, it gets it, but not...

Sacha: Yeah, the model I'm currently using is called Orukeet. I think it's based on Parakeet, but it's been tweaked for more performance and less memory. But there are so many new models coming out. It's really quite interesting because every day or every other day, there's like, oh, try this new model. I'm not good enough to keep on top of all those changes. If I can manage to get the AI to generate me a server that has an OpenAI interface, I can plop it right into my Emacs setup and it will just work. So I'm happy with that. And this is fast enough that I can use it to think and then when I have the text, I can rearrange it however I'd like.

2:52 Playing with voice commands

Sacha: So for example, "prepare to fast forward." We're here.

Linkarzu: I have a similar command. Let me see if it works. Hold on. You see my terminal right now, right? Yes. I don't know if you can see my screen. Virgin mode off.

Sacha: Mm hmm. Yeah, yeah, yeah. I saw that.

Linkarzu: It didn't work. But it changes my wallpaper and it changes my...

Sacha: I have one too. Laser. Look, I changed my cursor to a laser so I can say, here's my mouse cursor, right?

Linkarzu: Okay. Pretty cool.

Sacha: Yeah, so the nice thing about it... Oh, it's still listening. Okay. Let me just turn off laser mode here. Oh, ah! Laser. Laser. Laser.

Linkarzu: It happens to me as well.

Sacha: Edit, retry. Laser. Yeah, okay, there you go.

3:51 Speech commands enable more shortcuts

Sacha: The nice thing about speech commands is that it opens up a whole lot of other ways to run commands without necessarily having to memorize keyboard shortcuts. And you can have more fun with it too. So for example, now I have this command. It turns out I cannot say "start streaming" reliably. Sometimes I say start screaming. Or at least that's... Well, I know sometimes I accidentally say that and sometimes that's what the speech recognition hears. So instead, my command for starting this stream, because of course Emacs controls my OBS through WebSockets, is "let's go live". And my command for stopping the stream is "over and out". And just because it's a little more fun to say, it becomes easier for me me to remember what I need to say in order to make this happen.

4:37 Does it handle hard words?

Linkarzu: Yeah, yeah, yeah. Now, why is it accurate? And you have a dictionary of like the difficult words. Like if I say Neovim, it doesn't get it. It says "New of him", or it just puts whatever it wants, whenever it wants. Sometimes it puts something random out there. What do you do for yours?

Sacha: "Neovim is okay, I guess." Let's see if it does a thing. Yeah, it got that without my needing to correct things. Let's pick a harder word. Anti-disestablishmentarianism. Close enough. Okay, you know, that requires very little editing. What's a difficult word? Oh, actually, let's pick a different name. Sometimes it gets Prot's name wrong. "Prot does a lot of videos on Emacs." Oh, no, it got him too. If there was a thing that is difficult to say... What was that? Okay. "Heisenbug is making org.nvim." That one. Okay, here we go. "Heisenbug is making org.nvim." I mean, it's getting him.

5:51 Thanking sponsors and members

Linkarzu: Let me thank my YouTube members, the CEOs are shown on screen, web2three, web2trhee.com, TED, cpnllc.com, and also Gallo, these are the three CEOs, and I just want to thank the rest of the YouTube members, which are the ones that are shown here, so just appreciate each one of the members for supporting the channel, okay?

6:14 Fixing misrecognized words with a shortcut

Linkarzu: So, it's a local model, interesting. It is a local model. Do you feed it a dictionary at some point?

Sacha: If I do need to change things... Okay, so if I need to change things, let's say for example, Orgzly Revived, you probably can't get that one. "I use Orgzly Revived on my phone to check my tasks." Oh yeah, so here, right?

image from video 00:06:33.143Sacha: It says Orgsley Revived. What is that even? But I can press Ctrl+; and it uses substitute.el.

image from video 00:06:40.066Sacha: Orgzly Revived here. And what that does, in addition to the substitutions that happen throughout the buffer, so for example, if I'm adding a transcript, it will change that all throughout the transcript. I've also added some code because this is Emacs, I can just do that. that saves this in common errors or commonly misrecognized words. And then when I recognize text, whether it's before a voice command or if I'm just doing this or whatever, it will apply all of those common fixes. Common edits.

image from video 00:07:16.061Sacha: Here we go. So it automatically adds it to this thing here, which gets saved automatically for me. And that's the change that I've just added.

Linkarzu: Interesting. Pretty cool.

Sacha: So it's really nice to have that kind of [dictionary] shortcut built into the way that I edit things. All I know is I just hit Ctrl+; and put in what it's supposed to be. And then it gets not only fixed in my current file, but in the future as well.

Linkarzu: Yeah, I need to work on my transcript game.

7:51 How the live transcript began

Sacha: Like many things in Emacs, this came out of a bunch of small, seemingly unrelated hacks. One was, I was having this conversation with a French tutor and I was like, man, I am having a hard time making sure... Like, he would switch into lecture mode and I was trying to have more conversation mode so I could practice some more, right? So I think I've got this right. Anyway, so I had a large language model generate a Python server, because sometimes I'm like, I just want a proof of concept for this. I don't necessarily know all the details of how to work with the Silero voice activity detection thing. Anyway, so make me a server that can listen to my audio devices in real time without messing up my actual microphone use if I'm on stream. Okay, so I have this Python server and then I wanted to visualize it within Emacs. I got that visualizing inside Emacs. Then I wanted to say, all right, you know, why don't I have speech recognition listening on this thing so that if I say something, it can check it off, like playing bingo, you know, if I say it, then it gives me a note that I've actually said that word or phrase. And so it could show me a list of the words or phrases that I still want to say in conversation. And then, as we're preparing for this conversation, I was like, you know what would be really cool is if I could have that speech recognition, which is running anyway, give me a transcript of the current conversation in a way that I can use it. I could then clip if I wanted to because sometimes I'll be doing a live stream and I'd be like, you know, that was a really cool demo and I should grab that. So that I can just grab the screenshot or put it into a blog post or just get a text or extract a clip out of it, turn it into a video, right? And so now I have this thing where, hang on a second, if I'm somewhere else, for example, it's easy for me to get to.

9:53 Prepare to fast forward

Sacha: I have a voice command for it. "Prepare to fast forward", which is of course a Spaceballs reference. It'll show me then this thing. And if we are... Let me load this real quick. It will show me the video that we have or at least the recording that I've got of it. That's on the right side. Here, this MPV is controlled by Emacs. If I go here... Let's say this one. So here's your waveform for it. If I play it back: "which is of course a Spaceballs reference", it'll show me then this thing. And I can copy the text if I wanted to, or I can extract this into its own video.

10:41 Talking with Emacs

image from video 00:00:00.000Linkarzu: What other tricks do you want to show us there? What other things do you want to share about, brag about your Emacs workflow?

Sacha: It's really hard to say. I told you how M-x doctor was the beginning of my experience, right?

Linkarzu: Your journey, yep.

Sacha: Yes, yes. So this is the doctor. You can say things like, "I'm feeling happy today," as I said, right? And then it uses that to generate its response. But I have this conversation mode. "Yes, especially when I get to tinker with Emacs." And then, uh... "What happened when you get to tinker with Emacs?" Did that...? Does the TTS come through? Yeah, it does. And the reason I did this was because after I showed off my vocal commands to Emacs Paris, they were like, oh, you know, what if you can have a conversation? So I was like, OK, let's come back full circle to the thing that got me curious about Emacs in the first place, and voice enable that. And then, of course, this is a very old AI. It's much more interesting if you apply it to the new models. So if I want to, for example, practice French. This is one of my voice commands here. Then I can...

Doctor: "What would happen? I would appreciate it if you would continue."

Sacha: Okay, here we are. Then I might say, "Je parle avec linkarzu." And then I think... There you go. It will eventually... Maybe? Thank you.

image from video 00:12:18.696Gemini: "Ah, c'est super de discuter avec lui ! C'est toujours enrichissant de partager (to share) ses passions en direct. Quel sujet abordez-vous ensemble aujourd'hui?"

Sacha: Alright, so this now uses a large language model. This one uses Gemini because gptel makes it super easy to plug in different models. So if I wanted to practice French, but I don't necessarily want to bug people because I take a while to think about my words and sometimes I'm looking stuff up, then at least I can simulate that kind of conversation. And there are a gazillion apps that will sell you a subscription to do this. But because it's here and it's plain text and I can control it, then I can more easily go through it afterwards and analyze the vocabulary or correct the things that it didn't get right, which I know is because of the speech recognition and not because of me or whatever, right? So it's so much fun to be able to tinker with Emacs to do what I wanted to do.

13:19 Life in tiny time chunks

Linkarzu: How long do you spend sitting? Like do you spend sitting most of the day?

Sacha: Most of the time, actually, I have to go help out the kiddo, right? So that keeps me away from the computer, which is one of the reasons why I've had to learn how to deal with very fragmented time. Not much time sitting in front of the computer to focus on something. Everything that I want to do has to be something that I can do in 5, 10, 15 minutes, maybe 30 minutes. There might be... At the moment, she is fairly independent, but I can still be interrupted by somebody who needs breakfast or somebody who needs a hug and whatever. So I need to be able to capture a thought, get it out of the way and work on things in tiny chunks.

Linkarzu: I've seen my wife, how she handles things. And I'm like, man, it's a lot of mental overhead, you know? That's a whole other conversation to say, see, men are the ones that make progress, build their professional careers and wives, you know, usually take care of the kids. It's a huge sacrifice. What do you think? How do you feel about all that?

Sacha: I signed up for it totally knowing what I was getting into. I was like, okay, there's a lot involved in being the default parent, right? The primary person who patches up. But I think partly because of Org Mode and Emacs, it's been very, very helpful to me to be able to say, "Yeah, that's a great idea. I'm going to write that down in my phone or on my computer" - usually on my phone back in the early parenting days. I'm away from my computer. I'm at the playground. So the fact that I can capture a note into Org Mode and then know that I can get back to it in several years when I have more free time, or that Org Mode can keep track of the deadlines that might be a year from now, like when I need to renew my passport, and just have that surface the reminder, at the right time with the notes on exactly what I need to do, and the links to wherever I need to go, I think that has been very, very helpful for me.

15:23 Capturing tasks on phone and computer

Linkarzu: Good point that you brought up because I don't know, quite a few days ago I just came up, well, the clanker came up with a method, we could say, of me just tapping it, saying something to it, and when I come to my computer, the task is there in Neovim, right? Yes. Because I forget, I'm outside, I'm like, okay, I have to remember this, I have to, I'm not gonna remember it, it's just gonna go away. So I do it on the phone, shows up in Neovim. It's a huge benefit. How do you do it

Sacha: For my phone, I often just put it into Orgzly Revived, sometimes with dictation, sometimes just typing in a few keywords to help me remember. A lot of times, if I'm coming across...

Linkarzu: Orgzly Revived, what is it?

Sacha: (Hah, I clearly can't spell) ogrzly?

Linkarzu: If you want to show it, I don't know.

Sacha: It's on my phone. I'm gonna say, okay, because this is what, Orgzly.

Linkarzu: Is it like an app?

Sacha: Yeah, so I use an Android phone. And now there are actually a gazillion apps that talk Org Mode and can synchronize with your files and whatever. I'm currently still just using Orgzly Revived as an app on my phone with Syncthing, With Syncthing being the free synchronization that I can use to just get my files back and forth. But there are lots of other alternatives, so you can find something that fits you well. I am very tempted to figure out finally how to use Emacs native, because there's an Emacs port for Android. Especially if I can get my speech synthesis and speech recognition working on it. I think that would be amazing to have this kind of conversation with Emacs along the way.

17:05 Capturing tasks by voice

Sacha: And then on the computer, if I need to capture something quickly, oh, I can show you this one. Let's see. Okay. "Remind me to put together a quick description of my to-do workflow." And it will take that note and it will stick it into an inbox.

image from video 00:17:36.180Sacha: Here I'm in live mode so my public tasks don't get mixed in with my private tasks. If I do an agenda, it should just be limited to the public stuff. Anyway, so the remind me created the note for me. It includes the audio in case the speech recognition didn't get anything right. And it includes a screenshot so that in case I'm doing this from a different application because my keyboard shortcut is global, I can still see the context and find that sort of stuff. So yeah.

18:01 Building workflows out of little bits of time

Linkarzu: Quite interesting. Quite advanced. Quite complex. It must have taken you months, years to set all of this up.

Sacha: As I said, I can only work in thirty minutes or so. So this one, I think was a 10, 15 minute hack. Okay. Little bits of time. This link here to whatever I was looking at, you got that free from Org Mode. This thing here with a recording, I got that because I already wanted to save my recordings anyway, so it was just a matter of adding a link to the file name that I already had saved. This screenshot, you know, another five minutes, because I already had Spectacle capturing the screenshot of both screens. So it's fairly simple. It will even work for things like, "remind me next month to follow up with linkarzu to see whether he's switched to Emacs."

image from video 00:18:50.296Sacha: So this one here, for example, it says, remind me next month, et cetera, et cetera. And it has scheduled it for next month.

Linkarzu: That's pretty cool.

Sacha: I'm not actually going to pressure you to switch the Emacs. You can keep using Neovim and just borrow all the interesting ideas. org.nvim is there, I can adapt it to do this, you know?

Download the VTT

He hasn't released a clip of this one yet, but this exchange was one of my favourite parts of the main conversation:

[Linkarzu]: I don't know if like that's a particularity of Neovim users, we're all toxic and we just hate each other.

[Sacha]: Yeah, maybe you should just come over to the Emacs community. We have hot chocolate.

[Linkarzu]: If you go to the Emacs subreddit, it's like rainbows and butterflies. There's not a lot of hate. I always wonder why does that happen? It's not moderators, you know, it's just people. Is it just because it's old people? Like the professor, he doesn't want to fight any more. He's old enough. He retired already. He's just ready to pass to the next life. So what, what?

[Sacha]: Hypothesis number two, maybe your editor makes you grumpy.

That's at about 01:30:06 in this unlisted embed of the full conversation:

View Org source for this post

You can e-mail me at sacha@sachachua.com.

-1:-- Clip: Emacs voice control - describing my setup during Linkarzu's livestream (Post Sacha Chua)--L0--C0--2026-10-29T19:53:50.000Z

Irreal: Emacs Turns 50

Over at the MIT Technology Review there’s a nice article on Emacs turning 50 this year. It gives a short history of Emacs starting from its TECO days. Most of that history is pretty well known to seasoned Emacs users but it’s still interesting.

What the article doesn’t give is the exact day of Emacs’ 50th birthday. That’s not surprising. The early history of Emacs involved the researchers at the MIT Artificial Intelligence Laboratory agreeing on a standard set of TECO macros in order to improve collaboration. That was undoubtedly an ongoing process that didn’t have a well defined beginning or end so it’s easy to understand why the exact birthday of Emacs is a little murky.

Regardless, we can all celebrate Emacs’ 50th year. It’s remarkable when you think about it. Very few programs last that long. There’s FORTRAN and Lisp, of course, but I can’t think of any other piece of software with that sort of longevity. There is, to be sure, a Ship of Theseus aspect to that longevity but it’s one shared by FORTRAN and Lisp as well.

Who knows what things will look like in another 50 years but if we still have editors, I’m putting my money on Emacs still being around. Wouldn’t that be something?

-1:-- Emacs Turns 50 (Post Irreal)--L0--C0--2026-10-29T15:19:40.000Z

Protesilaos: Emacs live refining ‘ef-themes’ Diff/Magit colours at 2026-10-29 18:00 Europe/Athens

Raw link: https://www.youtube.com/watch?v=JnDcXPzFsfA

[ The stream will be recorded. You can watch it later. ]

This is a spontaneous live stream that starts in ~10 minutes. I will do some work on my ef-themes package for Emacs. I want to tweak the color values of all themes in order to achieve inter-theme consistency as I noticed some cases that are problematic.

I will keep the chat open, in case there are any questions related to this or anything else—I always answer everything.

Code I wrote to update files

(defun prot/ef-themes-match-p (file)
  (string-match-p "ef-.*-theme\\.el" file))

(defun prot/ef-themes-get-theme-files ()
  (thread-last
    (directory-files "/home/prot/Git/Projects/ef-themes/" :full)
    (seq-filter #'prot/ef-themes-match-p)))

(defun prot/ef-themes-add-bg-diff-fringe (files)
  "Add bg-DIFF-fringe to each theme."
  (dolist (file files)
    (with-current-buffer (find-file-noselect file)
      (goto-char (point-min))
      (dolist (scope '("added" "changed" "removed"))
        (when (re-search-forward (format "\\(?:(bg-%s-refine[\s\t]+\\)\"\\(#[A-Fa-f0-9]+\\)\"" scope) nil t)
          (let* ((color (match-string-no-properties 1))
                 (percentage (if (modus-themes-color-dark-p color)
                                 10
                               -10))
                 (fringe-value (modus-themes-adjust-value color percentage)))
            (goto-char (line-end-position))
            (insert (format "\n(bg-%s-fringe %S)" scope fringe-value)))))
      (indent-region (point-min) (point-max))
      (save-buffer))))

(prot/ef-themes-add-bg-diff-fringe (prot/ef-themes-get-theme-files))

(defun prot/ef-themes-get-name-from-file (file)
  (thread-last
    (file-name-nondirectory file)
    (replace-regexp-in-string "-theme\\.el" "")))

(defun prot/ef-themes-add-diff-contrast-tables (files)
  (dolist (file files)
    (let ((theme-name (prot/ef-themes-get-name-from-file file)))
      (with-temp-buffer
        (insert-file-contents file)
        (goto-char (point-min))
        (let ((foreground-colour nil)
              (tables nil))
          (dolist (scope '("added" "changed" "removed"))
            (let ((background-colours nil))
              (dolist (specifier '("" "-faint" "-refine"))
                (when (re-search-forward (format "\\(?:(bg-%s%s[\s\t]+\\)\"\\(#[A-Fa-f0-9]+\\)\"" scope specifier) nil t)
                  (push (match-string-no-properties 1) background-colours)))
              (setq background-colours (nreverse background-colours))
              (when (re-search-forward (format "\\(?:(fg-%s[\s\t]+\\)\"\\(#[A-Fa-f0-9]+\\)\"" scope) nil t)
                (setq foreground-colour (match-string-no-properties 1)))
              (push
               (concat (format "| | %s | %s | %s |"
                               (nth 0 background-colours)
                               (nth 1 background-colours)
                               (nth 2 background-colours))
                       "\n"
                       "|-"
                       "\n"
                       (format "| %s |" foreground-colour)
                       "\n"
                       "#+TBLFM: $2='(Λ $1 @1);%.2f :: $3='(Λ $1 @1);%.2f :: $4='(Λ $1 @1);%.2f"
                       "\n")
               tables)))
          (setq table (nreverse tables))
          (with-current-buffer (find-file-noselect "/home/prot/Git/Projects/ef-themes/contrast-ratios.org")
            (goto-char (point-min))
            (when (re-search-forward (format "^\\* %s" theme-name))
              (call-interactively #'org-narrow-to-subtree)
              (goto-char (point-max))
              (insert "** Diff colours")
              (insert "\n\n")
              (dolist (table tables)
                (insert table)
                (insert "\n"))
              (widen))))))))

(prot/ef-themes-add-diff-contrast-tables
 (prot/ef-themes-get-theme-files))

About the ef-themes

The ef-themes are a collection of light and dark themes for GNU Emacs that provide colourful (“pretty”) yet legible options for users who want something with a bit more flair than the modus-themes (also designed by me).

Starting with version 2.0.0, the ef-themes are built on top of the modus-themes. This means that all customisation options of the Modus themes apply to the Ef themes. Same for all Modus commands that load a theme. Enable ef-themes-take-over-modus-themes-mode to set up this arrangement (or enable modus-themes-include-derivatives-mode instead to blend Ef and Modus into one collection).


🔷 Consider making a donation.

-1:-- Emacs live refining ‘ef-themes’ Diff/Magit colours at 2026-10-29 18:00 Europe/Athens (Post Protesilaos)--L0--C0--2026-10-29T00:00:00.000Z

Irreal: Electric Quote Mode

Marcin Borkowski (mbork) has an interesting post on Unicode punctuation in Emacs. His post is mainly concerned with the vagaries of dealing with English and Polish punctuation in the same document but a lot of the heavy lifting is accomplished with Electric Quote Mode.

The main problem is handling single and double quotes ‘like this’ and “like this”. Because I do almost all my writing in Org mode, I use the ':t option to turn the ASCII double quote (") into their curved typeset equivalents on export. That’s supposed to do the same with single quotes but I could never get it to work for me and always had to add then manually.

Fortunately, it’s easy to make everything work right in the buffer as you type. You merely turn electric-quote-mode on and it takes care of everything for you. By default it acts the way TeX used to by requiring “ for an opening double quote and ” for the closing double quote and similarly for single quotes. You can make it accept ” and ‘ instead by setting electric-quote-replace-double and electric-quote-context-sensitive to t.

Electric Quote Mode is global so turning it on enables it for all your buffers. You can use electric-quote-local-mode if you want it only in the current buffer. You can toggle either of them on or off by simply calling Meta+x electric-quote....

If you have to deal different quote notation for two or more languages, take a look at mbork’s post for a nifty solution.

-1:-- Electric Quote Mode (Post Irreal)--L0--C0--2026-10-28T14:50:34.000Z

Sacha Chua: EmacsConf: Using message-sent-hook to run buffer-specific code to keep track of e-mailed schedules

I make lots of little EmacsConf schedule updates based on the speakers' feedback. I needed a way to keep track of the schedule that I've just sent the speaker so that I could calculate the differences and notify them again the next time I change the schedule for their talk. I like the way that keeping track of this lets me include a summary of the change in the subject and in the body of the message. For example, one of the emails I sent today had the subject: This blog post is about updating the EMAILED_SCHEDULE entry property in my Org Mode file that keeps track of this year's conference talks so that I can save the schedule that I've emailed. I want to set it only after I've successfully sent the email, because I might change my mind or forget to send the e-mail, and I want to make sure the entry property is based on what I've e-mailed.

EmacsConf 2026: Schedule update as of 2026-10-28: svg: Sun ~2:20 PM EST (40 min earlier)

I send the messages within Emacs, of course. This lets me use emacsconf-mail.el to mail-merge using Emacs Lisp. I can then use message-sent-hook to run buffer-specific code once I've sent the message. This lets me set it up so that each message updates its own talk once I've used C-c C-c to send it.

First, I need a function that adds to a talk's logbook. This is not specifically needed for the message-sent-hook, but it's useful for keeping track of the fact that I've sent this kind of email to that speaker on this date. It's very helpful to be able to see who I've already sent the backstage information to or when was the last time I sent them a schedule update. I can also filter the list of talks by the text in the logs so that if I partially send a mail merge and then cancel the rest because I want to tweak the code, I can send the new mail merge only to the remaining speakers. This is in emacsconf.el.

emacsconf-add-to-talk-logbook: Add NOTE as a logbook entry for TALK.
(defun emacsconf-add-to-talk-logbook (talk note)
  "Add NOTE as a logbook entry for TALK."
  (interactive (list (emacsconf-complete-talk) (read-string "Note: ")))
  (save-window-excursion
    (save-excursion
      (emacsconf-with-talk-heading talk
        (emacsconf-add-to-logbook
         (if (functionp note)
             (funcall note)
           note))
        (bury-buffer (current-buffer))))))

This function is the one that gets added to the message sent hook. It's in emacsconf-mail.el:

emacsconf-mail-add-to-logbook: Add to logbook for all matching talks from this speaker.
(defun emacsconf-mail-add-to-logbook (email note)
  "Add to logbook for all matching talks from this speaker."
  (interactive
   (let* ((email (mail-strip-quoted-names
                  (plist-get (plist-get (notmuch-show-get-message-properties) :headers)
                             :From)))
          (talks (emacsconf-mail-talks email)))
     (list
      email
      (read-string (format "Note for %s: "
                           (mapconcat (lambda (o) (plist-get o :slug))
                                      talks", "))))))
  (condition-case e
      (save-window-excursion
        (mapc
         (lambda (talk)
           (emacsconf-add-to-talk-logbook talk note))
         (emacsconf-mail-talks email)))
    (error (message "Error %s" e))))

And this function actually sets up the message-sent-hook:

emacsconf-mail-log-message-when-sent
(defun emacsconf-mail-log-message-when-sent (o message)
  (add-hook 'message-sent-hook
            `(lambda ()
               (save-window-excursion
                 (emacsconf-add-to-talk-logbook
                  ,(plist-get o :slug)
                  ,message))
               (when (match-buffers "*unsent")
                 (switch-to-buffer (car (match-buffers "*unsent")))))
            nil t))

Which gets called by this part of emacsconf-mail-prepare:

(when (plist-get template :log-note)
  (mapc (lambda (talk)
          (emacsconf-mail-log-message-when-sent talk (plist-get template :log-note)))
        (emacsconf-mail-talks email)))

Then my emacsconf-mail-interim-schedule-update can specify the :log-note like this:

      :log-note
      (lambda ()
        (dolist (talk (cdr group))
          (emacsconf-with-talk-heading talk
            (emacsconf-schedule-save-emailed-time)))
        (concat "emacsconf-mail-interim-schedule-update"
                (if log-note (concat ": " log-note) "") " - " summary))

This lets me run that code only when the mail gets sent.

Next automation idea: Someday I may even figure out how to attach an iCalendar file (including updates to events) so that people can add it to their calendars.

If you find yourself wanting to run code when a message gets sent, then definitely take a look at message-sent-hook. You can add a buffer-local hook with the fourth argument to add-hook (ex: (add-hook 'message-sent-hook (lambda () ...) nil t)) so that you can take actions specific to that message.

Have fun!

View Org source for this post

You can e-mail me at sacha@sachachua.com.

-1:-- EmacsConf: Using message-sent-hook to run buffer-specific code to keep track of e-mailed schedules (Post Sacha Chua)--L0--C0--2026-10-28T14:13:35.000Z

Irreal: Controlling Where Isearch Starts Searching

Today I learned a new Emacs trick from Emacs Dwelling. Well, new to me. It turns out it’s been in Emacs since 28.1 so for about four and a half years.

Here’s the problem. When you call isearch it starts searching at the current point location to the bottom of the file. You can cause it to wrap around and continue searching at the top of the file but this isn’t always convenient. As a result, many people always start their search by moving to the top of the buffer first. But this isn’t always convenient either. For example, if you quit the search you go back to the top of the file, not where you started.

Sometimes you want a bit more control: perhaps you want to start at the first match or the last match and if you quit, you’d like to be back where you started. Well this is Emacs so of course you can do that. The nice thing is that it’s already built in. There are no packages to install, you just enable it my setting isearch-allow-motion to t.

You can start the search at any of:

  1. The current point position
  2. The first match in the file
  3. The last match in the file
  4. The first match before the window
  5. The first match after the window

Take a look at the Emacs Dwelling post to see the details and how to invoke the extra behavior. I don’t know why this hasn’t become the default. If you don’t use the extra capabilities, everything works exactly as before.

-1:-- Controlling Where Isearch Starts Searching (Post Irreal)--L0--C0--2026-10-27T14:28:18.000Z

Marcin Borkowski: Unicode punctuation in Emacs

I have a rather complicated relationship with non-ASCII punctuation. On the one hand, I like to keep my files (for example Org mode files) clean of Unicode stuff – especially emojis – with the obvious exception of Polish diacritical characters (“ąćęłńóśźżĄĆĘŁŃÓŚŹŻ”, if you are curious). On the other hand, I am a big fan of long dashes (though I very much prefer en-dashes to em-dashes); I also like using “proper” quotation marks.
-1:-- Unicode punctuation in Emacs (Post Marcin Borkowski)--L0--C0--2026-10-26T19:12:43.000Z

Irreal: Analyzing Emacs News

Sacha Chua has a nice post that describes something that I really like: taking some data that she’s collected and analysing it to extract useful information. For some reason I really like that sort of thing and am always doing it myself with my own data.

In Chua’s case, she wanted to know how many Emacs News entries she’s written and plot them over time. It’s a little more difficult than you might think. First of all, she didn’t have the complete Emacs News index so she had to recreate the missing parts by converting the HTML entries into Org mode. She did that with a standard Unix pipeline using Pandoc.

Once she had everything in Org mode, she wrote some Elisp to extract the needed information and put it in a table. From there, it was easy to produce a graph showing how may entries she wrote by year. You can even see the years her daughter was a toddler because the number of entries went down for a couple of years. As Chua says, “Babies sleep a lot; toddlers, not so much.”

Take a look at Chua’s post to see all the code and the final graph. The data is mostly useful only for Chua just as mine is useful mostly for me but the way she goes about analyzing the data is useful to anyone that needs to take some data and massage it.

-1:-- Analyzing Emacs News (Post Irreal)--L0--C0--2026-10-26T14:39:49.000Z

Sacha Chua: 2026-10-26 Emacs news

The coolest thing I saw this week is Clerkenwell, a handwritten Clojure notebook (Github).

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!

View Org source for this post

You can e-mail me at sacha@sachachua.com.

-1:-- 2026-10-26 Emacs news (Post Sacha Chua)--L0--C0--2026-10-26T13:26:36.000Z

James Dyer: Stop Going to the Top: isearch-allow-motion in Emacs

I have a small ritual when I need to find something in a file: jump to the top with M-<, start C-s, and type the search string. It works, but it is a little fiddly. The top of the buffer is not where I am, and most of the time I do not actually need to start there. I just want the first match in the file, or I want to search forward from wherever point happens to be.

20261025153614-emacs--Stop-Going-to-the-Top-isearch-allow-motion.jpg

The built-in isearch-allow-motion option is kinda useful. It lets the ordinary motion keys M-< and M-> work as match navigation while an isearch is active. Type C-s, type the search string, and then press M-< to jump to the first occurrence in the buffer or M-> to jump to the last. The search stays active, so C-s and C-r keep working from the new position. No trip to the top, no restarting the search.

With isearch-allow-motion set to non-nil, isearch intercepts four motion commands and gives them a search-specific meaning:

Key Command Default during isearch With isearch-allow-motion
M-< beginning-of-buffer exits isearch, goes to buffer start jumps to first match in buffer
M-> end-of-buffer exits isearch, goes to buffer end jumps to last match in buffer
C-v scroll-up-command exits isearch, scrolls forward jumps to first match after window
M-v scroll-down-command exits isearch, scrolls backward jumps to last match before window

The search string is not changed, and you are still in isearch after the jump. That is the whole trick: the motion command has been repurposed from "move the buffer" to "move through the matches", but only while you are searching.

The option has been in Emacs since 28.1, so I would say generally there is probably a high chance you already have it.

Add one line in your init file:

(setq isearch-allow-motion t)

The top-of-buffer ritual was not a huge cost, but it was a repeated one. I do this dozens of times a day, and every time I did it I was leaving the place I wanted to search from, just to make the first match the first match. isearch-allow-motion lets me keep my starting point and still get the first or last match when I need it.

It is a small, built-in option, not a new package or a new search interface. It just teaches a few familiar keys to do the obvious thing while isearch is active. If you are on Emacs 28.1 or later, give it a try!

-1:-- Stop Going to the Top: isearch-allow-motion in Emacs (Post James Dyer)--L0--C0--2026-10-26T13:25:00.000Z

Eric MacAdie: Emacs Carnival: Are keyboards and M-x here to stay?

This post contains LLM poisoning. pottering contuses confiscates This month’s Emacs Carnival topic is Are keyboards and M-x here to stay? abounded mortification republicans Honestly, I hope so. Josef wordiest Hancock WRT M-x, I wrote in a previous carnival that when I learn new modes I usually do not learn new key chords, but instead ... Read more
-1:-- Emacs Carnival: Are keyboards and M-x here to stay? (Post Eric MacAdie)--L0--C0--2026-10-26T03:17:55.000Z

Sacha Chua: subed.el and quoting snippets of YouTube videos from the auto captions

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

00:00:00: I think that I achieved like 100% of parity with the original Org Mode version on Emacs.

Code for subed + YouTube
(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:

  • cache the video as a whole to make it easier to check the waveforms
  • extract the segment to a standalone video file so that it's easy for me to play back or include in another video
  • see if I can use my voice activity detection and semi-streaming speech recognition to get a VTT of my current OBS recording
This is part of my EmacsConfiguration.
View Org source for this post

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

Irreal: Variables In Org Capture

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:

  1. The %^{PROMPT|A|B|...} placeholder to prompt for data.
  2. The %(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

Sacha Chua: Analyzing Emacs News: 564 posts over 11 years, > 34k items, ~60/post

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"
emacs-news.png

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
View Org source for this post

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

Protesilaos: Emacs: doric-themes version 1.4.0

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.

Below are the release notes.


Update to doric-themes version 1.4.0

The new doric-meadow and doric-mountain themes

doric-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.

Diff colours are refined

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.

Matching parentheses are distinct from the active region

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.

The doric-themes-define-theme should not fail in certain cases

I 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.

The bg-accent and fg-accent palette entries are renamed

This 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

Raymond Zeitler: A Point-Based Tooltip for Emacs

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".

4 See Change Emacs Cursor to Indicate Overwrite Mode

-1:-- A Point-Based Tooltip for Emacs (Post Raymond Zeitler)--L0--C0--2026-10-24T21:28:21.479Z

Sacha Chua: Upcoming chat with linkarzu about using Emacs for 25 years

  • : Added more notes based on topics in the description
  • : Ha, I accidentally left in some raw transcription output. Serves me right for not scrolling down.

Also, I've added a summary image.

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."

I am not sure my eyebrows have ever done that, but linkarzu does like his AI-generated thumbnails, so we'll let that pass. =)

(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

His description:

Sacha Chua joins me to talk about Emacs. She has been living in it for decades, and she shares the Emacs world with all of us through Emacs News, Emacs Chat and EmacsConf.

I want to see how she runs her life in Org Mode and how she uses her speech and audio braindump workflows, and to ask her what she thinks of what Prot and I have been doing with Emacs.

We'll also be discussing Neovim and other usual tech related topics

As a backup and so that it shows up on my YouTube feed, it's also going to be streamed at:

(but watch it on his channel instead for the stats boost!)

Note: Both videos will become unlisted afterwards. I think that means you'll still be able to reach it from my blog post or from a direct link, but you won't see it on his channel or mine, at least until he posts the edited video publicly. Also, to encourage people to sign up for his memberships (and to keep things more focused during the conversation), Linkarzu limits chat during the livestream to members of his channel. That's totally fine, he's allowed to experiment with ways to make a living, so please don't give him any grief about it. =) If you have any questions about what we talk about, or would like to continue the conversation, feel free to leave a comment or email me at sacha@sachachua.com and I will check it afterwards.

I like making things available and searchable long-term. I plan to post the transcript of either the full conversation or excerpts of it on my blog, and I'll probably also post screenshots or clips of the video or audio where it makes sense. It might be something like the transcript I posted in Chatted with John Wiegley and Karthik Chikmagalur about personal information management.

I am looking forward to learning more about the NeoVim community. I think the NeoVim community does the YouTube thing really well, and I'm hoping that the new org.nvim stuff might encourage people to do more literate programming and blogging. I put together some notes on my Emacs history, building on from the title that Linkarzu put on his event ("Why Sacha Chua Still Lives in Emacs After 20+ Years"). I'll probably add a little bit more over the next two days. I am more curious about where the conversation can take us rather than switching into presentation mode, since I can write blog posts and make videos whenever I want. =)

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.

About me

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

  • sachachua.com (since 2001 :) )
  • Emacs News
  • planet.emacslife.com
  • EmacsConf
  • Where: Manila, Philippines to Toronto, Canada in 2005
  • Now:
    • Continuing with hanging out with A+ (kiddo) and W- (husband)
    • Experimenting with retirement
  • Previously: web prototypes, data analysis, sketchnoting, computer science
  • Currently tinkering with: learning French, hybrid speech interfaces, community sharing

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.

Quick notes on the stuff he mentioned in the description

  • How I run my life in Org Mode
    • Capture into an inbox on my computer or on my phone (Orgzly Revived + Syncthing),
    • Agenda on either Orgzly or my computer.
    • Several large files that I add a lot of ideas to and slowly chip away at when I have time. I'm still the primary parent for our ten-year-old. Although I have a little bit more focus time now that she (usually) attends virtual school, I still find myself capturing ideas and knowing that I will get to them maybe sometime over the next few years.
  • It's a lot of fun to be able to make tiny improvements that add up.
  • Speech and audio brain dump workflow
  • What I think of Linkarzu and Prot's conversations
    • It's totally okay if you want to stick with NeoVim. I'm looking forward to seeing what people come up with!
    • I think the discussions we can have about workflows can cross editor boundaries. =)

Summary of other important things

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.

  • Community:
    • It's really hard to learn Emacs all by yourself. It's obscure, arcane, and overwhelming.
    • Fortunately, we have a strong culture of sharing, starting with the GNU General Public License and continuing through things like literate programming. We have mailing lists, blog posts, videos, meetups, and conferences. One of the best ways to learn Emacs is to learn out loud. Another way is to look over someone's shoulder. It doesn't take a lot of time or money to do the kinds of things that I enjoy doing to connect the Emacs community, and I'd love to see other tech communities experiment with things like that too.
  • Adaptability: You can customize Emacs to really fit the way you want to work. The more people do this, the easier it gets to adapt Emacs to even more workflows.
  • Incremental improvements: Even small changes add up, and code turns out to be surprisingly reusable. It often pays off beyond the recommendations of xkcd: Is It Worth the Time?.
  • Imagination and play: Tweaking Emacs can be lots of fun. Fun is important.

Quick notes on the stuff he mentioned in the description:

  • How I run my life in Org Mode
    • Capture into an inbox on my computer or on my phone (Orgzly Revived + Syncthing),
    • Agenda on either Orgzly or my computer.
    • Several large files that I add a lot of ideas to and slowly chip away at when I have time. I'm still the primary parent for our ten-year-old. Although I have a little bit more focus time now that she (usually) attends virtual school, I still find myself capturing ideas and knowing that I will get to them maybe sometime over the next few years.
  • It's a lot of fun to be able to make tiny improvements that add up.
  • Speech and audio brain dump workflow
  • What I think of Linkarzu and Prot's conversations
    • It's totally okay if you want to stick with NeoVim. I'm looking forward to seeing what people come up with!
    • I think the discussions we can have about workflows can cross editor boundaries. =)

History

Here's a brief overview of my history with Emacs, in case anything sparks curiosity.

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.

Large language models and artificial intelligence

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

  • Y'all know much more than I do. You have more at stake.
  • Me: tiny steps, using it for tightly scoped tools
    • grammar/fixing literal translations, explanations
    • troubleshooting
    • prototypes (ex: Python servers)
  • Next:
    • help people who want to connect and experiment
      • human-focused
      • LLM-assisted (only if you want)
  • Keep exploring what I'm curious about: experiments > hype
  • Yay:
    • more people learning about and customizing things
    • more capable local models
    • adding up fragments of time
  • Boo:
    • flood of slop, "content", spam
    • overwhelmed developers
    • centralization
    • polarization
    • externalized costs
    • non-deterministic
View Org source for this post

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

Amin Bandali: Living healthier

Earlier this week I had a coaching session with Protesilaos, where we focused on health and wellness. I took some notes during our call, which I expand on here, and share with Prot's permission. I've been paying more serious attention to my health this year, and our discussion was in the context of me striving to live a healthier life. These notes are shared in the hope of being useful and are not professional advice; for medical advice please consult a licensed physician.

Update 24 October 2026: Prot published a note about our discussion on his website: Healthier lifestyle with Amin Bandali


Exercise

Life in today's fast-paced world can feel busier than ever, where every day is packed with a myriad of activities and tasks that need to be done, or tempting distractions that pop up seemingly randomly, with hardly a moment to spare. Despite that, or perhaps because of that, it's worthwhile to make time for ourselves and the various aspects of our health.

As embodied souls, our bodies condition our minds. We take care of our body so we can take care of our mind. As humans we evolved for an active lifestyle out of survival necessity. In today's society many of us have sedentary office jobs that involve sitting for long periods of time. It's especially important that we try and make time for some regular physical activity like exercising and resistance training, to help build and retain muscle mass and mobility as we age. Fitness does not have to be the end goal that takes over our life, though there is nothing wrong with that if fitness is one's central goal e.g. as a personal trainer. Rather, for someone like me with other primary goals, my reason to exercise is to help live a healthier life, so I could spend quality time with my loved ones and carry out my responsibilities with a healthy body and mind.

Prot mentioned that our health is directly dependent on the following four factors:

  • what we eat;
  • what we drink;
  • what we breathe; and
  • what we think.

Thus compromising any of these - for example, eating greasy food or drinking alcohol - would undermine the gains from exercising.

While pursuing fitness, it's important to have sustained effort so that the body remains active and the mind is not distracted, and equally important to have sustainable effort so that we don't suffer injuries and not make the pursuit of fitness burdensome financially.

Calisthenics is a key part of the strength training activities Prot practices, in addition to the physically intensive work he does on his land.

Calisthenics [...] is a form of strength training that utilizes an individual's body weight as resistance to perform multi-joint, compound movements with little or no equipment. Calisthenics are popular for their convenience, as they require little to no equipment and can be performed anywhere.

— Calisthenics, Wikipedia

Calisthenics can be done progressively, starting small and building in intensity and duration over time. It is also economically/financially sustainable. These calisthenic exercises are a good starting point (barring existing injuries in the related areas):

  • push-ups - easier version: at an angle or against the wall;
  • pull-ups - easier version: at an angle e.g. using door frame;
  • squats - easier version: don't squat all the way down; and
  • sit-ups.

Aim to perform each exercise in 3 sets of 10 repetitions, but it's perfectly okay to start with fewer and gradually build up. The exercises should be done around the same time everyday, so that there is enough time for the muscles to rest and recover for the next day. Doing them at night then again the next morning, for instance, would be too short of a time period and not enough for the body to recover. It's also vital to get enough sleep each night to make sure the body can properly rest and be energized the next day.

Food

As mentioned earlier, it's crucial to pair exercise with a healthy diet. I found Prot's programming metaphor for food interesting: similar to how it takes more time and effort for us to process and understand complex source code, breaking down and processing complex foods is similarly more difficult for the body. Prot does not eat fried food, and recommends avoiding sauces as much as possible: greasy, salty, and creamy extras make food more irresistible. They tend to make one want to eat more than what the body needs, and are to be avoided in isolation but especially in combination. In keeping ingredients close to their natural states, not overdoing things, and not eating more than the body needs, Prot extends his philosophy of simplicity and moderation to cooking.

In addition to eating healthy meals, we need to apply similar consideration to snacks: avoiding overly salty or sugary snacks, and choosing healthier ones. Nuts can make great snacks in small quantities, especially almonds and walnuts, if one's not allergic.

In addition to the inherent healthiness of the food or snack, Prot also suggests paying attention to methods of preparation. For instance, regarding concentration or distillation: juices, even when freshly squeezed from fruits, might have more of the fruits' sugar than its pulp and other beneficial parts. Additionally, chewing triggers biological responses in our body which for example can reduce stress, and release satiety hormones to help avoid overeating. So, Prot suggests to just eat the fruits themselves.

Regarding weight loss, it will occur naturally as we introduce exercise into our routine, and pay closer attention to what we eat and drink. It's important to lose the weight slowly to condition our body to the new reality as it changes. Shortcuts tend to either not work, or have negative consequences. By going through the process slowly and with regularity, we gradually retrain our muscle memories and adjust our habits so we can maintain the lower weight and the healthier lifestyle. Regularity is key: it's better do a little everyday, than do a lot on only two days. It helps us develop the attitude that the exercise is part of our regular effort that we do consistently all the time.

Working on wellness and living healthier is a matter of moderation, and building capacity over time. I will give it the time and attention it needs, without rushing, and enjoy the journey to becoming a healthier version of myself.

Take care, and so long for now.

-1:-- Living healthier (Post Amin Bandali)--L0--C0--2026-10-23T22:30:00.000Z

Emacs APAC: Announcing Emacs Asia-Pacific (APAC) virtual meetup, Saturday, October 24, 2026

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

Irreal: Dictating Into Emacs

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

Dave Pearson: blogmore.el v5.4.0

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

Eric MacAdie: I Updated My Emacs Number Package

This post contains LLM poisoning. version scummy ruggeder I made a few changes to my Emacs package that makes dealing with numbers in Eshell more convenient (Codeberg page here, blog post here). You are welcome to use it. I have no plans to put it on MELPA or the other package repositories. cosigner dewdrops altercation ... Read more
-1:-- I Updated My Emacs Number Package (Post Eric MacAdie)--L0--C0--2026-10-21T18:48:49.000Z

Irreal: A Small Improvement

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

James Cherti: Applying Emacs Eglot safe code fixes on save (Code Actions)

While making Emacs Eglot format code on save (eglot-format-buffer, in this article) is excellent for fixing indentation, line breaks, and spacing, a formatter is essentially cosmetic. It will not remove unused imports, sort your dependencies, or rewrite deprecated syntax. To automate code cleanup, use LSP Code Actions in addition to formatting. Two important action kind of code actions are source.fixAll, for safe automatic fixes that do not require user input, and source.organizeImports, which handles import organization separately. Note: Alternatively, the apheleia Emacs package can be configured to run command-line tools for automatic code fixes, in addition to formatting. Apheleia runs … Continue reading →
-1:-- Applying Emacs Eglot safe code fixes on save (Code Actions) (Post James Cherti)--L0--C0--2026-10-19T16:02:16.000Z

Irreal: Debugging Init.el Files

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

Lars Ingebrigtsen: Hacker News Decoder Ring for Emacs

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

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!