<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotemacs/modules/modeline-config.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:28:07+00:00</updated>
<entry>
<title>docs(load-graph): classify UI and core-UX modules</title>
<updated>2026-05-24T21:28:07+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T21:28:07+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=e1789025131b048049777542a91fb7eb55195ce5'/>
<id>urn:sha1:e1789025131b048049777542a91fb7eb55195ce5</id>
<content type='text'>
Fourth classification batch: the modules that shape the first interactive frame — ui-config, ui-theme, ui-navigation, font-config, selection-framework, modeline-config, mousetrap-mode, popper-config, dashboard-config, nerd-icons-config. I annotated each header, added a Batch 4 table to the inventory, and extended the validation allowlist. 33 of 102 modules are now classified.

These mostly stay eager: each has a real first-frame reason (theme, font, modeline, completion stack, landing page). No new hidden dependencies. popper-config carries the spec's open question about its enabled/disabled state, noted for the deferral phase.
</content>
</entry>
<entry>
<title>fix(modeline): key VC cache on resolved truename for symlink moves</title>
<updated>2026-05-24T12:22:56+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T12:22:56+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=9135298c45f2f03ab92903edb242c8f5f94396d5'/>
<id>urn:sha1:9135298c45f2f03ab92903edb242c8f5f94396d5</id>
<content type='text'>
The VC modeline cache keyed on (list file cj/modeline-vc-show-remote). If file was a symlink whose target moved to a different VC tree (shared drives, CI workspaces), the key was unchanged and the cache kept serving the old branch/state.

