<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotemacs/init.el, branch main</title>
<subtitle>My Emacs configuration
</subtitle>
<id>https://git.cjennings.net/dotemacs/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/dotemacs/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/'/>
<updated>2026-06-05T10:28:58+00:00</updated>
<entry>
<title>feat(term): replace vterm with ghostel as the terminal engine</title>
<updated>2026-06-05T10:28:58+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-05T10:28:58+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=ebdf9e466b0e1f86e9b7d76650ac32408273e7a7'/>
<id>urn:sha1:ebdf9e466b0e1f86e9b7d76650ac32408273e7a7</id>
<content type='text'>
I swapped the terminal engine from vterm to ghostel (libghostty-vt) everywhere. term-config replaces vterm-config (the F12 terminal, the C-; x menu, tmux history capture), and ai-term replaces ai-vterm (the F9 Claude-agent launcher). ghostel renders the agent TUI without vterm's flicker under heavy streaming, and one engine now covers every terminal workflow.

Two behavior changes fall out of the swap. F9 launches in a terminal frame now: ghostel renders in TTY frames, so the old GUI-only guard is gone. Terminal windows no longer dim when unfocused: ghostel resolves its palette into the native module per-terminal, so there's no per-window color hook to dim through the way vterm had.

auto-dim drops its vterm color-advice path, the dashboard Terminal button launches ghostel, and the vterm and vterm-toggle packages are removed. The tmux pane-history and copy-mode machinery carried over unchanged. It keys on the pty tty, which ghostel exposes.
</content>
</entry>
<entry>
<title>feat(signal): add Signal client foundation on a signel fork</title>
<updated>2026-05-27T01:24:58+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-27T01:24:58+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=049325f438817c0b4f4a443f71b8821b0bfd357a'/>
<id>urn:sha1:049325f438817c0b4f4a443f71b8821b0bfd357a</id>
<content type='text'>
I'm building a Signal client in Emacs on signal-cli (linked as a secondary device) with a fork of the signel package as the front end. signel is on MELPA but effectively abandoned, and the behavior I want needs internal edits, so owning a fork beats advising a dead package. Full rationale and the rejected alternatives are in docs/design/signal-client.org.

This lands the signal-cli-independent foundation: contact-list parsing for a completing-read picker, and the predicate that suppresses a notification for the chat being actively viewed. Both are pure and unit-tested without a linked account. cj/signal--parse-contacts was corrected against a live account (signal-cli 0.14 puts givenName/familyName at the top level, not under profile), and verified across all 94 real contacts.

The use-package wiring loads the fork from ~/code/signel, sources the account from a gitignored signal-config.local.el (a phone number is an identifier, not a credential, and this keeps it off the mirror without a GPG prompt), and turns off auto-open so an incoming message can't steal a window. Verified live: signel-start spawns the jsonRpc process, loads the account, and receives over the channel.

