<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotemacs/modules/user-constants.el, branch load-graph-classify-end</title>
<subtitle>My Emacs configuration
</subtitle>
<id>https://git.cjennings.net/dotemacs/atom?h=load-graph-classify-end</id>
<link rel='self' href='https://git.cjennings.net/dotemacs/atom?h=load-graph-classify-end'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/'/>
<updated>2026-05-24T21:13:01+00:00</updated>
<entry>
<title>docs(load-graph): seed module inventory and annotate foundation headers</title>
<updated>2026-05-24T21:13:01+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T21:13:01+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=2d83f8f55977dbf69f4f80e490ed0b96aeea4eee'/>
<id>urn:sha1:2d83f8f55977dbf69f4f80e490ed0b96aeea4eee</id>
<content type='text'>
I started the init.el load-graph classification with the foundation batch. I added docs/design/module-inventory.org as the living per-module inventory and annotated the seven foundation modules (system-lib, user-constants, host-environment, system-defaults, keyboard-compat, keybindings, config-utilities) with the load-graph header contract: layer, category, load shape, eager reason, top-level side effects, runtime requires, and direct-test-load safety.

I changed no load order, so init.el keeps its current eager order. The inventory records one hidden dependency for Phase 2: system-defaults uses host-environment and user-constants symbols at load while declaring them eval-when-compile, so the compiled module cannot load standalone.
</content>
</entry>
<entry>
<title>refactor(foundation): hygiene pass across early-init, user-constants, system-defaults, chrono-tools</title>
<updated>2026-05-16T07:35:38+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-16T07:35:38+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=1c5a2ebab7c721d795ed9331afdb305fd683e172'/>
<id>urn:sha1:1c5a2ebab7c721d795ed9331afdb305fd683e172</id>
<content type='text'>
Six small fixes the 2026-05-15 module-by-module re-review surfaced:

- Consolidate `user-home-dir` -- canonical defconst stays in
  early-init.el (package-archive bootstrap needs it before normal
  modules load); user-constants.el switches to a `defvar` with the
  identical `(getenv "HOME")` expression so the module still loads /
  byte-compiles standalone, but at runtime early-init's defconst
  wins.
- Drop the redundant `(autoload 'env-bsd-p ...)` line in
  system-defaults.el.  The `(eval-when-compile (require
  'host-environment))` already exposes the symbol to the byte
  compiler, and at runtime host-environment is loaded earlier in
  init.el.  Added a comment documenting the boundary.
- Convert `cj/debug-modules` and `cj/use-online-repos` from `defvar`
  to `defcustom`, with `:type`, `:group 'cj`, and a top-level
  `(defgroup cj ...)` so both show up in M-x customize.
- Name the package-archive priorities in early-init.el.  Nine new
  defconsts replace the magic numbers (200 / 125 / 120 / 115 / 100 /
  25 / 20 / 15 / 5) with one constant each, plus a header comment
  explaining the local-first ordering and the gnu &gt; nongnu &gt; melpa &gt;
  melpa-stable trust ranking within each tier.
- Delete the 19-line commented-out `use-package time` world-clock
  block in chrono-tools.el.  `time-zones` immediately above is the
  active replacement; git history preserves the old config if anyone
  needs it.
- Add coverage for `cj/tmr-select-sound-file`.  Collapsed the
  prefix-arg branch into a delegation to
  `cj/tmr-reset-sound-to-default` (single reset source) and
  extracted `cj/tmr--available-sound-files` as a pure helper that
  tests directly.  9 ERT tests across Normal / Boundary / Error
  cover the available-sounds helper, the reset path, the prefix-arg
  delegation (no prompt), the normal selection path, and the
  empty-dir / missing-dir / cancel boundaries.
</content>
</entry>
<entry>
<title>feat(transcription): extend dired T to transcribe videos via ffmpeg, with tests</title>
<updated>2026-05-14T19:09:16+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-14T19:09:16+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=4a7529393ebbbc225939f21be783ec624d0a8168'/>
<id>urn:sha1:4a7529393ebbbc225939f21be783ec624d0a8168</id>
<content type='text'>
Pressing `T' in dired/dirvish on an audio file already transcribed
it; on a video file it bounced with "Not an audio file".  Real
recordings ship as .mp4 / .mkv at least as often as raw .m4a, so
the one-key flow ended at the wrong place.

Pipeline now:
- audio path -&gt; direct into `cj/--start-transcription-process'
  (unchanged).
- video path -&gt; async ffmpeg extracts the audio track to a temp
  .mp3 under `temporary-file-directory' (libmp3lame, VBR q:a 4,
  ~165kbps -- right size for speech, accepted by every backend),
  then transcribes that file with the temp marked for cleanup
  after the transcription sentinel fires.

Surface changes:
- `cj/video-file-extensions' added to user-constants.el (mp4, mkv,
  mov, webm, avi, m4v, wmv, flv, mpg, mpeg, 3gp, ogv).
- New predicates `cj/--video-file-p' / `cj/--media-file-p'.
- New `cj/--extract-audio-from-video' (async ffmpeg with success
  callback; surfaces `cj/--notify' on failure; user-errors if
  ffmpeg isn't on PATH).
- `cj/--start-transcription-process' gains optional `cleanup-file'.
  Sentinel deletes it after the existing logic runs.  Backwards
  compatible -- the audio flow doesn't pass it.
- `cj/transcribe-audio' renamed to `cj/transcribe-media' (dispatcher
  on audio vs video).  `cj/transcribe-audio-at-point' renamed to
  `cj/transcribe-media-at-point'.  Both old names kept as
  `defalias' so M-x history and any external references still work.
- `T' in dired-mode-map + dirvish-mode-map points at
  `cj/transcribe-media-at-point'.
- Module commentary USAGE block updated.

15 new ERT tests in `tests/test-transcription-video.el' cover the
predicates (happy/boundary/error), ffmpeg invocation (correct args
+ missing-ffmpeg path), the dispatcher (audio direct, video via
extraction, non-media rejected), the aliases, and the T binding.
One existing test in `test-transcription-status-and-commands.el'
updated to stub the new delegate name.

Verified locally that ffmpeg is on PATH with libmp3lame, and that
the exact arg list my code uses produces a valid MP3 from a
synthetic test video.
</content>
</entry>
<entry>
<title>fix(user-constants): create calendar data files on first launch</title>
<updated>2026-02-21T21:17:16+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-02-21T21:17:16+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=39e0b989ad1fd43c4632d267754eb3621dc3e278'/>
<id>urn:sha1:39e0b989ad1fd43c4632d267754eb3621dc3e278</id>
<content type='text'>
org-agenda-list prompts interactively for missing files, which hangs
the async subprocess chime uses to fetch events. Create empty
placeholders at init so calendar-sync can populate them on first sync.
</content>
</entry>
<entry>
<title>chore(yasnippet): move snippets into emacs.d for source control</title>
<updated>2026-02-16T11:14:28+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-02-16T11:14:28+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=bd3a6511c119464803f8e0bb142519ece2114327'/>
<id>urn:sha1:bd3a6511c119464803f8e0bb142519ece2114327</id>
<content type='text'>
Relocate snippets-dir from ~/sync/org/snippets/ to ~/.emacs.d/snippets/
and restore 28 snippet files from backup.
</content>
</entry>
<entry>
<title>feat(hugo): extract hugo-config module with C-; h keybindings</title>
<updated>2026-02-14T06:06:45+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-02-14T06:06:45+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=cb44e9cae2284585cf77f9cf6927947c066db45d'/>
<id>urn:sha1:cb44e9cae2284585cf77f9cf6927947c066db45d</id>
<content type='text'>
Standalone module for ox-hugo blog workflow. One-file-per-post
structure with keybindings for new post, export, open dir (dirvish
and system file manager), and toggle draft.
</content>
</entry>
<entry>
<title>feat(calendar-sync): add RECURRENCE-ID exception handling for recurring events</title>
<updated>2026-02-03T13:39:50+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-02-03T13:39:50+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=d0f928114f096af39c8231b7c4a9c9b05fc45f08'/>
<id>urn:sha1:d0f928114f096af39c8231b7c4a9c9b05fc45f08</id>
<content type='text'>
Handle rescheduled instances of recurring calendar events by processing
RECURRENCE-ID properties from ICS files. When someone reschedules a single
instance of a recurring meeting in Google Calendar, the calendar-sync module
now shows the rescheduled time instead of the original RRULE time.

New functions:
- calendar-sync--get-recurrence-id: Extract RECURRENCE-ID from event
- calendar-sync--get-recurrence-id-line: Get full line with TZID params
- calendar-sync--parse-recurrence-id: Parse into (year month day hour minute)
- calendar-sync--collect-recurrence-exceptions: Collect all exceptions by UID
- calendar-sync--occurrence-matches-exception-p: Match occurrences to exceptions
- calendar-sync--apply-single-exception: Apply exception data to occurrence
- calendar-sync--apply-recurrence-exceptions: Apply all exceptions to occurrences

Also adds DeepSat calendar configuration (dcal-file) to user-constants,
init.el, and org-agenda-config.

48 unit and integration tests added covering normal, boundary, and error cases.
</content>
</entry>
<entry>
<title>feat(calendar-sync): multi-calendar support with property tests</title>
<updated>2025-12-02T13:55:21+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2025-12-02T13:55:21+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=703bc366cf8439a398362d98bef513ef98942fd4'/>
<id>urn:sha1:703bc366cf8439a398362d98bef513ef98942fd4</id>
<content type='text'>
Added multi-URL calendar sync supporting Google and Proton calendars.
Each calendar syncs to separate file with per-calendar state tracking.
Added 13 property-based tests for RRULE expansion. Total: 150 tests passing.
</content>
</entry>
<entry>
<title>feat(calendar-sync): Add automatic timezone detection and chronological sorting</title>
<updated>2025-11-17T00:09:17+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2025-11-17T00:09:17+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=d842e08de5600c90c2f9de93ca81aea32be9775a'/>
<id>urn:sha1:d842e08de5600c90c2f9de93ca81aea32be9775a</id>
<content type='text'>
Implemented calendar-sync.el as a complete replacement for org-gcal, featuring:

**Core Functionality:**
- One-way sync from Google Calendar to Org (via .ics URL)
- UTC to local timezone conversion for all event timestamps
- Chronological event sorting (past → present → future)
- Non-blocking sync using curl (works reliably in daemon mode)

**Automatic Timezone Detection:**
- Detects timezone changes when traveling between timezones
- Tracks timezone offset in seconds (-21600 for CST, -28800 for PST, etc.)
- Triggers automatic re-sync when timezone changes detected
- Shows informative messages: "Timezone change detected (UTC-6 → UTC-8)"

**State Persistence:**
- Saves sync state to ~/.emacs.d/data/calendar-sync-state.el
- Persists timezone and last sync time across Emacs sessions
- Enables detection even after closing Emacs before traveling

**User Features:**
- Interactive commands: calendar-sync-now, calendar-sync-start/stop
- Keybindings: C-; g s (sync), C-; g a (start auto-sync), C-; g x (stop)
- Optional auto-sync every 15 minutes (disabled by default)
- Clear status messages for all operations

**Code Quality:**
- Comprehensive test coverage: 51 ERT tests (100% passing)
- Refactored UTC conversion into separate function
- Clean separation of concerns (parsing, conversion, formatting, sorting)
- Well-documented with timezone behavior guide and changelog

**Migration:**
- Removed org-gcal-config.el (archived in modules/archived/)
- Updated init.el to use calendar-sync
- Moved gcal.org to .emacs.d/data/ for machine-independent syncing
- Removed org-gcal appointment capture template

Files modified: modules/calendar-sync.el:442, tests/test-calendar-sync.el:577
Files created: data/calendar-sync-state.el, tests/testutil-calendar-sync.el
Documentation: docs/calendar-sync-timezones.md, docs/calendar-sync-changelog.md
</content>
</entry>
<entry>
<title>feat(ui): Add buffer modification state to color indicators</title>
<updated>2025-11-14T07:53:30+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2025-11-14T07:53:30+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=ef32e0443aab69e3ccd31aa2449142409aaa4440'/>
<id>urn:sha1:ef32e0443aab69e3ccd31aa2449142409aaa4440</id>
<content type='text'>
Change modeline filename and cursor colors to indicate buffer
modification status, not just read-only/overwrite state.

Color scheme changes:
- White (#ffffff): Unmodified writeable buffer
- Green (#64aa0f): Modified writeable buffer (unsaved changes)
- Red (#f06a3f): Read-only buffer
- Gold (#c48702): Overwrite mode active

Previously: All writeable buffers were green regardless of modification
Now: White when clean, green when dirty (better visual feedback)

Implementation:
- Updated cj/buffer-status-colors in user-constants.el:
  - Changed 'normal' → 'unmodified' (white)
  - Added new 'modified' state (green)
- Updated state detection in modeline-config.el:
  - Now checks (buffer-modified-p) before defaulting to unmodified
- Updated cursor color logic in ui-config.el:
  - Same state detection as modeline for consistency
  - Added after-change-functions hook for real-time updates
  - Added after-save-hook to update on save

Priority order (highest to lowest):
1. Read-only (red) - takes precedence over everything
2. Overwrite mode (gold) - takes precedence over modified state
3. Modified (green) - buffer has unsaved changes
4. Unmodified (white) - default for clean writeable buffers

Tests:
- 18 comprehensive tests in test-ui-buffer-status-colors.el
- Tests state detection logic and priority order
- Tests color constant definitions and mappings
- Tests integration with cursor and modeline
- All tests passing
</content>
</entry>
</feed>
