aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* feat(term): close the EAT buffer when its shell exitsCraig Jennings11 days1-0/+3
| | | | Set eat-kill-buffer-on-exit so exiting the shell (exit, C-d) kills the EAT buffer instead of leaving a dead terminal around, matching the ghostel-kill-buffer-on-exit behavior.
* feat(theme-studio): expose EAT terminal faces with a previewCraig Jennings11 days4-3/+40
| | | | Add EAT as a theme-studio app so its faces become editable in the studio: the 16 named ANSI palette faces, the SGR attribute faces (bold, faint, italic, slow/fast blink), and the three shell-prompt annotation faces. renderEatPreview draws a sample terminal with the palette rows, colored ls output, and the prompt annotations, so editing a face updates the preview live. No seed colors are set, so the faces sit at their own defaults until themed, consistent with the vanilla pass. Mirrors the existing ghostel terminal app.
* feat(term): toggle EAT instead of ghostel on F12Craig Jennings11 days3-44/+96
| | | | F12 now creates and toggles a single EAT terminal (pure-elisp, fully themeable) instead of a ghostel one, reusing the existing dock-and-remember geometry toggle. ghostel stays for ai-term on M-SPC. The EAT terminal runs a plain shell with no tmux. F12 and C-; are bound in EAT's semi-char and mode keymaps so they reach Emacs from inside the terminal (EAT forwards unbound keys to the shell otherwise). Retargeted the toggle's buffer predicate and create-new path from ghostel to EAT, and updated the buffer-filter tests to the EAT semantics.
* docs(term): design for the F12 -> EAT terminal swapCraig Jennings11 days1-0/+83
|
* feat(dashboard): enable nerd-icons on the section headingsCraig Jennings11 days1-0/+1
| | | | dashboard-set-heading-icons t adds icons to Projects/Bookmarks/Recent titles, the last open piece of the dashboard-theming fix (file icons and the font-lock fix already landed). Renders colored now that the dashboard is out of global font-lock.
* fix(transcription): land video transcripts beside the source, not in /tmpCraig Jennings11 days2-4/+32
| | | | The video flow extracts audio to a temp .mp3 in /tmp, then derived the .txt/.log from that temp path, so transcripts landed in /tmp and died on reboot, contradicting the "alongside the source" docstring. Thread the source video through cj/--start-transcription-process as an optional output base so the outputs derive from it (talk.mp4 -> talk.txt beside the video). Audio is unchanged. New regression test pins the output base to the source video.
* feat(theme-studio): bind dashboard preview list items to dashboard-items-faceCraig Jennings11 days2-22/+22
| | | | The dashboard preview rendered the project/bookmark/recent rows as plain text, so editing dashboard-items-face didn't recolor them. Wrap those rows in dashboard-items-face so the preview reflects the face. It inherits widget-button (unspecified) while unset, so the rows stay bare until the face is themed -- this only makes the preview responsive. Completes the dashboard preview's face coverage.
* fix(calendar-sync): atomic writes, curl --fail, and zero-event vs garbageCraig Jennings11 days3-17/+104
| | | | Three robustness fixes from the config audit. (1) calendar-sync--write-file and --save-state now write a temp file in the same directory and rename it into place, so org-agenda or chime reading mid-write never sees a half-written calendar. (2) The two curl fetches gain --fail, so an HTTP 404/500 error page exits non-zero instead of flowing its HTML into conversion. (3) calendar-sync--parse-ics distinguishes a healthy zero-event calendar (a real iCalendar with BEGIN:VCALENDAR and no in-window events returns the header) from garbage (no VCALENDAR returns nil), so a near-empty calendar no longer reports "parse failed". New robustness tests; the empty-calendar boundary test updated to the corrected behavior. Verified against the live feed: all three calendars fetch and write cleanly.
* fix(org-roam): drop the redundant refile-targets build that froze first idleCraig Jennings11 days1-2/+4
| | | | org-roam is :defer 1, and its :config called cj/build-org-refile-targets, running the multi-file refile scan synchronously at the 1s idle on a cold cache, which froze Emacs at first idle. org-refile-config already schedules the same build on a 5s idle timer, so the org-roam call was a duplicate. Removed it; the 5s timer covers it.
* chore(dashboard): use the nf-cod-library codicon for the Calibre launcherCraig Jennings11 days2-2/+4
| | | | Closer to the actual Calibre logo than the faicon book-open. Switches the launcher's icon function to nerd-icons-codicon, adds the matching declare-function, and adds nerd-icons-codicon to the test's icon mock.
* refactor: strip all literal colors from config modulesCraig Jennings11 days7-51/+41
| | | | Remove every hardcoded color from the config so nothing assigns a non-themeable value. Stripped: the org-faces defface defaults (28 hex foregrounds, which the theme overrides at runtime anyway), the hl-todo keyword colors, the eshell prompt gray/white, the org-noter and music active-window #1d1b19 bg tint (face-remap is now a no-op), the pdf-view midnight palette, and the calibredb/nov epub sepia. The config now renders with default/theme faces, which surfaces where theming support is missing. Those gaps are tracked in todo.org as the next phase. 0 hex colors remain; validate-modules clean and the org-faces/build-theme/face-diagnostic tests pass.
* fix(dirvish): reap the Super+F popup session on any frame closeCraig Jennings11 days1-0/+16
| | | | The popup's q (cj/dirvish-popup-quit) reaps the session's dired buffers, but closing the Hyprland float directly or letting it lose focus bypasses q and orphans them, which is the "leaves a load of buffers around" symptom. A delete-frame-functions hook scoped to the "dirvish" popup frame runs dirvish-quit on every close path. Regular C-x d sessions, where multiple dired buffers are wanted for mark-and-move, and the global dired-kill-when-opening-new-dired-buffer (deliberately off for that same reason) are both untouched. Verified: a navigated popup session drops back to baseline when the frame closes without q.
* fix(eww): inject the User-Agent reliably under byte-compilationCraig Jennings11 days1-0/+7
| | | | eww-config.el is lexical-binding and the User-Agent advice let-binds url.el's url-request-extra-headers, but the file never declared it special. The byte-compiler then bound it lexically, so the injected header never reached url-retrieve and the desktop User-Agent silently dropped in compiled production (eww kept working, just with the default UA). A top-level (defvar url-request-extra-headers) makes the compiler treat it as dynamic so the binding propagates. The advice tests, which exercise the compiled path, now pass.
* test(ai-term): isolate layout-capture globals in collapse-split testsCraig Jennings11 days1-2/+12
| | | | The multi-window and single-window collapse tests call cj/ai-term, which captures the current layout into cj/--ai-term-last-direction / cj/--ai-term-last-size on toggle-off, but only let-bound cj/--ai-term-last-was-bury. They leaked last-direction into the display-rule test, whose display-saved action reads those globals to choose the split direction, so its agent buffer split below instead of right and the assertion failed. Let-bind last-direction and last-size to nil, matching the roundtrip test in the same file. Full ai-term suite is green.
* feat(ai-term): step into detached sessions too, attaching themCraig Jennings11 days5-112/+166
| | | | The next-agent step (C-; a n / M-SPC) cycled only live agent buffers, so a detached session (alive in tmux, no Emacs buffer) was reachable only through the picker. Now the queue is every active agent, live buffer or live session, keyed on the project dir and ordered by buffer name. Stepping onto a detached one attaches it: show-or-create recreates the terminal, which reattaches the tmux session. The live-buffer swap path is unchanged. I replaced the buffer-rotation helper with a dir-based one and added an active-agent enumerator, with 10 tests.
* test(lsp): narrow startup smoke test for prog-lsp config resolutionCraig Jennings11 days1-0/+66
| | | | Pins the load-time invariants of the central LSP module: lsp-enable-remote stays nil (no auto-start on TRAMP files), the file-watch-ignore defaults live in one idempotent helper, the eldoc provider is stripped from the global hook, and no mode accrues a duplicate lsp-deferred entry. Tests the top-level :init and helper surface, since :config defaults defer to lsp-mode's own load under make test.
* fix(theme-studio): restrict the cursor UI row to fg + bgCraig Jennings11 days3-12/+56
| | | | Emacs draws the cursor as a rectangle: its foreground colors the glyph sitting on it and its background is the cursor color, but weight/slant/underline/strike and box are no-ops on it. The UI table now shows only the fg and bg swatches for the cursor row and mutes the style and box cells to a dash, so the studio stops presenting controls Emacs drops. New #cursorrowtest gate; styletest/boxtest retargeted off cursor (it was UI_FACES[0], their generic subject) onto the first styled face.
* feat: add avy (in-buffer jump) and wgrep (editable grep)Craig Jennings11 days2-0/+20
| | | | The two clear wins from the package-list review. avy fills the in-buffer motion gap (C-: jump-to-char-timer, M-g w/l for word/line) that windmove and isearch leave open; wgrep turns a consult-grep / embark-export result into an editable, write-back-across-files refactor (C-c C-p to edit, C-c C-c to apply).
* chore(google-keep): mark v1 implemented, file live-setup VERIFY, record in specCraig Jennings11 days1-1/+5
|
* feat(google-keep): org-page renderer, refresh command, keybindings (Phase 2-3)Craig Jennings11 days4-0/+359
| | | | The elisp side of the Keep integration: a pure JSON-to-org core (parse, tag/heading/render helpers) kept free of .emacs.d specifics for later extraction, plus the IO runner cj/keep-refresh (async make-process + sentinel, atomic temp-then-rename write to keep-file, stderr-token to display-warning) and the glue (C-c k prefix, executable warning, require in init.el). 15 ERT tests over the core and the parse-render-write chain. Read-only v1; live fetch needs the one-time gkeepapi + token setup.
* feat(google-keep): gkeepapi data bridge with JSON contract (Phase 1)Craig Jennings11 days2-0/+244
|
* docs(spec): round 2 review clean, google-keep spec ReadyCraig Jennings11 days1-1/+5
|
* docs(spec): fold round-1 review into google-keep spec (rubric Ready)Craig Jennings11 days1-30/+90
|
* docs(spec): resolve google-keep decisions (read-only v1 to write v2, ↵Craig Jennings11 days1-52/+51
| | | | gkeepapi, authinfo)
* fix(calendar-sync): re-derive status from a declined occurrence overrideCraig Jennings11 days2-1/+46
| | | | A recurring event declined for one occurrence still synced with :STATUS: accepted, because apply-single-exception merged the override attendees but never re-derived the user's status from them, so filter-declined never dropped it. Re-derive :status via find-user-status when the exception overrides :attendees, leaving the inherited status when the override doesn't name the user. Four new tests cover declined, accepted, no-attendee, and user-absent overrides.
* fix(dirvish): point the bg wallpaper command at set-wallpaper on WaylandCraig Jennings11 days2-6/+7
| | | | The Wayland branch of cj/--wallpaper-program-for returned swww, but this system's wallpaper daemon is awww, so dirvish "bg" silently no-opped. Point it at the set-wallpaper script (on PATH via dotfiles), which wraps awww img and persists the choice to waypaper's config. X11 still uses feh. Updated the wallpaper-program test to match.
* docs(spec): google-keep in-editor integration spec (draft, 5 open decisions)Craig Jennings11 days1-0/+153
|
* feat(ledger): un-orphan ledger-config and rewrite clean-on-saveCraig Jennings11 days2-14/+27
| | | | ledger-config.el was never required in init.el, so the whole module was dead code. I added the require (which installs ledger-mode, flycheck-ledger, and company-ledger via the global ensure) and rewrote the clean-on-save: instead of rebinding C-x C-s to a clean-then-save command, a buffer-local before-save-hook tidies the buffer on every save path, gated by cj/ledger-clean-on-save. The reports shell out to the ledger CLI, so a load-time check now warns when it isn't on PATH. Added ledger-highlight-xact-under-point.
* refactor(theme-studio): extract control factories to controls.js, drop dead ↵Craig Jennings11 days6-234/+221
| | | | | | | | previewFaceAttrs I split the custom dropdown, detail-editor, and expander factories out of app.js into controls.js (205 lines), spliced back at a CONTROLS_J token by generate.py. The token sits at the exact extraction point, so the assembled page is byte-identical and every gate passes unchanged. app.js drops from 927 to 721 lines. I also removed previewFaceAttrs (function, export, and test). It was test-only dead code whose docstring stalely claimed the gate calls it. The gate uses assertPreviewFaces instead.
* refactor(theme-studio): tier-1 simplification passCraig Jennings11 days8-82/+81
| | | | | | | | These are behavior-preserving cleanups from the refactor/simplify assessment, all test-verified. I merged syncMockHeight and syncPkgHeight into one syncPaneHeight(tableId, paneId), inlined the two single-use displayHex/displayName closures, dropped a pkgbody guard that buildPkgTable already does, and had paintUI call worstCellHtml instead of rebuilding the covered-contrast cell. I deleted the dead generatorHues "manual" branch (a copy of the fallback) and locateInfoLine (orphaned when I removed the preview info line earlier today). The two nerd-icons loaders now share _load_nerd_icons_artifact, with a sentinel so a null-file edge keeps its exact behavior. face_coverage.classify reads through named locals now, guarded by a new characterization test. Two assessment findings were wrong and skipped after I checked them against the code: LOCATE_REG is live (read by previewSpan), and normalizePaletteEntryCore doesn't exist.
* fix(theme-studio): gold nav arrows for the language and preview dropdownsCraig Jennings12 days2-4/+10
| | | | The gold viewnav style was scoped to .pkgbar, so the arrows on the .langbar selectors (language and preview) fell back to default gray. I broadened the rule to .langbar and added a dimmed-gold disabled state for the single-pane preview.
* style(theme-studio): unify nav dropdowns to gold-on-darkCraig Jennings12 days3-6/+12
| | | | The view, language, and preview selects share a navsel class matching their flanking arrow buttons (dark bg, gold bold-mono text), so each select and its arrows read as one control.
* feat(theme-studio): visible size-nav buttons + 48 pt gallery scaleCraig Jennings12 days5-53/+83
| | | | | | | | | | The preview dropdown gets flanking nav buttons, matching the view selector, so the size steps with a click. Left/Right arrows do the same when the dropdown is focused. Both clamp at the ends and disable on a single-pane app. I extended the size scale to 32 and 48 pt for inspecting a glyph's detail. The cell width scales with the size, so beyond about 48 pt the grid is mostly scrolling. I removed the separate hover info line beside the dropdown. Each glyph's own title tooltip already shows its face and color, so the line was redundant. A new computed-style gate confirms the point size renders to the right pixels (24 pt is 32 px), so the pt label isn't lying.
* chore(theme-studio): update WIP theme snapshotCraig Jennings12 days2-107/+141
|
* feat(theme-studio): nerd-icons gallery as a hue-ordered icon gridCraig Jennings12 days13-187/+2366
| | | | | | | | | | The nerd-icons pane is now a grid: one row per color face, the rows ordered by hue so families cluster, distinct icons (deduped within a color) drawn in their color with the icon's nerd-font name beneath. A "preview:" dropdown above the grid picks the glyph size in points, with Left/Right arrows to step it. Single-pane apps show it disabled, naming the preview. This replaces the v1 legend in the pane, whose data is still captured for round-trip. build-nerd-icons-legend.el is now a library. A cj/nerd-icons-write-legend entry point requires nerd-icons only at write time, so the capture logic loads and unit-tests without it. It dedupes icons by name within a face, computes each face's native hue, and orders the groups by hue. Writing the test surfaced a latent bug: face-hsl used (cadr (assoc t spec)), which grabs the first keyword instead of the plist. It only worked because the real faces fall through to the face-foreground branch. I fixed it to a correct t-clause parse. Coverage: 7 ERT capture tests (dedupe, hue order, lightness tiebreak, name sort, skip rules), 4 Python validator edges, and browser gates for the grid and the size dropdown. Locate stays color-level: clicking a color flashes its icons, and clicking an icon flashes its color row. Icons aren't individually editable, so there's nothing per-icon to select.
* fix(theme-studio): render nerd-icon glyphs in previews instead of tofuCraig Jennings12 days7-12/+151
| | | | | | | | The legend, dashboard, and package previews drew nerd-icon glyphs as empty boxes. The font-family never reached them: PREVIEW_FONT was spliced into inline style="..." attributes with a double-quoted family name, so the inner quote closed the attribute early and the font was silently dropped. Dropping the quotes fixes it. A no-space family name needs none. I embedded the glyph font directly: Symbols Nerd Font Mono, encoded with fontTools (woff2_compress output is rejected by headed Chrome and Firefox), inlined as a data: URI under the unique family name ThemeStudioNerd so it resolves to the embed rather than a system-installed copy of the same name. The page is self-contained and renders on any clone. I added a #fonttest gate that parses previewLines output and asserts the resolved font-family plus glyph coverage, plus a make font target that re-encodes the woff2 with fontTools.
* chore(elisp): clear byte-compile warnings (18 more modules)Craig Jennings12 days18-23/+102
| | | | | | Add declare-function/defvar declarations for lazily-loaded package symbols, reflow over-long docstrings, swap pdf-view-*-command interactive-only calls for their non-interactive twins, fix a malformed with-demoted-errors in ledger-config (the clean-buffer body was being read as the format string), and rename the unprefixed global wwwdir to cj/httpd-wwwdir (no external refs). No behavior change. Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ
* chore(elisp): clear byte-compile warnings (calibredb, config-utilities, ↵Craig Jennings12 days6-2/+53
| | | | | | | | tramp, diff, chrono, auth) Add declare-function/defvar declarations for lazily-loaded package functions and variables so each module compiles cleanly standalone; config-utilities wraps two lazy EmacSQL oref slot reads in with-no-warnings. No behavior change. Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ
* chore(elisp): clear byte-compile warnings (org, music, org-roam)Craig Jennings12 days4-152/+283
| | | | | | Move own-command bindings out of use-package :bind to keymap-global-set / with-eval-after-load (C-c C-a org-appear-toggle, R music radio-station, C-c n r/t roam recipe/topic) — same keys, verified live. Pull music-config helper defuns out of :config/with-eval-after-load to top level (their proper home; emms referenced only at call time via declare-function). Swap obsolete org-show-all for org-fold-show-all (9.6). Plus declare-function/defvar for lazy symbols. No behavior change; full suite + launch smoke green. Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ
* chore(elisp): clear byte-compile warnings (elfeed, markdown, prog-general)Craig Jennings12 days3-13/+45
| | | | | | The use-package :bind autoload stubs for own same-file commands collided with their defuns ("defined multiple times"). Move those bindings out of :bind to keymap-global-set / with-eval-after-load after the defun — same key, command, and map, verified live in the daemon. prog-general's case was instead a redundant same-file declare-function (removed). Plus declare-function/defvar for lazy package symbols, and mark-whole-buffer swapped for goto-char+push-mark in the elfeed tag helpers. No behavior change. Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ
* chore(elisp): clear byte-compile warnings (org-noter, calendar-sync, vc, ↵Craig Jennings12 days5-24/+133
| | | | | | | | dirvish, org-contacts) Add declare-function/defvar declarations for lazily-loaded package symbols and reflow over-long docstrings so these modules compile cleanly standalone. org-contacts keeps the diary special vars (date/entry/original-date) declared function-locally rather than file-wide, so the lexical `entry` parameter is unaffected. No behavior change. Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ
* chore(elisp): clear byte-compile warnings (mail, dashboard, eshell, erc)Craig Jennings12 days5-29/+175
| | | | | | Add declare-function/defvar declarations for lazily-loaded package functions and variables so each module compiles cleanly standalone, reflow over-long docstrings, and swap the obsolete erc-server-buffer-p for its named replacement erc-server-or-unjoined-channel-buffer-p (obsolete since 30.1). No behavior change. Two erc setq targets (erc-unique-buffers, erc-generate-buffer-name-function) appear not to be real ERC variables — declared to silence the warning with a NOTE flagging that the intended buffer-naming may not be taking effect. Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ
* style(elisp): clear defcustom-group and dead-local byte-compile warningsCraig Jennings12 days3-5/+15
| | | | | | Add :type and a containing :group to the three localrepo defcustoms (new localrepo defgroup) and to cj/org-agenda-window-height. Drop the unused `initial` local in the org-contacts completion-at-point function. Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ
* fix(elisp): make dead let-bindings of foreign special vars take effectCraig Jennings12 days2-0/+11
| | | | | | Byte-compile flagged three let-bindings of package vars as unused lexical variables — under lexical-binding they compiled to dead locals the package never saw, so the intended behavior silently never happened. Declare each var special so the binding is dynamic: music-config now actually suppresses the emms overwrite prompt on playlist save (emms-source-playlist-ask-before-overwrite) and turns off orderless smart-case for the music picker; org-roam copy-todo-to-today now actually applies its custom dailies capture template. Same class as the coverage-core json fix. Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ
* chore(theme-studio): commit WIP theme locked-face reorderingCraig Jennings12 days1-223/+229
| | | | Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ
* refactor(org-refile): drop roam Project notes as refile targetsCraig Jennings12 days2-11/+11
| | | | | | Stop scanning org-roam notes tagged "Project" for refile targets; keep the "Topic" scan. Behavior change by decision: roam Projects are no longer pulled in anywhere (the agenda never scanned them either — that was a stale doc claim, corrected separately). Refiling into Topic notes and into per-project todo.org files is unchanged. Test reworked to assert Topic is included and Project is not. Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ
* fix(org-agenda): filter missing base files; drop the roam-source doc claimCraig Jennings12 days2-29/+62
| | | | | | cj/--org-agenda-base-files now drops files that do not exist (a fresh machine may lack the synced calendars or the inbox), and org-agenda-skip-unavailable-files is set as a backstop, so org-agenda never prompts to create a missing path — the interactive-prompt class that once hung the chime daemon. The filter lives in the one shared helper, so the agenda builders, single-project view, and the chime initializer all get the existence check. Also correct the docs: the commentary and docstrings claimed org-roam nodes tagged "Project" are agenda sources, but they were never scanned; roam Project/Topic notes are refile targets (org-refile-config.el), not agenda sources. Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ
* feat(ai-term): wrap-teardown + shutdown entry points for rulesetsCraig Jennings12 days4-0/+287
| | | | | | Add the three headless functions the rulesets wrap-it-up workflow calls via emacsclient -e, since this module owns the aiv- session naming, the agent buffer, and the geometry restore. cj/ai-term-quit kills a project's tmux session and agent buffer and restores the layout, idempotent and safe when already gone. cj/ai-term-live-count returns the integer count of live aiv- sessions for the shutdown safety gate. cj/ai-term-shutdown-countdown re-checks that gate, then runs an abort-able run-at-time countdown in the echo area and, uncancelled, runs the shutdown command (a defcustom so tests stub it). Reuses the existing kill/close helpers. 13 ERT tests cover the live-count parsing, the quit kill-and-idempotency, and the gate-abort/cancel/tick logic; the tmux and shutdown side effects are manual. Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ
* docs: holistic README pass — version floor, layout, features, targetsCraig Jennings12 days1-3/+15
| | | | | | Bump the Emacs floor to 30 (developed on 30.2). Fix the module count (~100 to ~120), add docs/ to the layout, and reword scripts/ now that it also holds theme-studio. Add Theme Studio, the ghostel native terminal, and ai-term to Features, and make coverage-summary to the dev targets. Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ
* test(nerd-icons): dir-precedence probe + legend round-trip (phase 4)Craig Jennings12 days3-0/+33
| | | | | | Lock the dir-precedence decision with an ERT probe: when a dir icon already carries nerd-icons-completion-dir-face, the advice's prepended nerd-icons-yellow is first in the face list and wins. Extend the #nerdiconstest browser gate with an export/import round-trip over an assigned nerd-icons color, asserting it re-imports to the same state and that the separate nerd-icons-completion dir-face stays out of the nerd-icons app. Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