The fork edits (notify routing, the upstream input-clobber bug) and the contact-picker command are still to come.
</content>
</entry>
<entry>
<title>chore(init): drop the disabled popper-config module</title>
<updated>2026-05-26T22:33:08+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-26T22:33:08+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=1cca84c509f9765ea601c93648850b641da19a31'/>
<id>urn:sha1:1cca84c509f9765ea601c93648850b641da19a31</id>
<content type='text'>
popper-config was use-package :disabled t, so use-package elided the whole form and none of it ran. It was a no-op sitting in the load graph, required only from init.el. I removed the module and its require. No behavior change. validate-modules passes and init still loads clean. The config stays in git history if popper is ever wanted again.
</content>
</entry>
<entry>
<title>refactor(user-constants): move filesystem creation out of module load</title>
<updated>2026-05-26T00:06:13+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-26T00:06:13+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=423ca0e8e502661343a8aa02d5c58c5029e40b03'/>
<id>urn:sha1:423ca0e8e502661343a8aa02d5c58c5029e40b03</id>
<content type='text'>
(require 'user-constants) created ~8 directories and ~10 org/calendar files at load time, via a top-level dolist for the calendar stubs and a top-level call to cj/initialize-user-directories-and-files. That meant any bare require — tests, byte-compile, batch tools — wrote to disk. It's why a stray sync/org/ tree kept appearing in the repo during test runs.

I removed both top-level forms and folded the gcal/pcal/dcal creation into the initializer. The path defconsts stay exactly as they were, so every consumer that just reads a path is unaffected. init.el now calls the initializer right after requiring the module, guarded by (unless noninteractive), so interactive and daemon startup create everything in the same order as before while a bare require stays side-effect-free.

Added tests/test-user-constants.el: loading the module creates nothing, and the initializer creates the backbone dirs and the configured files. Updated the module header — top-level side effects are now none and it's safe to load in tests.
</content>
</entry>
<entry>
<title>feat(auto-dim): dim non-selected windows via auto-dim-other-buffers</title>
<updated>2026-05-25T14:25:32+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-25T14:25:32+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=56da3d940b26a51102bce39b3b82dfbbc2b391fd'/>
<id>urn:sha1:56da3d940b26a51102bce39b3b82dfbbc2b391fd</id>
<content type='text'>
I added auto-dim-config, a module that loads my local auto-dim-other-buffers fork and dims windows that don't have focus so the selected window stands out. A non-selected window drops to a pure-black background with faded gray text. The dimmed faces live in the dupre theme (themes/dupre-faces.el) so they track theme switches, and the module remaps default, the font-lock faces, and org-block onto them so syntax-highlighted code fades too rather than staying lit. Fringe is left out because dimming it forces a full-frame refresh that flickers on this non-pgtk build.

dim-on-focus-out is nil, so tabbing to a browser or terminal on Hyprland doesn't dim the whole frame. vterm and agent windows don't dim either, because the terminal paints its own per-cell colors past the face remap. I'm keeping that, since the agent's output stays readable while I work in code on the other side.

The module loads after the theme, carries a load-graph header, joins the header-contract allowlist, and the inventory moves to 103 of 103 classified.
</content>
</entry>
<entry>
<title>docs(init): retire stale module comments and track follow-ups</title>
<updated>2026-05-24T21:13:08+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T21:13:08+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=09d51da39d01843011ab8e1206da5fe90de8d8f9'/>
<id>urn:sha1:09d51da39d01843011ab8e1206da5fe90de8d8f9</id>
<content type='text'>
Three init.el requires carried vague comments: latex-config "WIP need to fix", prog-shell "combine elsewhere", and a "Modules In Test" banner. I replaced them with descriptive comments and moved the real follow-up work into todo.org tasks: investigate the latex-config state, and decide whether prog-shell config folds into prog-general. I also marked the module-classification task DOING.
</content>
</entry>
<entry>
<title>feat(linear): re-enable linear-config and wire the reworked command surface</title>
<updated>2026-05-24T04:16:24+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T04:16:24+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=f92619569be0c5bd0ca6af2257b1b2f8884b4191'/>
<id>urn:sha1:f92619569be0c5bd0ca6af2257b1b2f8884b4191</id>
<content type='text'>
linear-emacs grew a lot of new commands in its rework: filtered lists, saved queries, Custom Views, open-in-browser, comments, delete, and set-assignee/state/priority/labels on the issue at point. The config still listed and bound only the original seven, and the init.el require was commented out while the package was in flux.

I re-enabled the require, expanded :commands to all 25 autoloaded commands, and rebuilt the C-; L keymap around them: lists and views up top, an o/r/D set for the issue at point, sync on s/S/u/U, and a C-; L e sub-prefix for editing the issue's fields. The lazy authinfo key-load advice and the data/linear.org path carry over unchanged.

I verified the dependency symbols still exist before wiring, but the live connection check (C-; L ?) is still yours to run.
</content>
</entry>
<entry>
<title>fix(linear): load API key for check-setup and pin org file to emacs home</title>
<updated>2026-05-23T18:05:33+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-23T18:05:33+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=b70781e8eeaa67cf2a1aa804c27b92f38fd52742'/>
<id>urn:sha1:b70781e8eeaa67cf2a1aa804c27b92f38fd52742</id>
<content type='text'>
linear-emacs-check-setup read linear-emacs-api-key directly and bailed to "API key is not set" before making any request, so the lazy :before advice on the GraphQL request never fired for it. A fresh session always reported the key missing even though it was in authinfo. I extracted cj/linear--install-key-advice and put the loader on check-setup as well as the request entry point, with a regression test.

I also pinned linear-emacs-org-file-path to data/linear.org inside emacs home, next to the calendar-sync output. Left to its default it falls back to org-directory/gtd/linear.org and silently created a stray ~/org tree on the first pull.

The init.el require is commented out for now while linear-emacs is reworked. The config will need rework once that lands.
</content>
</entry>
<entry>
<title>feat(linear): wire linear-emacs into the config for DeepSat</title>
<updated>2026-05-23T14:51:55+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-23T14:51:55+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=b7323a37a37a5948716060edb508a4c5791117b5'/>
<id>urn:sha1:b7323a37a37a5948716060edb508a4c5791117b5</id>
<content type='text'>
I added modules/linear-config.el to load the local linear-emacs checkout (the same :load-path + :ensure nil shape gptel and org-drill use) and point it at DeepSat's Linear workspace. The personal API key comes from authinfo.gpg, loaded lazily by a named :before advice on the request function, so there's no GPG prompt at startup. The default team is DeepSat's Software Engineering team, and the commands sit under a C-; L prefix.

Verified live against DeepSat: the connection authenticates, lists all five workspace teams, and pulls real issues (SE-*, DEE-*). Tests cover the key-loader (loads when unset, keeps an existing key, stays nil when absent) and the keymap wiring.
</content>
</entry>
<entry>
<title>feat(telega): add telega.el module with docker-backed TDLib</title>
<updated>2026-05-13T21:06:38+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-13T21:06:38+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=8fc423d90eff78176fa538f2cf838cb671592e99'/>
<id>urn:sha1:8fc423d90eff78176fa538f2cf838cb671592e99</id>
<content type='text'>
New `modules/telega-config.el` configures telega.el as an in-Emacs Telegram client. `telega-use-docker' is on so TDLib runs in a container instead of needing a system-level build -- pairs with a follow-up `scripts/setup-telega.sh' for fresh-clone installs. First-run auth (phone + verification code) is interactive inside `M-x telega' and isn't scripted here.

Launcher binding: `C-; G` (mnemonic: teleGram). `C-; t` and `C-; m t` were both taken (test-runner, music's "repeat track"), so the launcher landed on a free top-level letter.

Two tests cover the wiring: module loads, launcher is bound.
</content>
</entry>
</feed>