Added the resolved file-truename to the key, so a symlink re-pointed at a new target produces a different key and the cache refreshes. The extra file-truename is one stat per modeline refresh, cheap next to the VC calls the cache exists to avoid. Tests cover truename inclusion, key stability for an unchanged file, and a symlink whose target moves.
</content>
</entry>
<entry>
<title>refactor(ui): four UI/navigation hygiene fixes from module-by-module re-review</title>
<updated>2026-05-16T07:56:25+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-16T07:56:25+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=d618bb4620d5d651027e772b8ccc490e1bab6d80'/>
<id>urn:sha1:d618bb4620d5d651027e772b8ccc490e1bab6d80</id>
<content type='text'>
- popper-config.el: move `(popper-mode +1)` and `(popper-echo-mode
  +1)` from the use-package `:init` block into `:config`.
  `:disabled t' on use-package skips `:config' but still runs
  `:init', so the previous shape enabled popper-mode on every load,
  including batch / test runs, despite the disabled marker.

- modeline-config.el: make `cj/modeline-vc-fetch' fall back when
  the internal `vc-git--symbolic-ref' is missing.  `require' uses
  `nil 'noerror', the call sits inside an `fboundp' guard, and
  `ignore-errors' wraps the call itself so an Emacs version that
  renames or removes the accessor leaves `branch' at
  `vc-working-revision''s output instead of crashing the modeline.

- ui-config.el: guard the cursor-color `post-command-hook' behind
  `(display-graphic-p)' both at install time and inside the
  function body.  Batch / TTY runs short-circuit cleanly with no
  per-command overhead.  A `server-after-make-frame-hook' catches
  the daemon case where the first GUI frame is created after
  ui-config loads and installs the hook lazily.  Updates
  test-ui-config--buffer-cursor-state and
  test-ui-cursor-color-integration to stub `display-graphic-p' so
  the work body still runs under batch.

- nerd-icons-config.el: drop `:demand t' (`:defer t' now), keeping
  the `:config' advice install as the natural lazy-on-load path.
  Add a `with-eval-after-load 'nerd-icons' block as a safety net for
  the already-loaded case on re-eval; the block uses `advice-member-p'
  so the advice never stacks.
</content>
</entry>
<entry>
<title>feat(modeline): surface flycheck status in the custom modeline</title>
<updated>2026-05-16T07:12:56+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-16T07:12:56+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=adca2736e2e3ef1eef1aaf62542aa2189a560463'/>
<id>urn:sha1:adca2736e2e3ef1eef1aaf62542aa2189a560463</id>
<content type='text'>
The custom modeline builds `mode-line-format` from explicit segments
and skips `minor-mode-alist`, so flycheck's lighter never appears.
That hid error and warning counts even in buffers where flycheck was
auto-enabling (every emacs-lisp and sh buffer).

The fix is Option 4 from the design doc: customize the flycheck
modeline variables, then add a single guarded `(:eval ...)` form to
`mode-line-format`.  Five new lines total, two-file change.

`modules/flycheck-config.el` :custom block gets:
  (flycheck-mode-line-prefix "🐛")
  (flycheck-mode-success-indicator " ✓")
`flycheck-mode-line-color` stays default-t so error / warning counts
pick up their faces automatically.

`modules/modeline-config.el` `mode-line-format` gets an `(:eval ...)`
between the recording indicator and `cj/modeline-vc-branch`:

  (:eval (when (and (mode-line-window-selected-p)
                    (bound-and-true-p flycheck-mode))
           (flycheck-mode-line-status-text)))

The `mode-line-window-selected-p` guard mirrors `cj/modeline-vc-branch`
and `cj/modeline-misc-info` -- segments hide in inactive windows.
The `bound-and-true-p flycheck-mode` guard keeps the form silent in
buffers where flycheck hasn't loaded or isn't enabled, which is
safer than referencing `flycheck-mode` directly.

The `(:eval ...)` is inline rather than a named `defvar-local`, so no
addition to the risky-local-variable list is needed.

`tests/test-modeline-config-flycheck-segment.el` -- 3 smoke tests
asserting the segment is present and both guards are in place.  All
existing tests stay green.

Manual verification (per the design doc) is the user's call -- the
emoji prefix and the colored count behavior need a running GUI Emacs
to observe.
</content>
</entry>
<entry>
<title>perf: cache modeline VC data per buffer</title>
<updated>2026-05-04T01:24:14+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-04T01:24:14+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=ba1a0249bfbc61ba3590ec0c9cd8b5568980ab22'/>
<id>urn:sha1:ba1a0249bfbc61ba3590ec0c9cd8b5568980ab22</id>
<content type='text'>
The custom modeline's VC `:eval` form was calling `vc-backend`, `vc-working-revision`, `vc-git--symbolic-ref`, and `vc-state` on every redisplay. Mode-line eval runs every keystroke. For a large git repo or a TRAMP buffer over SSH, the round-trip cost shows up as visible input lag.

I split the inline form into helpers and added a buffer-local cache. `cj/modeline-vc-info` returns the cached plist when its TTL hasn't expired and the cache key still matches. The TTL defaults to 5 seconds via `cj/modeline-vc-cache-ttl`. Save and revert hooks invalidate the cache so the user sees state changes promptly. The render path (`cj/modeline-vc-render`) is now a separate function so it can be tested without touching VC at all.

Remote files are skipped by default. `cj/modeline-vc-show-remote` opts back in for cases where TRAMP VC is fast enough to be worth it.

Measured on this repo: uncached reads were about 2.4 ms each, cached reads were about 0.0025 ms each, and remote-skipped reads pay only the cheap `file-remote-p` check.

I added five tests in `tests/test-modeline-config-vc-cache.el`: cache reuse within TTL (backend called once for two reads), refresh after TTL expiry (called twice), remote-file bypass (no backend call, nil result), cache clear (buffer-locals reset to nil), and render output (branch text + face metadata preserved).
</content>
</entry>
<entry>
<title>fix(modeline): Align to window edge instead of buffer margin</title>
<updated>2025-11-15T10:37:06+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2025-11-15T10:37:06+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=d4f3d96fd9683831b973286834982df860178b45'/>
<id>urn:sha1:d4f3d96fd9683831b973286834982df860178b45</id>
<content type='text'>
Change mode-line-right-align-edge from 'right-margin to 'window.

This fixes an issue where centered text modes (nov-mode for EPUBs, etc.)
with wide buffer margins would cause the right-aligned modeline elements
(VC branch, weather, etc.) to have large gaps on the right side.

Now the modeline always aligns to the window edge regardless of buffer
margin settings.
</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>
<entry>
<title>a/v recording: fix setup, add test functionality and indicator</title>
<updated>2025-11-11T23:43:34+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2025-11-11T23:43:34+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=3750a4e683d19aa424223618b1d67a2d963461bf'/>
<id>urn:sha1:3750a4e683d19aa424223618b1d67a2d963461bf</id>
<content type='text'>
Integrates a modeline indicator to display active recording status
in Emacs. The indicator shows "🔴Audio", "🔴Video", or "🔴A+V" based
on the active recording processes. Includes functions for starting
and stopping audio/video recordings, with sentinel processes
ensuring timely updates to the modeline. Also adds extensive
integration tests to validate modeline synchronization.
</content>
</entry>
<entry>
<title>feat: Fix modeline lag and add org multi-level sort with comprehensive tests</title>
<updated>2025-11-08T22:11:58+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2025-11-08T22:11:58+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=513dfd2a1d497da8bd9d5261458cf4140dce7ad6'/>
<id>urn:sha1:513dfd2a1d497da8bd9d5261458cf4140dce7ad6</id>
<content type='text'>
Performance improvement and new feature with full test coverage.

## Changes

### 1. Fix modeline line/column position lag (#A priority)
- Replace expensive line-number-at-pos with cached %l/%c format specifiers
- Enable line-number-mode explicitly for caching
- Result: Instant modeline updates, zero performance overhead
- Files: modules/modeline-config.el:81-83, modules/ui-config.el:53

### 2. Implement multi-level org sorting
- New function: cj/org-sort-by-todo-and-priority
- Sorts by TODO status (TODO before DONE) AND priority (A→B→C→D)
- Uses stable sorting: priority first, then TODO state
- Gracefully handles empty sections (no error)
- Bound to C-; o o (ordering → org sort)
- Files: modules/org-config.el:278-299, modules/custom-ordering.el:253,267

### 3. Comprehensive ERT test suite (12/12 passing)
- Normal cases: Mixed TODO/DONE, multiple of same type, same priority
- Boundary cases: Empty sections, single entries, no priorities
- Error cases: Non-org-mode buffer
- Test file: tests/test-org-sort-by-todo-and-priority.el

### 4. Testing improvements discovered
- Disable org-mode hooks to avoid package dependencies in batch mode
- org-sort-entries must be called from parent heading
- Preserve priority cookie in org-get-heading (t t nil t)
- Add condition-case to handle "Nothing to sort" gracefully

### 5. Minor cleanup
- Comment out chime-debug setting (org-agenda-config.el:267)
- Mark modeline lag task as DONE in todo.org

## Technical Details

Modeline optimization:
- line-number-at-pos is O(n) where n = current line
- %l and %c are O(1) lookups from cached values

Org sorting algorithm uses stable sort:
1. Sort by priority (A, B, C, D, unprioritized)
2. Sort by TODO status (preserves priority order within groups)
Result: TODO [#A], TODO [#B], DONE [#A], DONE [#B], etc.
</content>
</entry>
<entry>
<title>fix: Resolve async buffer error and improve modeline spacing</title>
<updated>2025-11-04T23:23:50+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2025-11-04T23:23:50+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=ea8cf7cfed957b8a2b6f864faa2b2f4f8be118e7'/>
<id>urn:sha1:ea8cf7cfed957b8a2b6f864faa2b2f4f8be118e7</id>
<content type='text'>
- Fixed critical "Selecting deleted buffer" error on startup
  - wttrin-mode-line-mode now activates after init completes
  - Uses after-init-hook to prevent async buffer deletion
  - Weather widget loads safely after Emacs finishes initializing

- Improved modeline right-side spacing
  - Added 2 non-breaking spaces after misc-info segment
  - Prevents weather icon from appearing flush with edge
  - Non-breaking spaces survive right-align trimming
</content>
</entry>
</feed>
