<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotemacs/assets, 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-07-06T22:14:58+00:00</updated>
<entry>
<title>feat(music): cover-art fetch and cache for the player (fancy UI phase 2)</title>
<updated>2026-07-06T22:14:58+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-06T22:14:58+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=e74bb871ac0aa7ab6e63cd39759b8850f1181ffe'/>
<id>urn:sha1:e74bb871ac0aa7ab6e63cd39759b8850f1181ffe</id>
<content type='text'>
Phase 2 gives each track a local cover-image path so phase 3's GUI has art to draw. A radio station uses its logo, a local file a sibling cover image, and anything without either falls back to a shipped vinyl placeholder.

I consolidated the .m3u parse into one richer cj/music--m3u-entries that reads :name, :uuid, and :favicon per station. Phase 1's cj/music--m3u-labels is now a thin projection of it, so names and art share a single cached disk read. New stations capture their favicon into a #RADIOBROWSERFAVICON line at creation, so most need no lookup later. A legacy station with only a UUID resolves its favicon through a byuuid call.

The render path never touches the network. cj/music-art--for-track reads only the cache and returns the placeholder until art exists. cj/music-art--ensure does the blocking fetch off that path. A fetched response is validated as an actual image before it's cached, so an HTML error page or an empty body becomes the placeholder, not a poisoned cache entry. Only http and https URLs are fetched, so an external favicon field can't reach a file:// resource. Art lands under data/music-art/, keyed by UUID or a file hash. cj/music-clear-art-cache empties it, with no automatic expiry.

The pure helpers carry the tests: the parser, the cache key, the favicon URL, and the image validation, each with Normal, Boundary, and Error cases. The fetch, the byuuid lookup, and the placeholder fallback are verified live against a real station. Local files use a sibling cover image for now. Embedded-tag extraction is deferred. The full suite is green.
</content>
</entry>
<entry>
<title>fix(markdown): vendor strapdown.js instead of a plain-HTTP CDN</title>
<updated>2026-06-30T21:25:50+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-30T21:25:50+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=f53aaf2d6278a219dd19e4f40f2a4c3696ec838a'/>
<id>urn:sha1:f53aaf2d6278a219dd19e4f40f2a4c3696ec838a</id>
<content type='text'>
The live markdown preview pulled strapdown.js from http://ndossougbe.github.io over plain HTTP. That broke the preview with no network, loaded third-party JS over an unencrypted connection (mixed content, MITM), and trusted an unmaintained github.io page against the localhost preview.

I vendored the self-contained bundle (jQuery, marked, bootstrap themes) into assets/strapdown.js and embed it inline. The whole preview now serves from localhost and works offline. cj/markdown-html reads the file once and caches it.
</content>
</entry>
<entry>
<title>chore(emacs): sync abbrev_defs</title>
<updated>2026-06-08T16:31:02+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-08T16:31:02+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=45681f09141f3eca39046f759a51b8bf3270aaf6'/>
<id>urn:sha1:45681f09141f3eca39046f759a51b8bf3270aaf6</id>
<content type='text'>
</content>
</entry>
<entry>
<title>chore(todo): file dupre diff-face legibility bug from pearl handoff</title>
<updated>2026-06-07T13:16:50+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-07T13:16:50+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=636c701efaddb38edba3922ee465423c07654583'/>
<id>urn:sha1:636c701efaddb38edba3922ee465423c07654583</id>
<content type='text'>
dupre's diff-refine-changed is bright gold under near-white text (WCAG ~1.35), unreadable as a plain background. Filed with the side-by-side comparison render from the pearl session that surfaced it.
</content>
</entry>
<entry>
<title>chore: drop the stray butterfly PNG backup and ignore .bak files</title>
<updated>2026-05-26T23:07:13+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-26T23:07:13+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=cbc6100ae09fa177a8980b956c8709f595995f60'/>
<id>urn:sha1:cbc6100ae09fa177a8980b956c8709f595995f60</id>
<content type='text'>
assets/M-x_butterfly.png.bak was a tracked duplicate of the banner PNG, an accidental backup that got committed. I removed it and added *.bak to .gitignore so editor and image backups stay out of the tree.
</content>
</entry>
<entry>
<title>feat(dashboard): render the butterfly banner with a transparency mask</title>
<updated>2026-05-25T18:15:21+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-25T18:15:21+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=32901098e43f2aadb69ca6e2d99465402da06247'/>
<id>urn:sha1:32901098e43f2aadb69ca6e2d99465402da06247</id>
<content type='text'>
The dashboard banner showed the butterfly PNG without its transparency, because the image descriptor was built before the mask props were set. I moved the banner setup (dashboard-startup-banner, dashboard-banner-logo-title) ahead of dashboard-setup-startup-hook and added dashboard-image-extra-props with :mask heuristic, so the mask is in place when the startup hook builds the buffer. If a *dashboard* buffer already exists I refresh it, so the change shows without a restart.

I also re-encoded the PNG smaller and kept the previous encoding as M-x_butterfly.png.bak.
</content>
</entry>
<entry>
<title>feat(agenda): add project-filtered agenda view on C-f8</title>
<updated>2026-04-10T02:04:11+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-10T02:04:11+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=76969cee800a79bc932eef936a3a7234bd53153b'/>
<id>urn:sha1:76969cee800a79bc932eef936a3a7234bd53153b</id>
<content type='text'>
New cj/todo-list-single-project prompts for a project, then shows the
daily agenda scoped to that project's todo.org plus calendars and inbox.
Moved cj/todo-list-all-agenda-files from C-f8 to s-f8.
</content>
</entry>
<entry>
<title>feat(vterm): pass F8, F9, F10 through to Emacs</title>
<updated>2026-04-07T18:04:48+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-07T18:04:48+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=fdf5bff8e75570a4ac924011dadf0702fbb40a9f'/>
<id>urn:sha1:fdf5bff8e75570a4ac924011dadf0702fbb40a9f</id>
<content type='text'>
Add nil bindings for F8 (agenda), F9 (gptel), and F10 (music) in
vterm-mode-map so these global keybindings aren't swallowed by vterm.
Also disable wttrin-debug and update abbreviations.
</content>
</entry>
<entry>
<title>fix(slack): add error visibility to notification handler</title>
<updated>2026-03-14T17:55:48+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-03-14T17:55:48+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=fe5fe07681997da3e1c3e050cdc91216572d4bae'/>
<id>urn:sha1:fe5fe07681997da3e1c3e050cdc91216572d4bae</id>
<content type='text'>
Websocket library silently swallows callback errors via condition-case.
Wrap cj/slack-notify in condition-case to surface errors in *Messages*.
Add cj/slack-test-notify command for pipeline verification.
</content>
</entry>
<entry>
<title>fix(calendar-sync): handle variable-length date lists in RRULE UNTIL</title>
<updated>2026-03-10T04:40:26+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-03-10T04:40:26+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=813f1390de1846694f235c257dc06831d0609dac'/>
<id>urn:sha1:813f1390de1846694f235c257dc06831d0609dac</id>
<content type='text'>
date-to-time used (reverse date) which broke when RRULE UNTIL values
were parsed as 5-element lists (year month day hour minute) from UTC
timestamps. This caused recurring events with UTC UNTIL dates to
expand to 0 occurrences, producing stale calendar entries.
</content>
</entry>
</feed>
