textpod.el v0.3.0
Another round of improvements of textpod.el, a companion Emacs package to Textpod.
Custom ID format - attribute name and prefix
Added two defcustom options in textpod.el:
textpod-id-property(default"TEXTPOD_ID") - name of the Org property used to store the note id.textpod-id-prefix(default"") - prefix prepended to ids when stored, stripped before sending requests to the server.
Introduced textpod--strip-prefix helper and updated all id call
sites (textpod-open-current-note, textpod-org-heading-to-note,
textpod-org-region-to-note) to respect these configs.
textpod-set-id sets ID without creating a node
I need a function to create texpod id property without sending note to the server. This is needed to submit old notes so they have proper timestamp.
Added textpod-set-id: an interactive command that sets
textpod-id-property on the current heading (with textpod-id-prefix)
derived from a time as YYYYMMDDhhmmss, without making any HTTP
request.
- No prefix arg → uses
(current-time). - With prefix arg → prompts via
org-read-date, so the note can be back-dated before later uploading withtextpod-org-heading-to-note.
Quickly getting to Textpod notes
Using Ack is possible, placing point on a file-name and pressing {H-RET} will call visit-source and go directly to the line.
ack ":ID:\s*textpod_" *.org | grep -v 2026-04-22 | head -n 20
2026-04-12.org:226::ID: textpod_20260412124048
2026-04-13.org:10::ID: textpod_20260414214110
2026-04-13.org:237::ID: textpod_20260414212612
2026-04-13.org:247::ID: textpod_20260414224720
2026-04-13.org:263::ID: textpod_20260414210103
2026-04-13.org:314::ID: textpod_20260414224944
2026-04-16.org:12::ID: textpod_20260416100352
2026-04-16.org:97::ID: textpod_20260417092822
2026-04-21.org:205::ID: textpod_20260421120000
Since I made my Textpod notes to be Org-roam notes, I can query them directly from SQLite DB.
-
(sqlite-mode-open-file "~/.config/emacs/org-roam.db")
select id, file, title from nodes n where n.id like '"textpod_%"';
| textpod_20260329123259 | ~/Notes/2026-03.org | Standalone HTML Galleries |
| textpod_20260315124829 | ~/Notes/2026-03.org | Build HackerNews Legible Frontend |
| textpod_20260415130636 | ~/Notes/2026-04.org | Improve public notes |
| textpod_20260415205822 | ~/Notes/2026-04.org | “Why Kim Wexler Was The Real Villain of Better Call Saul” by BreakingHub (19 min) |
| textpod_20260415210948 | ~/Notes/2026-04.org | “Linus Lays down the Law” – The PrimeTime (ThePrimeagen), 13 min |
| textpod_20260412124048 | ~/Notes/daily/2026-04-12.org | Improve HackerNews Legible Frontend |
| textpod_20260414214110 | ~/Notes/daily/2026-04-13.org | Interview about k8s setup in Amazon |
| textpod_20260414212612 | ~/Notes/daily/2026-04-13.org | kumo – local AWS emulator in Go |
| textpod_20260414224720 | ~/Notes/daily/2026-04-13.org | Snowflake MCP server |
| textpod_20260414210103 | ~/Notes/daily/2026-04-13.org | Claude Code quota exhausted / degraded quality |
| textpod_20260414224944 | ~/Notes/daily/2026-04-13.org | Trying Textpod once again |
| textpod_20250612070826 | ~/Notes/html.org | I'm betting on HTML |
| textpod_20260421120000 | ~/Notes/daily/2026-04-21.org | Rewrite Textpod to Go |
| textpod_20260416100352 | ~/Notes/daily/2026-04-16.org | Advice to org-roam-dailies-goto-today |
| textpod_20260417092822 | ~/Notes/daily/2026-04-16.org | Links <2026-04-16 Thu> |
| textpod_20260422124810 | ~/Notes/daily/2026-04-22.org | textpod.el v0.3.0 |
[TK: :colnames yes got broken for some reason].
My textpod.el config
The config automatically makes notes published to Textpod as Org-roam nodes.
(use-package textpod
:config
(setq textpod-id-property "ID"
textpod-id-prefix "textpod_"
textpod-token (cadr (auth-source-user-and-password "finita.myaddr.dev" "textpod"))
;;textpod-url "/notes"
textpod-url "http://localhost:3000/notes"
)
:bind (("C-c b x" . textpod-set-id)
("C-c b b" . textpod-org-heading-to-note)
("C-c b o" . textpod-open-current-note)))
Updates:
- <2026-04-23 Thu>: add keybindings
Advice to org-roam-dailies-goto-today
Currently, when calling org-roam-dailies-goto-today, {C-c n d} for me, Emacs switches to the daily note buffer, but moves point to (point-min). I want to advice to first check if the buffer with "YYYY-MM-DD.org" (for today) already exists, and if so, switch to this buffer. Related to Org-roam.
Here's an advice function to achieve that:
(defun my-org-roam-dailies-goto-today-advice (orig-fun &rest args)
"Switch to today's daily note buffer/window if it exists, otherwise create it.
Preserves point position if buffer already exists."
(let* ((today (format-time-string "%Y-%m-%d"))
(daily-buffer-name (concat today ".org"))
(existing-buffer (get-buffer daily-buffer-name))
(existing-window (and existing-buffer
(get-buffer-window existing-buffer t))))
(cond
;; Buffer is already displayed in a window - switch to that window
(existing-window
(select-window existing-window))
;; Buffer exists but not displayed - switch to it in current window
(existing-buffer
(switch-to-buffer existing-buffer))
;; Buffer doesn't exist - create it
(t
(apply orig-fun args)))))
(advice-add 'org-roam-dailies-goto-today
:around #'my-org-roam-dailies-goto-today-advice)
This advice:
- Checks for buffer by name (not file path)
- If the buffer is displayed in any window (even in another frame), selects that window
- If the buffer exists but isn't displayed, switches to it in the current window
- Otherwise calls the original function
To remove the advice later:
(advice-remove 'org-roam-dailies-goto-today
#'my-org-roam-dailies-goto-today-advice)
- Edit <2026-04-17 Fri>: check existing buffer using buffer name, not the buffer vising a file, that depends on org-roam-dailies-directory (I have two different, but one set of dailies).
- Edit <2026-04-21 Tue>: check if the daily buffer is already opened in existing window, if so - switch to it.
Improve public notes
Next round of improving Textpod on <2026-04-15 Wed>.
Deploy publicly –
Introduced --base-path flag, to serve notes under sub-path.
To start Textpod with sub-path, use:
textpod --token $TEXTPOD_TOKEN --base-path notes
Publish textpod.el to the repository
textpod.el is a companion Emacs package for quick publishing notes from Org Mode documents.
Install it like this:
(use-package textpod
:config
(setq textpod-token (cadr (auth-source-user-and-password "finita.myaddr.dev" "textpod")))
(setq textpod-url "/notes"))
Then these helper functions do the heavy-lifting:
- textpod-org-region-to-note: Convert the Org region between BEG and END to Markdown and send to Textpod.
- textpod-org-heading-to-note: Send the current heading’s subtree to Textpod.
- textpod-open-current-note: Open the current heading’s Textpod note in a browser.
When creating a note, textpod.el sets TEXTPOD_ID property that's equal
to node'id.
Change separator in notes.md to ^L
Default separator \n\n---\n\n is fragile as it requires exactly two
newlines before / after the note. I often messed up notes when
editing notes.md file manually. To fix that, I replaced it with ^L
character (insert in Emacs via C-q C-l) which usually represents new
pages.
Fix attachments links
Attachement links need to respect --base-path flag.
Handling attachments
My current workflow:
-
Assets are stored next to Notes under
assets/directory -
I can quickly search for them, then open in Emacs/qView:
find ../assets -name "*prefix-in-emacs*" ../assets/m-g-prefix-in-emacs.png ../assets/m-s-prefix-in-emacs.png -
I can reference an asset as an image:

-
An image is a link to an image file that does not have a description part (Images (The Org Manual)).
-
Textpod already support images. What happens when I drag-n-drop an image file to the textarea? Textpod copies them under attachments directory.
What I want:
- When creating a public note containing an image via API (using textpod.el), copy the asset to assets/ directory, mimicing the directory structure in the notes.
- I need another API endpoint -
POST /notes/assets/<name>, accepting binary as payload.
Okay, I implemented it, let's test.
textpod-org-heading-to-note
Prompt: Implement textpod-org-heading-to-note function, it should select current (top) heading - the one that has TEXTPOD_ID parameter, and call textpod-org-region-to-note on it.
The command {M-x textpod-org-heading-to-note} works!
Should the textpod id be just id?
Currently I use TEXTPOD_ID property. If using ID property, notes will
automatically become Org-roam nodes. Currently IDs are generated as
timestamps via (setq org-id-method 'ts) with nanoseconds precision.
Then it will come with seconds precision. I will need to a prefix, to
be able to quickly find them (e.g. with {M-x deadgrep}).
Wrap h3 into details on index page
To declutter the index page, h3 markdown headings (h2 Org headings)
are wrapped into <details> tag. The note page renders normally,
showing table of contents on the left sidebar.
Trying Textpod once again
On <2026-04-13 Mon> I installed Textpod on Mac Book Pro M1 using Cargo (Rust tooling).
Good:
- Separate from notes, and from blog.
- Managing ton of markdown files for small notes that I produce - huge overkill
- I want something like this, write in org-mode, press a button (call elisp function) and heading is sent to website.
- Open source, so I can fork and improve it for my needs.
- ☑ make a fork - ~/Developer/src/github.com/velppa/textpod
- Can make it prettier.
Open questions:
- ☑ Does it have an API? Like I would select a Org text, convert it
to Markdown and send to Textpod.
- use {M-x textpod-org-region-to-note} function
- ☑ How to increase font? Increased in the sources.
- ☑ Token access, for hosting on finita-la-comedia
- ☑ Internal Org Roam links are pasted as markdown files. So in theory, I could somehow expose them.
I ended up forking it to https://github.com/velppa/textpod and greatly improving it for my needs. You're reading notes served by Textpod v0.2.0. It is my project.
Notable changes:
- Read-only mode with token authentication: The app now supports an
optional
--tokenflag that enables authentication. When a token is set, users without the token see a read-only interface where they can only search notes. Access is granted via a?token=Xquery parameter (which sets a cookie) or a Bearer token in the Authorization header. The UI dynamically adjusts based on authentication status, hiding edit/delete controls and changing the editor placeholder for unauthenticated users. - Enhanced note management: Notes now have unique IDs based on
timestamps, enabling direct access via
/note/:idURLs.PUTmethod allows editing notes (upserting if not found). - Subpath support: to deploy on your domain under subpath, supply
--base-path notesflag. Then notes will be under/notessubpath. - The app watches the notes file for external changes and automatically reloads.
- The link archiving feature lets users prefix URLs with
+to both raw urls and Markdown-formatted. - Special handling of links to
.mdfiles: They appear when exporting from Org-roam. These are internal links and Textpod now turns them into search by filename, effectively returning all mentions of this concept. Poor man zettelkasten import. - UI improvement: larger fonts, footer, better code snippets for dark mode, tags on the heading.
- textpod.el: Companion Emacs package for quick sending notes to Textpod.
Later changes to setup will go into separate notes.