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

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

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

As usual, this chart was generated using the /github-activity skill shared in my emacs-skills repo.
Four open issues is the lowest it's been since the project's first week, which I'm pleased about.
All of this requires daily attention.
agent-shell.Tokens keep your agents running. Sponsors keep agent-shell running.
agent-shell is built and maintained by me, an indie dev, while the tools it competes with have well-funded teams behind them. Vendor-neutral tooling matters more than ever.
Using agent-shell at work? Ask your employer to sponsor too. Companies can contribute at a scale individuals can't.
Thank you to all contributors for these improvements!
display-buffer-overriding-action when displaying asynchronously (@bcc32)agent-shell stands on acp.el to speak ACP and shell-maker for its shell foundations, so fixes landing there reach you just the same:
agent-shellAs of this writing, we're 88% of the way to the 150 monthly sponsors goal. If agent-shell is part of your daily workflow, please consider becoming a monthly sponsor (or ask your employer to).
Not in a position to sponsor? A GitHub star helps with exposure and brings in new users.
-1:-- agent-shell 0.87 updates (Post Alvaro Ramirez)--L0--C0--2026-10-13T00:00:00.000Z
-1:-- org-ibullets.el - Display Emacs org-mode heading stars as UTF-8 bullets (Post James Cherti)--L0--C0--2026-10-12T17:30:47.000Z
-1:-- wizard.el - A Lightweight suite of useful Emacs functions (Post James Cherti)--L0--C0--2026-10-12T17:19:21.000Z
People have been having lots of fun playing Emacs Golf over in r/emacs. Check out the "Fun" category for some examples of people comparing how few keystrokes they need in order to get something done.
Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, Mastodon #emacs, Bluesky #emacs, Hacker News, lobste.rs, programming.dev, lemmy.world, lemmy.ml, planet.emacslife.com, YouTube, the Emacs NEWS file, Emacs Calendar, and emacs-devel. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at sacha@sachachua.com. Thank you!
*
You can e-mail me at sacha@sachachua.com.
-1:-- 2026-10-12 Emacs news (Post Sacha Chua)--L0--C0--2026-10-12T11:58:28.000Z
Raw link: https://www.youtube.com/watch?v=5xNWU8EcUeI
[ The stream will be recorded. You can watch it later. ]
This evening I will do a live stream where I will continue developing
the new denote-data feature for denote: it is an opt-in, in-memory
cache with no dependencies that speeds up all sorts of operations.
I have made lots of improvements to it and it is already looking very
promising, though I plan to develop it further and eventually write
ert tests for it.
I will keep the chat on the side and shall respond to any questions about my programming but also any other issue (the other day I did a 5-hour live stream on the topic of “ask me anything” because my live streams tend to be that, anyway—recording here: https://protesilaos.com/codelog/2026-10-08-ask-me-anything-emacs-tech-politics-life/).
🔷 Consider making a donation.
-1:-- Emacs ‘denote-data’ testing live today, 2026-10-12, at 16:00 Europe/Athens (Post Protesilaos)--L0--C0--2026-10-12T00:00:00.000Z
I came to realize that attempting to wrap up the “Wordcraft” section (as I call it) in Emacs is a fool’s errand. Emacs is Wordcraft. It’s what it’s all about. By the time I finish a draft, a new config or setting changes what I have. This here is an attempt to stay true to my conclusion in the last section.
In my opinion, Abbrevs in Emacs is one of its fundamental features. It’s one of those things I’d show someone coming from other word-processing programs to show them the magic of Emacs. In essence, it’s a simple tool: you type two or more letters, and Emacs expands it to a phrase or even a whole paragraph. For example, when you type “hw” it will expand to “hello world” as soon as you hit space after the “w”.
Of course, you can achieve more with this useful function. We will get there. First, let’s discuss general usage:
To add a new abbrev, stand with the marker after the abbreviation, and then use: C-x a g for a global abbrev (which applies everywhere in Emacs), or C-x a i l for the specific major mode of the buffer you’re in (so, if you’re working in shell inside Emacs, it will add an abbrev there specifically.)
Once added, Emacs has a smart (and when you get it, helpful) way of remembering your lower-case/upper-case usage, depending on how you recorded the abbrev. Assume we’ve added “rnd” for “Research and Development”. This means:
You can escape abbrevs with C-q after typing the abbrev. If “rnd” expands to “Research and Development” but you actually want to type “rnd” in the buffer, type rnd followed by C-q1.
If you want to add a prefix to an abbrev, for example, you want the end result to be “non-Research and Development”, type: non- and then M-’ (this will result in a hyphen showing, which is good) and then rnd to produce the abbrev.
Finally, don’t forget about edit-abbrevs which lets you edit your abbrev file directly. This is good when you want to add a bulk of abbreviations (I did this when I wanted to add usernames: I copy-pasted a bunch of those from Outlook one day during lunch).
Alright, with the basics out of the way, let’s dig into the config itself.
First, we want to turn on Emacs’ abbrevs. In my case, I want to put the abbrev file in a folder I sync between my computers so these shortcuts are available on both of them. To do this, I need to set the path for the abbrev file:
(setq-default abbrev-mode t)
(setq abbrev-file-name "~/Sync/Personal/.abbrev_defs")
Now something a bit more advanced. I posted about this before, but I didn’t explain my use case, and I think its usefulness is a bit lost as a result. Here’s the general idea.
At work, we have user IDs (usernames) that I need to use with ServiceNow, our ticketing system. It’s also useful when I want to write an email, and there are five people named “John.” My brain solved this problem years ago with nicknames: the John who has tattoos and a beard looks like a motorcyclist, so I call him Harley (Davidson); the other John loves tea from Georgia, so I call him George, etc.
When I want to dig out Harley’s username, I prefix it with @, and Emacs spits out “jd1234” for his username. This way I don’t hijack the word “Harley” to always be associated with jd1234, only when I really want to use it2.
The post I linked above explains how this works and gives proper credit to Prot, who helped me build this up using regex:
(abbrev-table-put global-abbrev-table :regexp "\\(?:^\\|[\t\s]+\\)\\(?1:[@].*\\|.*\\)")
(abbrev-table-put org-mode-abbrev-table :regexp "\\(?:^\\|[\t\s]+\\)\\(?1:[@].*\\|.*\\)")
When it comes to grammar, I explored two options in Emacs. I first started with LanguageTool, then discovered Harper, which I use today. But as these Emacs posts go, I need to take a step back and talk about spelling and grammar checkers first.
With a spell-checking tool, we just need a dictionary: a list of words the program checks against what we typed and identifies the closest match in its list. The mechanism behind it is a mathematical principle of edit distance: it is a fuzzy search for words that are the closest to the misspelled one3. This can be complemented with a personal dictionary of additional unofficial words that we tend to use, and that’s mostly it; we’re good to go.
A grammar checker is a different animal, or actually, animals. And before we start talking about these, it’s worth mentioning that grammar, or what is generally considered “correct” language, is a moving target4. Language is a grey area, where what’s correct can be — at least to a degree — subjective.
The complex mix of tools used in what we cram together today under the term “grammar checkers” is a big underestimation of the history and development of the last 50 years or so of progress and the different tools that we use today. With modern-day LLMs, the confusion extends further, as these usually don’t use any of those tools, relying only on transformers and weights to predict what seems correct and what isn’t5.
Harper
Many Emacs folks, myself included, use Harper these days. It’s no wonder, given its speed and minimal install size, especially compared with LanguageTool, the other free alternative. But this lightness comes at a cost.
Harper provides:
… and that’s it. Indeed, in my first post about Harper, I noted a couple of basic errors it missed, and I still think it’s far from “the private alternative to Grammarly,” as it states on its homepage. I actually barely use it because it’s the competition that does a much better job, grammar-wise. If it were up to me, the tagline would be “a private, fast, customizable grammar checker for when you don’t want a grammar nanny looking over your shoulder.”
Harper is available on homebrew (it seems the folks behind Harper made a dedicated dmg since I last installed it), but for Ubuntu and its kin, it requires Cargo. This also means that if Harper is the only program you have from Cargo, you also need to remember to update it6. I’ve written and explained my Harper setup a few times, but it evolved over time to accommodate both my Mac and my Linux desktop, so I’m including the whole thing here. Note that Harper works through Eglot on Emacs, as I explained in the first post linked above.
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs
'(org-mode . ("harper-ls" "--stdio")))) ; This is the eglot-related integration
(setq-default eglot-workspace-configuration
'(:harper-ls (:dialect "American" :linters (:LongSentences :json-false :AvoidCurses :json-false))))
;; These are options for Harper. I chose American dialect as the language, I also want to ignore long sentence errors (the main issue is that it hides other errors nested in those) and I also want Harper not to tell me when it thinks something is offensive. The full list of these options is in https://writewithharper.com/docs/rules. It needs to be nested inside the :linters option.
(when (eq system-type 'gnu/linux)
(add-to-list 'exec-path (expand-file-name "~/.cargo/bin"))
(add-to-list 'exec-path "/snap/bin"))
;; On Linux, harper-ls lives in ~/.cargo/bin (installed via Cargo/Rust).
;; /snap/bin is added for Vale (installed via snap - that's another program I might discuss, ignore it for now) and any future snap packages.
;; On Mac, Harper is installed via Homebrew which handles PATH automatically.
LanguageTool
LanguageTool provides:
In addition, as I was researching more into LanguageTool, I found lesser-known layers and additions that are not fully documented. For example, there’s a neural network for confusing pairs included. I don’t know how important those other tools are, but if you use the full product online to get a taste, you will see the difference when comparing it to Harper immediately.
While it sounds like I’m favoring LanguageTool over Harper, I don’t have it installed anymore, and I haven’t used it in a while. The reason for this is that it requires Java and over 10GB (yep) if you want to get the n-gram running with the other additions. Below is my old config, based on version 6.1. You will need to install langtool.el to run it, without the full layers discussed above (by the time I found out about these options, I stopped using it):
(require 'langtool)
(setq langtool-language-tool-server-jar "~/Path/To/Current/LanguageTool-#.#/languagetool-server.jar")
(setq langtool-default-language "en-US")
All in all, I do not think Emacs is the best place for grammar checkers. YMMV, but since I have access to Grammarly Pro’s features, I prefer it. I also use AI to go through my posts, and it does a few very useful things, grammar being just one of them if I need it to. If I didn’t have Grammarly, I’d consider LanguageTool’s pro package, which comes with additional perks that are not available for a local install.
My logic here is simple: if I write a post for my blog, which is going to be public anyway, I don’t mind using online tools or LLMs. For more sensitive things, like my journal, I use Harper if I want to, but most of the time it’s an overkill for my own mind’s rambling anyway. This is why I said above that I barely use Harper, as I don’t really have a use case for it.
As I’m writing this, I realize another simpler trick is Emacs’ undo. Simply use C-/ (or whatever you have it bound to) and it will cancel the expansion. ↩︎
Now that I use corfu, which also contains my abbrevs, typing “george” will bring up a menu allowing me to choose george from a drop down instead of specifically typing @george. ↩︎
The mathematical concept behind this is called Levenshtein distance, after Vladimir Levenshtein, who came up with this in 1965. The basic idea in spelling programs is to measure how many “jumps” are required to correct a mistyped word. For example, “helo” to “help” is one jump, as well as “helo” to “hello”; “helo” to “hallow” though substitutes e with a, inserts an l, and then inserts the letter w — 3 jumps. This means that pure Levenshtein spell checkers will rank the first two corrections higher on the list (modern spell checkers introduce a few more tweaks). Wikipedia gives us an interesting history lesson here: Unix got Ispell, which is based on SPELL, and GNU built upon that with Aspell, which is what we have in Emacs today. The front end in Emacs, ispell.el, is named after the original program it once used. ↩︎
Brennan wrote a good post about this recently with plenty of links, which I’m stripping here because I want you to go read his whole thing, not just my footnote: “To ask someone to write properly is to assume there is a way to write proper. There isn’t. There is no universal, static version of English. Or any other language, for that matter. Every dialect of English, including regional accents and non-standard forms, follows complex, systematic grammatical rules.” ↩︎
While most popular LLMs today are generic and don’t rely on more traditional grammar tools, specific grammar products like Grammarly and Langtool do use specific traditional tools as well as an LLM on another layer, making the box of grammar checkers as a category even more vague. ↩︎
After realizing how often I forget to update Harper on Linux (I don’t get reminders as I do with other packages), I searched for a solution and discovered topgrade. It’s a Rust tool that probes your system, figures out which packages you use as well as your hardware, and offers you all the updates in one go: apt, flatpak, cargo, snap, and even updates for your boot, if you have a Linux machine from a brand that supports it, like System76. ↩︎
n-gram is a sequence of symbols, or in our case, words, where n is the number of words. So “a nice sunset” is a 3-gram sequence and “they are wearing hats” is a 4-gram one. Grammar checkers look at many of these sequences in a large body of text and evaluate whether what we type has errors. For example, if I write “they are wearing hat,” the checker will run this against several examples and will find that “they are wearing hats” (with an s at the end) is much more common than the first example, which means the first example is probably wrong and should be corrected to what is found in the text. As you can imagine, the checker needs to look at a lot of text to make good corrections, which is why that body of text is huge — hence the size of a few hefty gigabytes. LanguageTool explains n-grams and how they use them, which is helpful. If this sounds similar to LLMs and how they work, it’s because the connection exists: the basic idea of looking through a large body of text is the same, and in a way, LLMs today are built on a similar concept. However, LLMs take this a step further and compute what would make sense using weights and other settings in real time, while n-grams are essentially static lists of words the computer just compares against. There’s no “thinking” involved. It helped me to think about it this way: in terms of hardware, your hard drive reading speed is way more important for n-grams than your GPU. ↩︎
-1:-- Emacs Config Gems - Part 6 (Post TAONAW - Emacs and Org Mode)--L0--C0--2026-10-11T22:54:34.000Z
I’m still under the weather: mostly I’ve been sleeping non-stop except for meals and bathroom breaks. That means that I’m still not up to producing a post with the usual Irreal wit and charm1. I did have this post from Curtis McHale about the future of Emacs interaction in my TODO queue so I decided to do a quick write up about it.
McHale’s contention is that we use keyboards because they’re the best way of interfacing with computers. He points to everybody’s favorite keyboard replacement, voice input, but notes that even on Star Trek—that arguably popularized the idea—Captain Picard still tells his crew members where he wants to go and they type in the commands at their control stations.
As I said in my last post on the matter. my written thoughts are always much better organized than my verbal ones so moving to spoken input would be a net loss for me. Still, it will be interesting to see how things play out.
OK, OK. Maybe that overstates my usual efforts. I blame whatever sickness I have for that.
-1:-- Will The Emacs Keyboard Die? (Post Irreal)--L0--C0--2026-10-11T17:24:04.000Z
Every now and then, I use Blogger as a personal system for publishing articles.
The articles are structured with sections and subsections and an initial table of contents.
The articles are mainly written in Org Mode using Emacs.
In this case, the table of contents is generated by the writing system itself during the conversion phase from Org Mode to HTML.
-1:-- Automatic TOC in BLOGGER articles via HTML code (Post D.V.F.P.)--L0--C0--2026-10-11T00:00:00.000Z
-1:-- [TASK] Re: orgmode: do textsec-url-suspicious-p for http and https links (Post Org Mode requests)--L0--C0--2026-10-10T10:09:02.000Z
For more than a decade the Clojure REPL most people saw in a terminal was
REPLy, the one behind lein repl. It
served us well, but it’s showing its age, and I kept wishing for a terminal
REPL that felt like CIDER, without the Emacs part. So I finally built one.
neorepl 0.1 is out today!
There’s no shortage of Clojure REPLs, so it’s fair to ask. Here’s what I was after:
rebel-readline and bb repl --connect are both great, and I borrowed from
them freely, but neither tries to be an nREPL-first tool with CIDER’s tooling,
jack-in and a CLI for agents. That combination is the whole point of neorepl.
Update (2026-10-11): A few people asked how neorepl relates to the
Clojure CLI REPL, which the
Clojure team released a couple of weeks ago, and to bb repl --connect. (I’m a
bit embarrassed to admit I hadn’t heard of the former until after I announced
neorepl.) Funny enough, all three are nREPL clients under the hood, but they’re
after pretty different things. In practical terms:
datafy-based inspector - and I’ll be borrowing some
ideas from it. It doesn’t do completion yet.bb repl --connect puts babashka’s console REPL in front of any nREPL server,
with completion, arglists and docs, in very little code. It’s a great way to
poke at a running server, but it won’t start one for you.So, if you want the fanciest line editing and you live in deps.edn land, give
the Clojure CLI REPL a try. If you want CIDER in your terminal, for you and
for your agents, that’s what neorepl is for. The neat part is that babashka’s
server bundles CIDER’s inspector and test runner ops these days, so neorepl’s
test runner (already on main) and the inspector that’s coming next work with
babashka out of the box.
Here’s the REPL in action:

A few things worth pointing out:
,doc, ,source, ,apropos,
,macroexpand, ,pst, ,reload, ,edit (which opens a definition in your
$EDITOR) and ,help for the rest. The Clojure reader sees a comma as
whitespace, so they can’t clash with real code.*in* gets its input,
something REPLy never quite got right.Without a server to connect to, neorepl starts one for the project, the way
CIDER’s jack-in does, with cider-nrepl on the classpath, and stops it when you
exit. It knows Leiningen, the Clojure CLI, babashka and shadow-cljs projects.
ClojureScript works too. Start a ClojureScript REPL in the session (through
shadow-cljs or piggieback, which jack-in adds for you) and neorepl follows
along: evaluation, completion, docs and the rest switch to ClojureScript, and
:cljs/quit takes you back.
Coding agents are surprisingly good at Clojure, as long as they have a REPL to talk to. Most of them drive CLIs much better than anything else, so neorepl has a CLI that’s meant to be driven:

neorepl eval evaluates code on the project’s server. Calls share a
session, so *ns*, *1 and *e carry over from one call to the next, and
--session keeps separate lines of work apart.--format json (or edn) gives you the values, the output and the
namespace as data.--timeout interrupts the evaluation before giving up, so an infinite loop
doesn’t take the server down with it, and --max-output keeps a stray
(range) from eating the agent’s context.neorepl check finds unbalanced parens, brackets and braces without a
server. That’s the most common way for an agent (or a human) to break a
Clojure file.neorepl doc, source, apropos, complete, where, macroexpand and
reload do what the REPL’s commands do.neorepl server starts a server that stays up, for editors, agents and
neorepl eval to share.The exit codes are boring on purpose: 0 when all went well, 1 for an
evaluation error, 3 when there’s no server to talk to and 124 for a timeout
(like timeout(1)).
A new client is also a new way to break servers, or to be broken by them, and the nREPL world has quite a few servers these days. Fortunately I’ve been working on proof, a compatibility suite for nREPL, which made neorepl a good test subject.
proof checks both sides of the conversation. proof proxy sits between a
client and a server and grades everything the client sends: every request has
an id, need-input gets answered with stdin in the right session, and so on.
proof serve is a small nREPL server that can behave like other servers in
very specific ways - sending only the last value of the code like Basilisp and
jank, dropping stderr, having no interrupt op, splitting output into one
message per character, and so on. I ran neorepl’s REPL and CLI through both,
against nREPL, babashka, Basilisp and jank, and through every quirk proof
serve knows about.
neorepl came out of it in decent shape, but not unscathed:
neorepl eval -) that then read *in* itself crashed
neorepl and left the evaluation waiting on the server for input that was
never coming.neorepl eval exited with 1 when it lost the server, which is the status
for code that failed.All of those are fixed in 0.1. It also turned up a couple of things that are
nREPL’s fault rather than neorepl’s: need-input and the end of an
interrupted evaluation can go to the wrong connection when a session is used
from more than one. Those will be fixed in nREPL itself.
It works the other way around too. proof now checks servers with the exact requests neorepl sends while starting its REPL and evaluating code, next to the ones CIDER, Calva, Conjure, vim-fireplace and REPLy send, so a server that breaks neorepl finds out from proof, not from a bug report. nREPL, babashka, Basilisp and jank all pass those checks today.
Building neorepl turned up a few problems elsewhere, which is one of my favourite side effects of writing a new client. piggieback used to evaluate only the first form you sent it and silently drop the rest, and it loaded the ClojureScript compiler at startup even when nobody asked for a ClojureScript REPL, which added almost half a second to the startup of every server with ClojureScript on the classpath. The fixes for both (piggieback#162 and piggieback#161) will be in the next piggieback release. The Node.js REPL in ClojureScript itself doesn’t cope well with interrupted evaluations, and that one is next on my list.
0.1 is the foundation. Here’s what I’d like to tackle next:
The easiest way to install neorepl is bbin:
$ bbin install io.github.nrepl/neorepl
$ cd my-project
$ neorepl
The README has the details.
It’s early days, so I’m sure you’ll find rough edges. Please file issues,
and tell me what you’d like to see next. And if you’re one of the people who
kept lein repl and REPLy going over the years - thank you! neorepl wouldn’t
exist without you.
Keep hacking!
-1:-- neorepl: A Terminal REPL for Clojure (and Friends) (Post Meta Redux)--L0--C0--2026-10-10T09:38:00.000Z
-1:-- Repeating commands (Post Marcin Borkowski)--L0--C0--2026-10-10T05:14:56.000Z
The topic that wouldn’t die. It started with a quick post on Sacha’s solution to a problem posed by David Pape. I thought it was a nice solution but then 0-_-0_ pointed out that abo-abo had already solved this problem with his tiny library. So I wrote about that and prepared to move on.
I was not allowed to. Frequent commenter Phil remarked in my original post that he would use a keyboard macro. He suggested that that would make sense for those of us who who seldom need to enter a sequence of ranges like Pape. In Pape’s case this was something that he needed to do a lot so a solution like Sacha’s or abo-abo’s make sense. I agree with Phil. A keyboard macro is exactly how I would solve this problem given that I seldom have it.
This seems pretty obvious and might not be worth writing about except that Phil’s solution showed me something that I didn’t know: You can set the initial value of the counter with a Ctrl+u n where n is the desired initial value. That’s a lot easier than using Ctrl+x Ctrl+k Ctrl+c to do the job.
As a final note, I think that Phil had a typo in his macro. As far as I can tell it should be
C-u 47 <f3> # <f3> , SPC <f4> <f4> <f4> <f4> <f4> <f4> <f4> <f4>
At least that’s what works for me.
It turns out that I regularly use a macro with a counter so I’m really happy to learn that the counter can be set so easily.
Update : Fixed link.
-1:-- Still More On Entering Ranges (Post Irreal)--L0--C0--2026-10-09T15:34:21.000Z
In a nice coda to yesterday’s post on Sacha Chua’s solution for entering number ranges, 0-_-0_ points out that a solution already exists. That solution is abo-abo’s tiny library. I vaguely remember seeing tiny when abo-abo released it 13 years ago but I never followed up and used it. Still, anything from abo-abo is worth taking a look at and, in any event, we now have two solutions to the problem.
Abo-abo’s solution brings to mind another Emacs lesson: very often, if you need some functionality, there’s a good chance that it already exists. Of course, that’s not the same as being easily discoverable. An internal function can usually be discovered through the on-line documentation but a third party library like tiny won’t appear in that documentation until it’s loaded.
That’s it for today. I just wanted to tell the rest of the story from yesterday.
-1:-- More On Number Ranges (Post Irreal)--L0--C0--2026-10-08T15:33:30.000Z
-1:-- Fixing the Emacs 31+ Tree-sitter syntax highlighting issue in indirect buffers (The Emacs maintainers will not fix it, so I did) (Post James Cherti)--L0--C0--2026-10-08T12:45:56.000Z
I use org-use-speed-commands all the time when navigating and performing actions on org headlines, but I think I have found a friction point, something that just feels a little off.
When the point is on * and then you press the key t, it calls org-todo via org-self-insert-command. Fast, but the feel is off, the point stays on the stars, so I have to move right before I can type the headline.
The reason is in org-todo itself. It has a cursor fixup that parks point just after the keyword, but only when point is not at beginning of line. Speed keys by definition run at beginning of line (org-speed-command-activate requires (bolp)), so the fixup never fires for exactly the keys that would benefit most.
There is no custom option for it, so I added a small :after advice:
(defun my/org-todo-stop-after-keyword (&rest _)
"Move point to just after TODO keyword when `org-todo' leaves it at BOL.
Speed command `t' (via `org-self-insert-command') runs with point at
beginning of headline, where `org-todo' skips its cursor fixup."
(when (and (bolp) (org-at-heading-p))
(when (looking-at org-todo-line-regexp)
(goto-char (or (match-end 2) (match-end 1)))
(when (looking-at " ") (forward-char 1)))))
(advice-add 'org-todo :after #'my/org-todo-stop-after-keyword)
Now * Test with point on * plus t becomes * TODO Test with point on Test, ready to type. Same for cycling * TODO to * DONE.
Small change, but speed keys finally feel like they finish the job instead of leaving me parked on the margin!
-1:-- Org Speed Keys: Making t Land After the TODO Keyword (Post James Dyer)--L0--C0--2026-10-08T10:03:00.000Z
gravity
Product: Gravity
Price: $9.99
We are all familiar with the act of note-taking. A thought occurs to you, you switch to your note-taking application and face a slew of decisions:
Our note-taking systems have evolved into a quagmire. We are now in the business of managing our systems. Instead of taking notes, we work incessantly on our note-taking system. Soon we avoid taking notes, it is too much bother.
Look at the discussions on the Zettelkasten Forum:
Or go to Obsidian Forum. You will see the same discourse. People obsessing over the system.
I had heard of Andrej Karpathy. Couldn’t tell you in what context, but the name was familiar to me. He wrote the following: The append-and-review note – karpathy.
Go read it, it is a small read. I will wait.
His system is two steps: Append and Review. Append to a single file. No tags, no metadata of any kind. This is the line which stood out to me: “When I note something down, I feel that I can immediately move on, wipe my working memory, and focus fully on something else at that time. I have confidence that I’ll be able to revisit that idea later during review and process it when I have more time.”
He periodically reviews his notes moving things which are important at the moment to the top. The rest of the information continues to sink to the bottom, like gravity.
The main advantage of this system? No friction. No decisions at the moment of entering a note/thought. Nothing lost. Nothing falls through the cracks.
Gravity is an attempt to provide an environment for this system to work. It can be a menu-bar only application. You press a keyboard command wherever you are. Up pops an editor window. You type or paste in what your thought was. Hit ⌘↵ or three ↵’s. The note is saved. You are back to the program you were in initially.
Periodically you can review your notes. The ones which are more relevant you can nudge up the chain. Everything else keeps getting demoted and drops down the list, like gravity.
Gravity is powered by a SQL database as its back end. You can export your whole repository of notes to a Markdown file, a text file, or a JSON file. So even though it is a database, there is no lock in to the product.
I was intrigued by the following line from the developer, Tristan Manchester:
Gravity has one job: minimize the time between “I have a thought” and “the thought is captured and I can move on.”
That appealed to me and I got curious.
gravity - window
A keyboard command, you can customize, from anywhere and this is the Gravity window. All of your thoughts and their content in a window.
gravity - capture
This is the capture window. A keyboard command, customisable, and this window pops up. You enter your thoughts, hit the return key twice, or ⌘+↵ and the content is saved to Gravity. You hit ⇧+↵ key to add a new line. (I notice that a simple ↵ does add a new line too).
In the Gravity window, you have the choice of promoting any entries you need to the top of the list. If you have a long list, this makes items which are relevant to you be readily accessible and it is the premise behind the program. Items which are not relevant for the time being keep sinking to the bottom.
You have the option of archiving or deleting thoughts you want to keep or want to discard.
It is all done in Gravity.
They are well thought out and well designed.
pref - general
You will want Gravity to launch at login. You want it to be always available. I don’t much care for the program to show up in the Dock. You can choose to have the two ↵’s to save the capture or rely on the usual ⌘+↵ command. I like having both.
pref - keyboard 1
pref - keyboard 1
pref - keyboard 1
The program is fully keyboard driven. I love this aspect of the program and they are all customisable. You set your own or stick to the ones shipping with the program. Regular readers know how much I like this feature.
pref - syncing
You can choose to sync to iCloud. That allows the same document in multiple machines. I love it.
pref - search
You can choose to have the archives included in the search. If you have a long list of entries in the current focus and want to move some over to the archive, you can still include them in the search and nothing is left out of your searches.
pref - advanced
This is where you can export your whole collection of notes to a Markdown file, a plain text file or a JSON file.
pref - more advanced
You can monitor some stats about your usage of the program. Optimize your SQL database. There is also a command line tool (I haven’t explored this much. Although I have installed it).
This is from the developer of Gravity:
I get the same feature request about once a week. “Love Gravity, but could you add folders?” “Would be perfect if I could tag notes.” “Any plans for linking between notes?” “An API would be amazing.” The answer to all of these is no. Not “not yet”. Just no.
This probably sounds like bad product thinking. Aren’t you supposed to listen to users? Give them what they ask for? Iterate toward their needs?
Usually, yes. But there’s a specific reason Gravity works the way it works, and adding these features would break it. The limitations aren’t gaps. They’re the design.
Someone recently asked me why Gravity doesn’t have folders. My first instinct was to say “we’ll add that eventually.” Then I caught myself. We’re not adding folders. We’re not adding tags either, or bidirectional links, or a plugin system, or templates, or a knowledge graph. These aren’t items on a roadmap we haven’t gotten to yet. They’re deliberate omissions. The missing features are the product.
I found this interesting. Mind bending, but interesting. Then I remembered:
Gravity has one job: minimize the time between “I have a thought” and “the thought is captured and I can move on.”
If you are looking for a capture application which is designed to do just that, have a thought, capture a thought, move on with life, then Gravity is the ideal tool for that need.
It is well-designed software which does that and does that well.
I am old. I forget things. There are too many times during the day when I have a thought and it floats away a few minutes later. For instance, read the Org manual and read the section on narrow and widen. A few minutes later, I have no recall of what I was thinking about. My solution? Capture the thought. Do I want to capture the thought in a stream of thoughts? Or do I want to categorize the thought into a file?
The program I use for this is Capture for Mac. I have a text file which has markdown headings like:
The Capture window in Capture gives me the option of inserting the content under a specific heading. It adds a time stamp and some context if I have highlighted something. That is what I use for capture at this point.
You can follow a similar arrangement in Gravity. Prepend some text to the capture. For instance, things about Emacs, can have Emacs: to start with, or Read: for things to read later or Watch: to watch later, and so on. And the system works because the Search function in Gravity is excellent.
I understand that the goals of the two programs are different. They are not solving the same problem. If your need is to record thoughts and move on with your life, Gravity as a system is perfect for that. I love working in it. I admire what it is trying to do and feel it is perfectly designed for that.
("o" "OBTF" entry
(file+headline "~/Dropbox/org/obtf.org" "OBTF")
"Created: [%(format-time-string \"%Y-%m-%d %H:%M\")]\n %?"
:prepend t
:empty-lines-before 1
:empty-lines-after 1)
You know I have to do everything in Emacs. I added this Org-capture template which lets me perform a similar function in Emacs. I can capture content to a file called OBTF.org. I can then refile content to the top or the bottom of the file through danielsz/Palimpsest. I can have an Archive heading and move sections to it through org-refile. All of this is more effort than Gravity, but it has the added advantage of being in Emacs.
Blog — Thinking About Capture — Gravity
No, We’re Not Adding Folders — Gravity
Gravity is a fantastic application to capture your thoughts. It is a product with a defined philosophy and it sticks to its vision.
I recommend Gravity heartily for your capture needs.
macosxguru at the gmail thingie.
Note: Thanks to Tristan Manchester, the developer of Gravity, for providing me with a promo code when I asked for one.
-1:-- Using Gravity For Your Thoughts (Post Bicycle for Your Mind)--L0--C0--2026-10-08T03:52:01.000Z
Raw link: https://www.youtube.com/watch?v=KuxjRH6RF0U
[ The stream will be recorded. You can watch it later. ]
This Saturday, 10 October 2026, at 16:00 Europe/Athens time, I will do an “ask me anything” live stream. I have been meaning to do this for a long time, but I never found the chance for it.
Ask me a question about anything—literally ANYTHING—and I will give you an honest answer (e.g. Emacs, philosophy, technology, keyboards, politics, the hut, Français, and more!). It does not matter if the question is personal or sensitive. I will give you a complete answer to the best of my abilities. If you need any ideas, check the rest of my website, where I write at length on a broad range of topics.
The plan is to do a live stream for at least 1 hour. If there are many questions, I will extend it to 2-3 hour or, generally, for as long as needed. And if there are no questions, then I can do something related to Emacs or close the stream earlier—no problem!
🔷 Consider making a donation.
-1:-- Live: Ask me ANYTHING (2026-10-10 16:00 Europe/Athens) (Post Protesilaos)--L0--C0--2026-10-08T00:00:00.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!