Auxillary and Optional Applications
Table of Contents
A literate programming file for helper apps in Emacs.
Introduction
The following applications are not needed. I alternate between trying to stay in Emacs taking advantage of the consistent interface, and using a stand-alone app on my Workday computer.
Federation
Mastodon
Glad to see the 2FA feature is working on the mastodon.el project, and even more glad to see the great birdland diaspora arrive in the land of the toots.
(use-package mastodon :straight (:host codeberg :repo "martianh/mastodon.el") :init (setq mastodon-instance-url "https://pdx.social" mastodon-active-user "howard"))
I would like a dedicate perspective to Mastodon, and I would like a leader key sequence that expands will all options, once Mastodon is running in its own perspective:
(use-package mastodon :config (ha-leader "<tab> M" `("mastodon" . ,(lambda () (interactive) (ha-tab-bar-new "mastodon" #'mastodon)))) (defun ha-mastodon-scroll-or-more () "Scroll a window, and at the end, get more entries in timeline." (interactive) (condition-case nil (scroll-up-command) (end-of-buffer (mastodon-tl--more)))) (use-package major-mode-hydra :config (major-mode-hydra-define mastodon-mode (:quit-key "q") ("Timelines" (("u" mastodon-tl-update "update") ("F" mastodon-tl-get-federated-timeline "Federated") ("H" mastodon-tl-get-home-timeline "Home") ("L" mastodon-tl-get-local-timeline "Local") ("T" mastodon-tl-get-tag-timeline "Hashtag")) "Specials" (("M" mastodon-notifications--get-mentions "Mentions") ("N" mastodon-notifications-get "Notifications") ("A" mastodon-tl-followed-tags-timeline "All Tags") ("S" mastodon-profile-view-bookmarks "Saved bookmarks") ("O" mastodon-profile-my-profile "My Profile")) "Post" (("c" mastodon-toot "Compose toot") ("e" mastodon-toot-edit-toot-at-point "Edit toot") ("t" mastodon-tl-thread "Read thread") ("r" mastodon-toot-reply "Reply") ("m" mastodon-tl-dm-user "Direct Msg") ("d" mastodon-toot-delete-toot "Delete")) "Toot" (("f" mastodon-toot-toggle-favourite "Favorite") ("b" mastodon-toot-toggle-boost "Boost") ("s" mastodon-toot-toggle-bookmark "Save") ("y" mastodon-toot-copy-toot-url "Copy URL") ("Y" mastodon-toot-copy-toot-text "Copy text")) "Navigation" (("n" mastodon-tl-next-tab-item "next" :color pink) ("p" mastodon-tl-previous-tab-item "previous" :color pink) ("," ha-mastodon-scroll-or-more "...more" :color pink))))))
Let’s turn on non-fixed-width fonts to make everything easier to read:
(use-package mastodon :hook (mastodon-mode . mixed-pitch-mode))
#+end_src
Matrix/Element
Yet another encrypted chat/VoIP client-server, but unlike Signal and Telegram, is act ually open source. In other words, a project for nerds. We’ll be using Alphapapa’s latest ement project.
(use-package ement :straight (:host github :repo "alphapapa/ement.el") :after major-mode-hydra :config (major-mode-hydra-define ement-room-mode (:quit-key "q") ("Send" (("c" ement-room-compose-message "Compose Message") ("d" ement-send-direct-message "Direct Message") ("S" ement-room-dispatch-reply-to-message "Message Reply") ("s" ement-room-send-message "Send (or <Return>)")) "Other" (("r" ement-room-send-reaction "React") ("e" ement-room-send-emote "Emote") ("R" ement-view-room "Jump to Room")))) (defun ha-ement-connect () (interactive) (let* ((auth-results (auth-source-search :host "matrix")) (auth-first (first auth-results)) (username (plist-get auth-first :user)) (password (funcall (plist-get auth-first :secret)))) (ement-connect :user-id username :password password :uri-prefix "https://matrix.org")) (ha-leader "a x S" '("send" . ement-send-direct-message) "a x s" '("send" . ement-room-send-message) "a x c" '("compose" . ement-room-compose-message) "a x r" '("room" . ement-view-room))) (ha-leader "a x" '(:ignore t :which-key "matrix") "a x x" '("connect" . ha-ement-connect) "a x s" '("send" . ement-send-direct-message)))
Perhaps we can make it more attractive:
(use-package ement :hook (ement-room-mode . ha-textual-litagures) :custom-face (ement-room-self-message ((t (:inherit variable-pitch :height 1.0)))) (ement-room-message-text ((t (:inherit variable-pitch :height 1.1 :foreground "#f08c60")))) :init (setq ement-save-sessions t ement-room-avatars nil ement-room-timestamp-format "" ement-room-send-message-filter 'ement-room-send-org-filter))
Other
The venerable twittering-mode allows me to follow all the twits.
(use-package twittering-mode :init (setq twittering-use-master-password t epa-pinentry-mode 'loopback) :config (defalias 'epa--decode-coding-string 'decode-coding-string) (ha-leader "a t" '("twitter" . twit)))
And we are no longer using this package.
Telega
I’m thinking the Telega package would be better than Bitlbee for Telegram communication.
(use-package telega :config (setq telega-chat-show-avatars nil telega-use-tracking-for nil ; '(any pin unread) ;; Use org formatting for normal messages. ;; Want to send the org markup without formatting? ;; The `nil' sets that with `C-u' then Return: telega-chat-input-markups '("org" nil) telega-emoji-use-images t ; telega-completing-read-function #'ivy-completing-read telega-msg-rainbow-title nil) (when (fboundp 'evil-insert-state) (add-hook 'telega-chat-mode-hook 'evil-insert-state)) (ha-leader "<tab> T" `("telega" . ,(lambda () (interactive) (ha-tab-bar-new "telega" #'telega)))))
For some reason, you need rainbow-identifiers to work, oh, I guess the docs state this.
In the Telega chat’s, let’s turn on non-fixed-width fonts:
(use-package telega :hook (telega-chat-mode . mixed-pitch-mode))
RPG DM
Been working on my RPG DM project for getting Emacs helping as a Dungeon Master’s Assistant. The idea is to be able to roll dice and whatnot. What I find most useful is the random tables.
(when (f-directory? "~/src/emacs-rpgdm") (use-package rpgdm :straight (:local-repo "~/src/emacs-rpgdm") :commands (rpgdm-mode rpgdm-tables-load) :init (setq rpgdm-base (expand-file-name "~/src/emacs-rpgdm")) :config (ha-leader "t D" '("rpg dm" . rpgdm-mode))))
Working on my new replacement of my DM code:
(when (f-directory? "~/src/emacs-rpgtk") (use-package rpgtk :straight (:local-repo "~/src/emacs-rpgtk") :after hydra ;; :commands (rpgtk-mode rpgtk-tables-load rpgtk-dice rpgtk-message) :custom (rpgtk-tables-directory (expand-file-name "~/src/emacs-rpgtk/tables")) :config (ha-leader "t D" '("rpg dm" . rpgtk-mode) "a d" '("rpg dm" . hydra-rpgtk/body))))
And my new Ironsworn project expands on it, giving me both the Oracles and the Moves. With an Org file, I can easily play Solo:
(when (f-directory? "~/src/emacs-ironsworn") (use-package rpgdm-ironsworn :after rpgdm :straight (:local-repo "~/src/emacs-ironsworn") :init (setq rpgdm-ironsworn-project (expand-file-name "~/src/emacs-ironsworn") ;; Ignore org links that call my RPG functions: org-link-elisp-skip-confirm-regexp (rx string-start (optional "(") "rpgdm-" (or "tables-" "ironsworn-") (one-or-more any)))))
The project is interesting, and I should record a tutorial how to use it.
Playing Music
After building a NAS out of a Raspberry Pi to hold my ripped CD collection, let’s control my playlists from Emacs.
On my Linux systems, I need to installed the requisite apps:
sudo apt install cifs-utils -y
And create the mount points:
And update our /etc/fstab file with:
//myserver/myshare /mnt/smbshare cifs credentials=/root/.smbcredentials,iocharset=utf8,file_mode=0777,dir_mode=0777 0 0
A more rich experience than the empv project is ready-player. First install mpv:
brew install mpv
And make sure we can play it:
mpv "/Volumes/music/Steely Dan/Aja/01 Black Cow.mp3"
And stream my favorite local Jazz radio station:
mpv --terminal --force-window=no --no-resume-playback \ 'https://ais-sa3.cdnstream1.com/2442_128.aac/playlist.m3u8' &
And then the configuration:
(use-package ready-player :straight (:type git :host github :repo "xenodium/ready-player") :config (when (eq system-type 'darwin) (set-fontset-font t nil "SF Pro Display" nil 'append) (ready-player-macos-use-sf-symbols)) (setq ready-player-my-media-collection-location "/Volumes/music") (defun ha-ready-player-load-playlist () "Open a playlist in the correct location." (interactive) (let ((default-directory (expand-file-name "Playlists/" ready-player-my-media-collection-location))) (setq ready-player-shuffle t) (call-interactively 'ready-player-load-m3u-playlist))) (transient-append-suffix 'ready-player-menu "?" '("P" "Load Playlist" ha-ready-player-load-playlist)) (defun ha-ready-player-load-directory () "Open a directory in the correct location." (interactive) (let ((default-directory (file-name-as-directory ready-player-my-media-collection-location))) (setq ready-player-shuffle nil) (call-interactively 'ready-player-load-directory))) (transient-append-suffix 'ready-player-menu "d" '("A" "Play Album" ha-ready-player-load-directory)) (ha-leader "a r" '("ready player" . ready-player-menu)) (ready-player-mode +1))
Since ready-player doesn’t support adjusting the volume, I can, at least, set the default volume low enough that my speaker adjustment affects it:
volume=70
Agentic Software
Not sure how much AI I want in my life. But I’m curious. Let’s start using Xenodium’s agent-shell project.
First, install all the NPM packages for agents you want to try out, including:
npm install -g @google/gemini-cli # and/or ... npm install -g copilot # and/or ... npm install -g pi-acp
Install the dependencies. Not sure why these are not getting picked up from Melpa, but here we are:
(use-package acp :straight (:type git :host github :repo "xenodium/acp.el")) (use-package shell-maker :straight (:type git :host github :repo "xenodium/shell-maker"))
I can access Google’s Gemini, but it requires Node version 20 or higher. On my Linux system, the default apt package is v18, so we use npm to install a higher version. We just need to update the PATH accordingly:
(use-package agent-shell :straight (:type git :host github :repo "xenodium/agent-shell") :config (add-to-list 'exec-path (expand-file-name "~/.nvm/versions/node/v24.12.0/bin")) (setenv "PATH" (concat (expand-file-name "~/.nvm/versions/node/v24.12.0/bin") ":" (getenv "PATH"))) (ha-leader "a i" '("Start AI" . agent-shell)))
Claude
So good.
(use-package agent-shell :ensure-system-package ;; Add agent installation configs here ((claude . "sudo apt install claude-code") (claude-agent-acp . "npm install -g @zed-industries/claude-agent-acp")) :config ;; Load from a single .env file (setq agent-shell-anthropic-claude-environment (agent-shell-make-environment-variables :load-env "~/.zshenv-work") agent-shell-anthropic-authentication (agent-shell-anthropic-make-authentication :login t))
Gemini
(use-package agent-shell :ensure-system-package ((gemini-agent-acp . "npm install -g @google/gemini-cli")) :config (setq agent-shell-google-authentication (agent-shell-google-make-authentication :login t) agent-shell-google-gemini-command '("gemini" "--experimental-acp")) )