diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-15 20:52:42 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-15 20:52:42 -0500 |
| commit | 8292829809eeec545aadd361d9334af14071e307 (patch) | |
| tree | 62249127bb4c783bca17818bd7ac0902339ef0cc /todo.org | |
| parent | 4289d0b97ce3afac3c5bf1402a1b20340fc6be48 (diff) | |
| download | dotemacs-8292829809eeec545aadd361d9334af14071e307.tar.gz dotemacs-8292829809eeec545aadd361d9334af14071e307.zip | |
chore(todo): file studio inbox tasks, archive resolved
Diffstat (limited to 'todo.org')
| -rw-r--r-- | todo.org | 376 |
1 files changed, 183 insertions, 193 deletions
@@ -44,35 +44,12 @@ Tags are additive. For example, a small wrong-behavior fix can be =:bug:quick:=, and a feature that requires internal restructuring can be =:feature:refactor:=. * Emacs Open Work -** DONE [#A] theme-studio: 2D gallery color picker for assignment dropdowns :feature:studio: -CLOSED: [2026-06-15 Mon] -Replaced the per-face color dropdown (mkColorDropdown popup in app.js) with a 2D grid in the palette-panel shape: galleryModel(cur,palette,ground) in app-core.js (pure; reuses columnsFromPalette) returns a default chip, an optional (gone) cell, and rows = ground strip then one row per family (members dark->light, one selected). 5 node tests + #gallerytest browser gate. Trigger and ‹ › step buttons unchanged; applies to all three tiers. From the roam inbox 2026-06-15. -** TODO [#C] theme-studio: extract a ground() helper for the repeated bg/fg literal :refactor:quick:solo:studio: -The literal {bg:MAP['bg'],fg:MAP['p']} repeats 21 times across app.js and palette-actions.js, plus more in the browser gates. Extract a ground() helper that returns it and replace every call site. Purely mechanical, no behavior change; the node tests + browser gates are the safety net. From the 2026-06-15 refactor review. -** DONE [#C] theme-studio: palette display toggle for base colors vs full palette :feature: -CLOSED: [2026-06-15 Mon] -Added an arrow control on the palette: right collapses each column to its base color (ground steps collapse to bg/fg too), down shows the full spans. #paltoggletest covers it. Commit 5ab506d9. -** TODO [#C] theme-studio: raise the max color spans to 5 :feature:studio: -Increase the palette's maximum span count to 5, for a smoother, slower transition across a color. From the roam inbox. -*** VERIFY which control caps below 5 — current maxes are all 8 -On review the per-column span control, the ground span control, and regenColumn all already cap at 8 (well above 5), so there's no sub-5 limit to raise. Either 5 is already reachable, or the intended control is a different one (e.g. the generator emits base-only columns — spanCount is hardcoded 0 in palette-generator-ui.js). Need Craig to point at the control he's hitting. +** TODO [#A] theme-studio: deploy-wip button on the browser page :feature:studio: +Add a button on the theme-studio page that runs the make deploy-wip target locally (build WIP.json into the theme, live-reload the daemon). The page is served from file://, so the browser can't run make directly. Needs a local bridge: a tiny localhost helper the button POSTs to, or a watched trigger file the page writes. Pick the mechanism before building. From the roam inbox 2026-06-15. ** TODO [#A] theme-studio: cannot reassign fg color :bug:studio: Selecting the fg tile, changing its value, and clicking update errors that an fg already exists instead of updating it. The update path treats a reassign as an add. From the roam inbox. -** DONE [#A] theme-studio: flag gone color assignments with a distinctive border :feature:studio: -CLOSED: [2026-06-15 Mon] -Swatches whose assigned color resolves to "(gone)" now carry a solid red outline (distinct from the dashed unused-tile flag). #gonetest covers it. Commit 0529189a. -** DONE [#A] theme-studio: flag unused palette tiles and columns :feature: -CLOSED: [2026-06-15 Mon] -usedPaletteHexes reverse-lookup over syntax/ui/package assignments + ground; a tile referenced nowhere gets a dashed outline, an all-unused column gets a dashed box. Biased safe (never flags a used color). Node tests + #unusedtest. Commit 7e7b871f. ** TODO [#A] theme-studio: remove the in-table preview column :refactor:studio: Drop the per-row preview from the assignment tables and rely on the live buffer preview alone. Requires auditing every assignment view so each face in the faces column has a situationally appropriate representation in the live preview. Craig wants a reusable project workflow for that periodic audit, created before the refit so it can drive the fix. From the roam inbox. -** TODO [#B] emacs: tag tasks by module name for sorting :refactor:studio: -Replace topic tagging with single-word module tags: :studio: for everything under scripts/theme-studio/, module-named tags elsewhere, :multi: for cross-area work. Drop bug/enhancement-style tags since work should be chosen on other bases. This changes the current six-tag convention, so update the priority-scheme section to document it, rewrite the task-audit workflow to reconcile tasks against the module scheme, then run the audit. Queue for end of session. From the roam inbox. -** TODO [#C] theme-studio: compact the contrast column to a half-filled symbol :solo:quick:studio: -Represent contrast as a colored number colored like the current text. The word passed is unnecessary -** DONE [#C] theme-studio: equalize style and box button cluster sizing :refactor:quick:studio: -CLOSED: [2026-06-15 Mon] -Shrank .sbtn from 26x24 to the 17x15 box-button size (font 13px), so the style and box clusters match and the row returns to roughly its pre-cluster height. Commit 44128931. ** TODO [#A] calendar-sync drops final occurrences, resurrects cancelled meetings :bug:solo: :PROPERTIES: :LAST_REVIEWED: 2026-06-13 @@ -124,17 +101,6 @@ Implemented 2026-06-06 in =modules/calibredb-epub-config.el=: *** TODO Embed Calibre DB metadata into the EPUB files Surfaced 2026-06-06 while building the bookmark naming: the metadata embedded in the EPUB files' OPF is worse than Calibre's database metadata. nov reads the embedded OPF and got truncated titles ("Frege" vs the filename's "Frege: A Guide for the Perplexed"), author-sort "Last, First" forms ("Christie, Agatha"), and lost punctuation ("A.B.C." -> "A B C"). The filenames (from Calibre's curated DB) are the good copy. Fix on the Calibre side: select all (or by library), run "Edit metadata -> Embed metadata into book files" so the DB metadata is written into each EPUB's OPF. Consider auditing author vs author_sort first. After embedding, the in-file metadata matches the library and any tool reading the files (nov, other readers, re-imports) gets the good data. Not an Emacs task; Calibre-side bulk maintenance. -** DONE [#A] Face and font diagnostic popup at point :feature: -CLOSED: [2026-06-15 Mon] -Read-only popup diagnosing why text at point paints as it does (face stack by source, merged attributes, real font vs declared family, theme/config/inherit provenance). Spec: [[id:98f065cf-8bd5-46a0-ac24-da94d66855ad][face-font-diagnostic-popup-spec-implemented.org]]. Building in modules/face-diagnostic.el: pure core cj/--face-diagnosis-at returns the report plist; cj/describe-face-at-point renders it into a read-only help buffer. From the roam inbox — "do this one first." -*** 2026-06-15 Mon @ 12:19:41 -0500 Phase 1 — core read model + buffer classifier landed -modules/face-diagnostic.el: cj/--face-diagnosis-at returns groups 0-2 (buffer classification, character context, face stack by source) via small pure helpers. 17 ERT tests (tests/test-face-diagnostic.el), byte-compile clean. Not yet wired into init.el; the interactive command and keybinding land in Phase 4. -*** 2026-06-15 Mon @ 12:26:52 -0500 Phase 2 — merged attributes + real font landed -cj/--face-diag-merged-attributes folds the ordered, remap-expanded spec stack ("computed"); cj/--face-diag-real-font reports font-at or "unavailable" under batch. Settles spec decision #7 (hand-fold, tested on overlay-over-text-prop, default-remap, and face-symbol fixtures). 23 ERT tests total, byte-compile clean. -*** 2026-06-15 Mon @ 12:30:30 -0500 Phase 3 — provenance trace landed -cj/--face-diag-provenance returns per-face provenance: themes from theme-face, config from saved/customized-face, the :inherit chain, and the attributes still unspecified that fall to the default. Version-sensitive internals sit behind small tolerant accessors. 30 ERT tests total, byte-compile clean. -*** 2026-06-15 Mon @ 12:37:16 -0500 Phase 4 — render + popup wiring landed -cj/describe-face-at-point renders the diagnosis into the read-only *Face Diagnosis* buffer (cj/face-diagnostic-mode), with region-scan mode and an out-of-scope banner; required in init.el; live-verified in the daemon (it already surfaces the auto-dim remaps). Command name settled as cj/describe-face-at-point. Deferred to follow-up: clickable face-name buttons (plain text for now) and the module-header allowlist entry; the keybinding is Craig's to pick. ** DOING [#A] Global yes-or-no-p fset defeats every strong confirmation :bug:quick: :PROPERTIES: :LAST_REVIEWED: 2026-06-13 @@ -161,6 +127,10 @@ Fixed 2026-06-13: cmail gets =mu4e-trash-folder= "/cmail/Trash"; refile is a per :END: From the 2026-06 config audit (verified against the live daemon). =early-init.el:69= =(setq native-comp-deferred-compilation nil)= — the obsolete alias of =native-comp-jit-compilation= — turns JIT native compilation OFF entirely, not "synchronous" as the comment claims: 19 .eln files exist for 184 packages, ~100 of 121 modules run interpreted for the daemon's lifetime, and system-defaults.el:42-44's speed-3/8-jobs/always-compile settings are dead. Plus =early-init.el:113-116= restores =gc-cons-threshold= to the captured STOCK default (800000, verified) post-startup — frequent small GC pauses forever. Together these plausibly feed the filed org-capture 15-20s task more than anything in the capture path itself. Actions: retest the old "Selecting deleted buffer" race on 30.2 and re-enable JIT (or AOT sweep); set a deliberate 16-64MB threshold (or gcmh). Check both before burning time on the capture-perf debug task. +** TODO [#B] theme-studio: sort newest colors near the top :feature:studio: +Newly added colors currently land after the ground layer (bg/fg), low in the order. Surface them near the first entry instead, in both the palette color list and the gallery/dropdown, since the most recently added colors are usually the ones being worked on. From the roam inbox 2026-06-15. +** TODO [#B] emacs: tag tasks by module name for sorting :refactor:studio: +Replace topic tagging with single-word module tags: :studio: for everything under scripts/theme-studio/, module-named tags elsewhere, :multi: for cross-area work. Drop bug/enhancement-style tags since work should be chosen on other bases. This changes the current six-tag convention, so update the priority-scheme section to document it, rewrite the task-audit workflow to reconcile tasks against the module scheme, then run the audit. Queue for end of session. From the roam inbox. ** TODO [#B] 2026-06 full config audit — findings backlog :refactor: Module-by-module review of all 121 modules + init/early-init, holistic passes (startup/perf, stability, UX consistency, package strategy), and spin-offs into pearl, chime, emacs-wttrin. Method: parallel read-only review agents per module group; key claims spot-verified (incl. against the live daemon) before filing. Run 2026-06-11/12, COMPLETE. Tally: ~165 module findings + ~40 holistic + 30 spin-off ≈ 235 total; 40 high-impact bugs filed as standalone tasks above this parent; the rest live in the group children below. Spin-off findings delivered as inbox handoffs to pearl, chime, and emacs-wttrin (2026-06-12-0057). Start with the synthesis child below for the recommended attack order. @@ -802,22 +772,6 @@ Decided 2026-06-11 (Craig): #0d0b0a is the canonical background — the three dr *** TODO org-todo color mismatch: test expects #ff2a00, theme renders #a7502d =dupre-theme-org-todo= (test:130) asserts the org-todo foreground is "#ff2a00" (intense-red), but the theme renders "#a7502d" (red-1). Design call: should org-todo be the bright intense-red or the muted red-1? Fix whichever side loses the decision. -** DONE [#B] dwim-shell: zip overwrites its own name, backup timestamp never expands, dired menu key dead :bug:quick:solo: -CLOSED: [2026-06-13 Sat] -From the 2026-06 config audit, =modules/dwim-shell-config.el=: -- =:338= — single-file zip is =zip -r '<<fne>>.<<e>>' '<<f>>'= — reconstructs the input filename as the archive ("Zip file structure invalid"; directories produce =foo.=). Should be ='<<fne>>.zip'= like the tar-gzip sibling. -- =:549= — backup destination single-quotes =$(date ...)= so the substitution is literal: =foo.txt.$(date +%Y%m%d_%H%M%S).bak=. Move it outside the quotes or format-time-string in Elisp. -- =:932= — dired-mode binding "M-S-d" is unreachable (Meta+Shift+d generates M-D); the dirvish binding two lines down is correctly "M-D". Fix + the stale commentary at dirvish-config.el:30. -Fixed 2026-06-13: zip single-file template now ='<<fne>>.zip'=; backup uses =format-time-string= in Elisp (real =YYYYMMDD_HHMMSS= stamp, dropped the now-unneeded =date= util); dired key M-S-d→M-D + dirvish-config.el:30 doc corrected. Both command strings extracted into top-level builders (=cj/dwim-shell--zip-single-file-command=, =cj/dwim-shell--dated-backup-command=) so they're unit-testable without the dwim-shell-command package — the command defuns live in its use-package :config, which the batch harness doesn't load. 2 builder tests green in make; live daemon confirms all three (backup stamp, .zip, dired M-D). Real backup/zip run + the dired keypress are a VERIFY. - -** DONE [#B] ERC: double mention notifications + tautological server list :bug:quick:solo: -CLOSED: [2026-06-14 Sun] -From the 2026-06 config audit, =modules/erc-config.el=: -- =:281= — =erc-modules= includes the built-in =notifications= module AND :config adds =cj/erc-notify-on-mention= to the same hook — every mention fires two desktop notifications. Pick one path (keep the custom one, slated for messenger unification). -- =:100= — =cj/erc-connected-servers=: inside =with-current-buffer=, the free =erc-server-process= is the buffer's own local value, so the eq test is tautologically true — returns ALL ERC buffers (channels, dead connections). Use =erc-server-buffer-p= + =erc-server-process-alive=. -- =:238= — =user-whole-name= read at load but =user-constants= only required at compile time (same trap as auth-config/keyboard-macros). -Fixed 2026-06-14: removed =notifications= from =erc-modules= (kept the custom =cj/erc-notify-on-mention=, so one notification per mention); rewrote =cj/erc-connected-servers= to filter on =(erc-server-buffer-p)= + =(erc-server-process-alive)= instead of the tautological self-eq; moved =user-constants= to a runtime require. New test-erc-config-connected-servers.el (live-server-only + empty cases) 2 green; module byte-compiles. erc-config not reloaded into the daemon (live IRC session) — takes effect on restart. VERIFY for the one-notification + real-server-list behavior. - ** TODO [#B] erc-yank silently publishes >5-line pastes as public gists :bug: =modules/erc-config.el:345= — C-y in any ERC buffer auto-creates a public gist for anything over 5 lines: clipboard content goes to a public URL with no confirmation, and no executable-find guard for =gist= (errors mid-send if absent). Privacy trap. Add a =yes-or-no-p= gate or drop the package for plain C-y. From the 2026-06 config audit. @@ -1368,14 +1322,6 @@ Update the =dev-fkeys.el= header comment (L33) — TS/JS is no longer punted; th ** TODO [#B] heavy-box comment inserts non-comment lines :bug:solo: =modules/custom-comments.el:427= — =cj/--comment-heavy-box= interior/empty lines carry no comment prefix, so in line-comment languages (elisp, Python) C-; C h injects syntax-breaking bare =*...= lines. The existing test characterizes the broken output (asserts =^\*.*\*$=). Prefix interiors like =cj/--comment-box= does; add the missing min-length validation (negative width hits make-string with a raw error); fix the test to assert corrected output. From the 2026-06 config audit. -** DONE [#B] help-config: three defects in one small file :bug:quick:solo: -CLOSED: [2026-06-13 Sat] -From the 2026-06 config audit, =modules/help-config.el=: -- =:67= — =cl-return-from= inside a plain =defun= (no cl-block): declining the save prompt signals "No catch for tag" instead of canceling. =cl-defun= or restructure. -- =:108= — =:hook (info-mode . info-persist-history-mode)= is dead twice: Info's hook is =Info-mode-hook= (capital I), and =info-persist-history-mode= doesn't exist anywhere. Implement the intent or delete. -- =:111= — auto-mode-alist maps .info to an interactive command that KILLS the buffer mid find-file — programmatic =find-file-noselect= of any .info destroys buffers and pops Info windows. Drop the entry; keep the explicit command. Zero test coverage on this module (the two broken paths are exactly the untested ones). -Fixed 2026-06-13: (1) extracted the save/cancel/open decision into a pure =cj/--info-open-plan= and routed =cj/open-with-info-mode= through it — no more =cl-return-from=, declining cancels cleanly; (2) deleted the dead =:hook= and the empty =:preface=; (3) dropped the destructive =auto-mode-alist= .info entry (kept =cj/open-with-info-mode= as an M-x command and =cj/browse-info-files= on C-h i). New test-help-config.el covers the planner (open / save-then-open / cancel) — 3 green; module loads clean. Stale daemon state (the .info auto-mode entry + the bogus info-mode-hook entry) cleared by hand so the running session is correct without a restart. Interactive Info open + find-file-no-longer-destructive are a VERIFY. - ** TODO [#B] jumper: register collisions and dead-marker errors :bug:solo: :PROPERTIES: :LAST_REVIEWED: 2026-06-13 @@ -1394,11 +1340,6 @@ Spec: [[id:540bf06b-16b8-46c6-b459-c40d1b9c795d][keybinding-console-safety-spec- ** TODO [#B] ledger-config is orphaned — ledger-mode never configured :bug:quick: Nothing requires =modules/ledger-config.el= (verified by grep), so .dat/.ledger/.journal open without ledger-mode, reports, or flycheck-ledger. The module looks finished, not staged (unlike duet-config, which documents its pre-alpha orphaning). Decide: wire into init.el (+ =cj/executable-find-or-warn= for the ledger binary) or delete. From the 2026-06 config audit. -** DONE [#B] markdown live preview clobbered by markdown-mode :bug:quick:solo: -CLOSED: [2026-06-13 Sat] -=modules/markdown-config.el:54= defines bare =markdown-preview=, which markdown-mode redefines the moment the first .md loads — the impatient-mode live preview is dead and F2 silently runs the package command (agent verified in the live daemon). Also =:61= guards on =(boundp 'httpd-process)=, a variable that doesn't exist in simple-httpd — use =(httpd-running-p)=. And the =:config= =(setq imp-set-user-filter 'markdown-html)= at line 41 is doubly dead (function-not-variable, symbol names nothing) — delete. Rename to =cj/markdown-preview=, rebind F2. From the 2026-06 config audit. -Fixed 2026-06-13: renamed the defun to =cj/markdown-preview= and rebound =<f2>=; guard is now =(httpd-running-p)=; deleted the dead =(setq imp-set-user-filter 'markdown-html)= (impatient-mode use-package is now =:defer t= only). Added a guard test (server-down → user-error); 4/4 green; live daemon confirms =cj/markdown-preview= defined and guarding. Browser-render check is a VERIFY. - ** TODO [#B] Messenger window/key unification :feature: :PROPERTIES: :LAST_REVIEWED: 2026-06-13 @@ -1465,11 +1406,6 @@ Folded the external review via spec-response. Craig accepted D1-D5; baked them p *** 2026-06-04 Thu @ 23:30:18 -0500 External re-review: ready Re-reviewed [[id:b54c94a0-d762-4b41-afd7-cf5593ce6675][docs/specs/vterm-to-ghostel-migration-spec-implemented.org]] after incorporation. Verdict: =Ready=. No further blocking review notes; implementation can start from the phase plan and acceptance criteria in the spec. -** DONE [#B] modeline runs synchronous git on the redisplay path, unguarded :bug:solo: -CLOSED: [2026-06-14 Sun] -=modules/modeline-config.el:173,154,145= — the mode-line :eval calls vc-backend/vc-state/vc-working-revision (synchronous git) on TTL expiry; a slow or unmounted filesystem stalls ALL redisplay. The cache key computes =file-truename= on every render (the "one stat per refresh" comment is wrong), and nothing is condition-case-wrapped, so a signal lands inside the mode-line eval. Defer the truename behind the TTL check; wrap the fetch in condition-case caching nil. From the 2026-06 config audit. -Fixed 2026-06-14 (Approach A): dropped =file-truename= from =cj/modeline-vc-cache-key= (key is now =(file show-remote)=, no per-render stat; a moved symlink is caught at the next TTL refresh when vc-backend resolves the link fresh). Wrapped =cj/modeline-vc-fetch= in =condition-case ... (error nil)= so a git signal on a slow/unmounted FS degrades to no-VC-info instead of breaking redisplay. Rewrote the two truename cache-key tests to assert the cheap key; added a fetch-swallows-vc-errors test. 9 modeline vc tests green; live daemon confirms the 2-element key and nil-on-error fetch. - ** DOING [#B] Module-by-module hardening :PROPERTIES: :LAST_REVIEWED: 2026-06-05 @@ -2809,9 +2745,6 @@ configuration (=text-config=, =diff-config=, =ledger-config=, =games-config=, =mu4e-org-contacts-setup=, =telega-config=, =httpd-config=, =org-agenda-config-debug=). -** DONE [#B] org-faces: custom header-row face layer + theme-studio app :feature:theme-studio:spec: -CLOSED: [2026-06-15 Mon] -Named, theme-agnostic faces for org TODO keywords and priorities, wired via org-todo-keyword-faces / org-priority-faces, plus a dedicated theme-studio "org-faces" app beside elfeed and mu4e. Spec: [[id:35578114-8c29-43af-97a2-fdfea01a802e][org-faces-spec-implemented.org]]. All four decisions resolved (prefix org-faces-, real defface defaults, auto-dim repointed to org-faces-*-dim, all 10 keywords). Phase 1 (modules/org-faces-config.el + 5 ERT tests), Phase 2 (auto-dim-config.el repoint), Phase 3 (theme-studio org-faces app) all landed and verified; the build-theme round-trip is confirmed mechanically. Residual visual check is a VERIFY under Manual testing and validation. ** TODO [#B] org-roam :config triggers the 15-20s refile scan synchronously at first idle :bug:solo: =modules/org-roam-config.el:78-79= — org-roam is =:defer 1=, so its :config calls =cj/build-org-refile-targets= at 1s idle, BEFORE the 5s background timer (=org-refile-config.el:144-151=); on a cold cache the 30k-file scan runs inline and freezes Emacs at first idle. Drop the call — org-roam is loaded long before the 5s timer fires. Likely a player in the filed org-capture 15-20s perf task (=[#B] Optimize org-capture target building performance=) — check both together. From the 2026-06 config audit. @@ -2837,14 +2770,6 @@ From the roam inbox: the page-me workflow should always identify which project e ** TODO [#B] Scratch buffer background a shade lighter than default :feature: Make *scratch* just-noticeably lighter than the normal background so it reads as the scratch buffer. Simplest is a buffer-local face remap on *scratch*; Craig is fine routing it through org-faces if a theme hook is wanted. The exact lightening delta is an aesthetic call to tune visually. From the roam inbox. -** DONE [#B] slack-config lifecycle gaps :bug:quick:solo: -CLOSED: [2026-06-14 Sun] -From the 2026-06 config audit, =modules/slack-config.el=: -- =:265= — w / @ / # bound to commands neither autoloaded nor in :commands — void-function before slack loads. Add to :commands. -- =:246= — =cj/slack-close-all-buffers= reads =slack-current-buffer= (declared but unbound) without the boundp guard its sibling has — void-variable on C-; S Q before slack loads. -- =:259= — raw =global-set-key= for C-; S bypasses =cj/register-prefix-map= (signal/erc use it); invisible to the keybindings registry and the planned unification enumeration. -Fixed 2026-06-14: added =slack-message-write-another-buffer=, =slack-message-embed-mention=, =slack-message-embed-channel= to =:commands= (w/@/# now autoload); guarded =cj/slack-close-all-buffers= with =buffer-local-boundp= (no void-variable on C-; S Q before slack loads); switched =global-set-key= to =(cj/register-prefix-map "S" cj/slack-keymap "slack")= (+require keybindings). New test-slack-config-close-all.el green; module loads, C-; S registered in the registry. Not reloaded into the live daemon (active slack session) — restart to apply. VERIFY for the pre-load key safety. - ** TODO [#B] Split window opens the dashboard in the other window :feature:quick:solo: :PROPERTIES: :LAST_REVIEWED: 2026-06-10 @@ -2857,42 +2782,10 @@ When splitting with C-x 2 (=split-window-below=) or C-x 3 (=split-window-right=) ** TODO [#B] system-defaults: top-level server-start unguarded in batch :bug:quick:solo: =modules/system-defaults.el:140= — raw module load under =--batch= (make validate-modules on a machine with no daemon socket) starts a server from a batch process; the suite only passes because the testutil stubs it. Wrap in =(unless noninteractive ...)= — the repo's established guard for this defect class; same guard stops the =custom-file= =make-temp-file= at line 104 littering temp files per batch load. From the 2026-06 config audit. -** DONE [#B] theme-studio color columns :feature:studio: -CLOSED: [2026-06-13 Sat] -:PROPERTIES: -:LAST_REVIEWED: 2026-06-11 -:END: -Show the palette as hue-grouped strips (dark→light) over the existing flat, individually-editable palette. Grouping is by OKLCH hue from the hex, so renaming a color never moves it. A per-strip count control generates a symmetric ramp (N → base ±N) from the strip's most-saturated color; regenerate is authoritative, repointing surviving-step references by lightness rank and leaving removed-step references a visible "(gone)". The ground strip is synthesized from the bg/fg assignments and pinned first; the standalone ramp panel is removed. Designed in [[file:docs/theme-studio-color-families-spec.org][docs/theme-studio-color-families-spec.org]]. Codex-reviewed Ready 2026-06-10 after response folded: pivoted from name-derived families to hex-derived families over a flat palette, which designs out the name-grammar/import-inference and chip-ownership blockers. All review findings dispositioned; both open decisions resolved. Builds on and supersedes the palette-ramps v1 ramp UI. - -All six phases landed 2026-06-10 (commits ebe18d51, 74db9a52, 111687b0, e7ae18c4, 77783126, f6ab0001, 9daeff15, and the Phase 6 commit); =make theme-studio-test= green (98 node tests, 16 browser gates). Code-complete and self-verified. The hue-adjacent warm-color grouping limitation is filed as a separate research task (=~/color-sorting.org=). Remaining: the manual aesthetic/fidelity sign-off under the Manual testing parent (hue grouping reads right, regenerate-replace reads as deliberate, removed-step "(gone)" is clear). Mark this DONE once that passes. -Retired 2026-06-13: the current implementation no longer uses color-derived hue families. Palette entries carry stable structural column ids, generated colors stay in their originating column, renames do not move tiles, and =#columntest= / =#roundtriptest= pin the behavior. -*** 2026-06-10 Wed @ 01:17:45 -0500 Family model core landed -Phase 1 (commit =ebe18d51=, grouping reworked in =77783126=). =familiesFromPalette=, =regenFamily=, =rankByLightness=, =stepRepointPlan= in app-core.js, pure and hex-derived. Grouping started as gap-clustering + flat neutral threshold; after the design discussion it became nearest-hue-anchor bucketing (no single-linkage chaining) + a lightness-scaled neutral threshold (pale tints keep their hue, mid grays go neutral). regenFamily handles n=0 without ramp()'s clamp; stepRepointPlan maps survivors / lists removed by signed lightness rank. 20 node tests including the green/yellow split and the no-chaining case. Open: hue-adjacent warm colors still merge — research task above (=~/color-sorting.org=). -*** 2026-06-10 Wed @ 01:17:45 -0500 Family sort core landed -Phase 2 (commit =74db9a52=). =sortFamilies=/=sortFamilyMembers=: neutrals first, then chromatic by base hue (rounded so a hue hair doesn't outrank lightness), ties by base lightness then hex; members dark→light. Display-only; stored palette order untouched. 4 node tests. -*** 2026-06-10 Wed @ 01:17:45 -0500 Family-strip rendering landed -Phase 3 (commit =111687b0=, columns =e7ae18c4=). renderPalette restructured into the pinned ground strip + hue-sorted family columns (top→bottom dark→light), chips keep per-chip rename/remove/select, move-arrows/drag dropped. #familytest gate locks the structure + rename-stays-in-strip. Existing palette flows stay green. -*** 2026-06-10 Wed @ 01:17:45 -0500 Count control + regenerate landed -Phase 4 (commit =f6ab0001=). Per-chromatic-strip count input (0-4); setting N regenerates the family as base ±N, repointing survivor references by lightness rank and leaving removed-step references on their now-gone hex. Also fixed the neutral-threshold curve to taper at both lightness ends (symmetric Munsell) so chroma-eased dark/light extremes keep their hue. #counttest gate covers count up/down + the survivor/removed reference behavior. -*** 2026-06-10 Wed @ 01:17:45 -0500 Base edit + retire ramp panel landed -Phase 5 (commit =9daeff15=). Editing a family base recolors the whole family (shared =regenFamilyInPlace= with the count control); editing a ground swatch writes the bg/fg assignment. The standalone ramp panel (button, panel, JS, CSS, #ramptest) is removed — fan a color via its column's count instead. #baseedittest gate covers base-edit recolor + reference follow + the bg-swatch edit. -*** 2026-06-10 Wed @ 01:17:45 -0500 Warnings, seeding, export, README close-out landed -Phase 6 (commit =c175e2be=). Export stays a flat palette and import needs no reconstruction (#roundtriptest: export→import→export byte-identical). =seedPkgmap= reads the flat palette unchanged. The too-similar warning stays on the full palette — the planned ramp-step exemption was dropped after analysis: ramp steps are a stepL apart (well above the ΔE threshold) so they never warn, and exempting same-family pairs would hide genuine near-duplicates (caught by #deltatest). README documents families, the ground strip, the count control/regenerate, removed-step references, and the ramp-panel removal. - ** TODO [#B] theme-studio: dashboard preview icons missing, list items unthemed :bug:studio: Found while theme-testing the live dashboard against the preview. - The navigator icons don't render in the preview at all, showing as mojibake. The nerd-font glyphs have no font fallback in the browser. - No way to set the color of the project, bookmark, and recent-files list items. The preview renders those entries as plain unstyled text, and the dashboard app exposes no editable face for them. -** DONE [#B] theme-studio delete entire color column :feature:quick:solo:studio: -:PROPERTIES: -:LAST_REVIEWED: 2026-06-13 -:END: -Add a column-level delete affordance to the palette. Deleting a normal color column removes the base color and every generated/imported tile in that column from the screen and from =PALETTE=. Ground remains pinned and non-deletable. Existing assignments that referenced removed tiles should follow the current deleted-color behavior: they remain on the old hex and appear as recoverable =(gone)= values rather than silently repointing. - -Acceptance notes: add a browser gate proving a column delete removes all entries with that stable =columnId=, leaves other columns alone, leaves ground non-deletable, and preserves any references to deleted hexes as gone values. - -Shipped 2026-06-13 in commit =2cf730d5=: normal column headers have a delete button, ground has no delete affordance, deleted names feed =lastGone= for recovery, and =#columntest= pins the behavior. - ** TODO [#B] theme-studio guide-support features :feature:studio: :PROPERTIES: :LAST_REVIEWED: 2026-06-13 @@ -2925,44 +2818,8 @@ Spec draft: [[id:2df157b8-c7c1-47a9-b080-d9586c6f424c][theme-studio-palette-gene Build a constraint-first palette generator for Theme Studio: start from bg/fg, generate editable palette columns in OKLCH, preview candidate columns before applying them, and apply proposals by append/replace/regenerate modes while preserving stable column ids and existing assignments where possible. V1 is palette-only, not automatic face assignment; generator output becomes normal editable columns after apply. -** DONE [#B] theme-studio palette ramps + contrast safety v1 :feature:studio: -CLOSED: [2026-06-13 Sat] -:PROPERTIES: -:LAST_REVIEWED: 2026-06-10 -:END: -The v1 build from [[file:docs/theme-studio-palette-ramps-spec.org][theme-studio-palette-ramps-spec.org]] (Ready, Codex-reviewed). Two coupled features: a ramp generator (one base color → harmonized tonal ramp) and background-contrast safety (worst-case floor over a face's foreground set + safe-lightness guidance). - -All five phases + the README close-out landed 2026-06-09 (commits 1d51a332, 9da6c663, e7021bfe, 1d8b9f9e, 843bbf08, 23926837); =make theme-studio-test= green (78 node tests, 12 browser gates). Code-complete and self-verified. Remaining: the aesthetic and real-Emacs-fidelity sign-off under the Manual testing parent below (does a ramp harmonize, does the safe band read clearly, does a "safe" tint actually read behind real syntax). Mark this DONE once that passes. -Retired 2026-06-13: this parent is no longer active planning work. The useful pieces are already in the current tool, and later structural-column work replaced the standalone ramp workflow. -*** 2026-06-09 Tue @ 18:40:20 -0500 Ramp generator core landed -Phase 1 (commit =1d51a332=). =ramp(baseHex, {n, stepL, chromaEase})= in app-core.js → ={steps: [{hex, clamped, offset}], adjusted}= or ={steps: [], error: 'bad-hex'}=. Holds the OKLCH hue, steps lightness by =stepL=, quadratic chroma-ease toward the extremes, gamut-clamps each step; knobs clamp to range with the clamped knob named in =adjusted= (n=2/stepL=0.08/chromaEase=0.5 defaults). 10 node tests (mid/near-white/near-black bases, hue-hold, chroma ease, knob clamping, malformed hex), suite 55→65, =make theme-studio-test= green. The app-core integrity stripper now drops =import= lines too. -*** 2026-06-09 Tue @ 19:06:46 -0500 Ramp UI in palette landed -Phase 2 (commit =9da6c663=). A "ramp" button opens a panel that generates from the current color and previews the steps (named per source swatch, clamp badge on out-of-gamut steps); the n/stepL/chroma-ease controls default to 2/0.08/0.5. Click a step or "add all" to insert adjacent to the source in -n..+n order; name collisions skip (no overwrite), hex duplicates add with a flag. New #ramptest gate pins count, ordered insertion, collision skip, and the clamp badge. Verified headless + screenshot. -*** 2026-06-09 Tue @ 18:53:16 -0500 Foreground-set + floor + L_max core landed -Phase 3 (commit =e7021bfe=). =fgSetFor=, =floor=, =lMax= and the =COVERED_FACES= constant in app-core.js, all pure and explicit-state. fgSetFor returns {set:[{hex,label}]} or a structured reason ('out-of-scope'/'empty'); floor returns {ratio, limitingHex, limitingLabel}; lMax scans L from black to bracket the dark-side crossing then binary-searches it (tol 0.001), status ok/none/all/clamp. 13 node tests including the keyword-blue #67809c fixture and lMax's none/all/clamp branches. Suite 65→78, =make theme-studio-test= green. -*** 2026-06-09 Tue @ 19:06:46 -0500 Worst-case contrast readout landed -Phase 4 (commit =1d8b9f9e=). The five covered overlay faces show the worst-case floor over their foreground set (live syntax colors + default fg) and name the limiting foreground; a syntax-color edit repaints them. Out-of-scope faces keep the single-pair cell; an empty set reads "no fg set". Verdict is WCAG AA by default. New #contrasttest gate pins the readout, the keyword-blue limiting case, the single-pair fallback, and the no-set string. -*** 2026-06-09 Tue @ 19:06:46 -0500 Safe-lightness picker guidance landed -Phase 5 (commit =843bbf08=). The OKLCH picker gets a "safe for" selector over the covered faces; the C×L plane shades the lightness band too light to keep that face readable, with the L_max ceiling (via =lMax= at the current chroma) as the band's lower edge. A too-dark foreground shades the whole plane. New #safetest gate pins band-shows-for-covered-face and hides-when-none. Verified headless + screenshot. -*** 2026-06-09 Tue @ 19:06:46 -0500 README + test-surface close-out landed -Commit =23926837=. README documents the ramp controls and defaults, the worst-case floor / limiting foreground, the five covered faces, the safe-lightness guidance, and WCAG-drives-PASS-FAIL with APCA as a diagnostic; the browser-gate list is updated. =make theme-studio-test= carries all new node tests and the #ramptest/#contrasttest/#safetest gates. All acceptance criteria met. - ** TODO [#B] theme-studio: preview element locate (hover + click) :feature:theme-studio:spec: :spec:studio: General preview interaction: hover any element for its section / face / value, click a current-pane element to flash and jump to its assignment row, off-pane elements hover-only (not clickable). Built on a face -> owning-app registry that previews also read for cross-pane live rendering. Spec: [[id:fbcf0e20-1328-42b4-aa36-3401509e7816][theme-studio-preview-locate-spec.org]]. Prerequisite for the richer org-agenda preview. -** DONE [#B] theme-studio preview face mislinks (org, erc, flycheck) :bug:quick:solo:studio: -CLOSED: [2026-06-13 Sat] -:PROPERTIES: -:LAST_REVIEWED: 2026-06-11 -:END: -Found by Craig 2026-06-11 during the manual-test walk (org case), then a full audit of all 20 bespoke previews confirmed three mislinks; the rest are clean: - -1. app.js:564 (org) — "Heading three" carries data-face org-headline-todo on a line with no TODO keyword; org-headline-todo's docstring says it applies to the part of the headline after the TODO keyword. Fix: add an org-todo keyword span mirroring the DONE line at app.js:563 (stars = org-level-3, keyword = org-todo, text = org-headline-todo). -2. app.js:765-766 (erc) — swapped: craig's own message text is erc-default-face and bob's is erc-input-face. erc-input-face is "ERC face used for your input"; swap them. -3. app.js:720 (flycheck) — swapped: brackets carry flycheck-delimited-error and the content flycheck-error-delimiter. In flycheck's delimiters highlighting style the delimiter strings get error-delimiter and the enclosed text gets delimited-error; swap them. - -Pin with a browser-gate assertion that these preview elements link the right faces (e.g. the org headline-todo span sits after an org-todo span; the erc my-message line uses input-face). -Fixed 2026-06-13: org heading three now has an =org-todo= keyword before =org-headline-todo=, flycheck delimiters/content are mapped to =flycheck-error-delimiter= / =flycheck-delimited-error= correctly, and ERC own/remote message text use =erc-input-face= / =erc-default-face=. Added =#previewlinktest= to pin all three mappings. - ** TODO [#B] theme-studio save button does not overwrite the current theme file :bug:studio: :PROPERTIES: :LAST_REVIEWED: 2026-06-13 @@ -2998,14 +2855,6 @@ Tentative follow-up from walking through the generator algorithms. Consider spli This may be cancelled if the extra distinction makes the generator harder to understand. Before implementing, decide final names and whether ground-aware source should include only bg/fg or also ground span steps. -** DONE [#B] theme-studio spans should stop at bg and fg bounds :bug:quick:solo:studio: -CLOSED: [2026-06-15 Mon] -:PROPERTIES: -:LAST_REVIEWED: 2026-06-13 -:END: -Done in commit 25e2d2ad: regenColumn ramps the dark side toward bg and the light side toward fg, bounded by the ground endpoints; pure black/white duplicates still skipped. Node tests + the #counttest bounds assertion pin it. -From the roam inbox: spanning a color should not generate colors beyond the current ground endpoints. No generated color should be darker than =bg= or lighter than =fg=. If the darker side hits =#000000=, do not create duplicate black tiles; if the lighter side hits =#ffffff=, do not create duplicate white tiles. Apply this to normal column spans and ground spans as appropriate, then pin with Node tests for the ramp/column plan and a browser gate for the palette UI. - ** TODO [#B] theme-studio UI face inheritance needs a spec :feature:studio: :PROPERTIES: :LAST_REVIEWED: 2026-06-13 @@ -3035,11 +2884,16 @@ While in here, audit individual leaf chords for other non-TTY keys (any =C-RET=, ** TODO [#B] Unified popup placement and dismissal rules :feature: All transient popups should follow one set of principles. Placement: when the Emacs frame is wider than tall, the popup rises from the right; when square or taller, from the bottom — settle the aspect-ratio threshold and the pop-out percentage. Dismissal: C-c C-c when there's an accept action, C-c C-k when there's a cancel, otherwise =q= closes the window. This generalizes two existing tasks — ai-term adaptive placement (the aspect-ratio docking) and the messenger window/key unification spec (the C-c C-c / C-c C-k dismissal) — into one config-wide policy. From the roam inbox. -** DONE [#B] vertico-prescient clobbers orderless filtering :bug:quick:solo: -CLOSED: [2026-06-13 Sat] -=modules/selection-framework.el:250= — =vertico-prescient-mode= defaults =vertico-prescient-enable-filtering t=, overriding =completion-styles= to prescient inside vertico sessions; the orderless config at :151 is dead exactly where it matters. Set =vertico-prescient-enable-filtering nil= — orderless matches, prescient sorts (and this resolves the dead =vertico-sort-function= finding in the buffer/window-libs child the other way around). From the 2026-06 config audit. -Fixed 2026-06-13: added =:custom (vertico-prescient-enable-filtering nil)= to the vertico-prescient use-package. Live daemon confirmed filtering nil + =completion-styles (orderless basic)= with the mode re-enabled — orderless matches, prescient sorts. No ERT test (framework defcustom, unexercisable in the stubbed-use-package harness); the in-session matching check is a VERIFY under Manual testing and validation. - +** TODO [#C] theme-studio: drop the too-similar-colors message below the palette :refactor:studio: +Remove the too-similar-colors warning under the palette display. It isn't useful there; the same information is reachable per-assignment through the inline contrast field. From the roam inbox 2026-06-15. +** TODO [#C] theme-studio: extract a ground() helper for the repeated bg/fg literal :refactor:quick:solo:studio: +The literal {bg:MAP['bg'],fg:MAP['p']} repeats 21 times across app.js and palette-actions.js, plus more in the browser gates. Extract a ground() helper that returns it and replace every call site. Purely mechanical, no behavior change; the node tests + browser gates are the safety net. From the 2026-06-15 refactor review. +** TODO [#C] theme-studio: raise the max color spans to 5 :feature:studio: +Increase the palette's maximum span count to 5, for a smoother, slower transition across a color. From the roam inbox. +*** VERIFY which control caps below 5 — current maxes are all 8 +On review the per-column span control, the ground span control, and regenColumn all already cap at 8 (well above 5), so there's no sub-5 limit to raise. Either 5 is already reachable, or the intended control is a different one (e.g. the generator emits base-only columns — spanCount is hardcoded 0 in palette-generator-ui.js). Need Craig to point at the control he's hitting. +** TODO [#C] theme-studio: compact the contrast column to a half-filled symbol :solo:quick:studio: +Represent contrast as a colored number colored like the current text. The word passed is unnecessary ** TODO [#C] archsetup Waybar Wi-Fi module should show no-internet state :feature: :PROPERTIES: :LAST_REVIEWED: 2026-06-13 @@ -4416,11 +4270,6 @@ These may override useful defaults - review and pick better bindings: :END: Display slack.el message and thread buffers in a dedicated popup window (side or bottom) and reuse that one window instead of spawning a new window per buffer. Likely a =display-buffer-alist= rule (or popper integration) in =modules/slack-config.el=. -** DONE [#C] Swap buffer delete/diff keys — destructive on capital :refactor:quick:solo: -CLOSED: [2026-06-13 Sat] -=modules/custom-buffer-file.el:515= binds =d= = =cj/delete-buffer-and-file= and =D= = =cj/diff-buffer-with-file=. Destructive commands should be the capital, and diff is the one hit often (when saving a buffer changed on disk). Swap them: =D= = delete, =d= = diff. From the roam inbox. -Swapped 2026-06-13: =cj/buffer-and-file-map= now binds =d= = =cj/diff-buffer-with-file=, =D= = =cj/delete-buffer-and-file=. keymap-lookup test added; live daemon re-bound by hand (defvar-keymap won't reassign a bound var on reload). Keypress check is a VERIFY under Manual testing and validation. - ** TODO [#C] Terminal GPG pinentry Completion :feature: :PROPERTIES: :LAST_REVIEWED: 2026-06-05 @@ -4485,30 +4334,8 @@ Craig, 2026-06-11 manual-test walk: the color picker's background is hard to dis :END: From the roam inbox: the =show= button for the raw JSON export does not fit the main theme-design workflow, but it may still be useful for debugging. Decide whether to hide it behind a debugging affordance, rename it, or remove it. Quick UI cleanup once the desired debugging surface is chosen; not marked solo because it is a workflow preference call. -** DONE [#C] theme-studio: remove redundant reset button on package faces :refactor:quick:studio: -CLOSED: [2026-06-15 Mon] -Removed the per-row reset column (package faces was the only tier with one). It was not equivalent to the bulk reset next to "lock all": per-row reset one face, bulk resets every unlocked face. Single reset path now, matching syntax/ui tiers. Commit 7a7b1c16. -** DONE [#C] theme-studio: rename preview sample names :feature:quick:solo:studio: -CLOSED: [2026-06-15 Mon] -Renamed the preview personas Alice→Christine and Eve→Evan across the mu4e/signel/telega previews, with the mu4e header spacing adjusted to stay aligned. Commit 44128931. ** TODO [#C] theme-studio: restrict the cursor row to its background :bug:studio: The UI table gives the cursor face the full control set (fg, B/I/U/S, box), but Emacs only honors the cursor face's :background. Its shape is cursor-type, not a face attribute, so every other control on that row is a no-op once the theme loads. Restrict the cursor row to just its background swatch so the studio doesn't present controls Emacs drops. -** DONE [#C] theme-studio Rust + Zig language previews :feature:studio: -:PROPERTIES: -:LAST_REVIEWED: 2026-06-11 -:END: -Requested by Craig 2026-06-11: add Rust and Zig code samples to the language previews (samples.py currently carries Elisp, Go, Python, TypeScript, Java, C, C++, Shell). Each sample should exercise the treesit token categories distinctive to its language (Rust: lifetimes, macros, attributes, traits; Zig: comptime, builtins, error unions), then regenerate theme-studio.html and extend the test surface. - -Shipped 2026-06-13: Rust and Zig were added to =samples.py= and =generate.py=, with generator tests pinning the language-specific category coverage. - -** DONE [#C] theme-studio separate tile selection from name editing :feature:quick:solo:studio: -:PROPERTIES: -:LAST_REVIEWED: 2026-06-13 -:END: -From the roam inbox: clicking a palette tile should have predictable intent. Single-click anywhere on a tile should select the whole color tile for editing/assignment. Double-clicking the name should enter name-edit mode with the cursor at the beginning of the name. Add browser-gate coverage for both paths so accidental single-click name edits do not regress. - -Shipped 2026-06-13: tile names are read-only until double-clicked; single-click selects the tile, and =#columntest= pins single-click vs double-click behavior. - ** TODO [#C] theme-studio terminal/ANSI colors :feature:studio: theme-studio represents GUI faces only; terminal colors aren't surfaced at all. Scope decided 2026-06-09: GUI-first faces, NOT full per-face display-class fallback. Two pieces: @@ -4573,10 +4400,6 @@ Findings from the 2026-05-20 investigation: navigation commands. - Live experiment scratch file: =~/dashboard-overscroll-experiment.el=. -** CANCELLED [#D] Desktop quick-capture: Note + Recipe types :feature:solo: -CLOSED: [2026-06-15 Mon] -Superseded 2026-06-15: the desktop popup was simplified to a single Task into the org-roam inbox (no Bug/Event, no template menu), so adding Note/Recipe types to the popup subset no longer applies. - ** Emacs Packages — Curl-Friendly Web Service Wrappers Ideas for new Emacs packages following the same pattern as wttrin: HTTP GET to a simple web service, render results in a buffer, optionally show summary in the mode-line. All of these share the async fetch + caching infrastructure already proven in wttrin. Captured On: [2026-04-04 Sat] @@ -8411,3 +8234,170 @@ CLOSED: [2026-06-12 Fri] Relocated from the global capture inbox 2026-06-06. When inside a projectile project, C-c c t (Task) files into that project's root todo.org under the "<Project> Open Work" header. If the project has no todo.org, fall back to the global inbox-file and warn naming the project. Implemented 2026-06-06 in =modules/org-capture-config.el=: a shared project-aware =function= capture target (=cj/--org-capture-project-location=) used by =C-c c t= (Task, =* TODO=) and a new =C-c c b= (Bug, =* TODO [#C]=). Matches an existing top-level "... Open Work" heading (so ~/.emacs.d hits "Emacs Open Work") and creates "<Capitalized project> Open Work" only when absent. Outside a project / no todo.org -> global inbox under "Inbox" (with a warning in the no-todo.org case). 15 ERT tests in =tests/test-org-capture-config-project-target.el=; daemon e2e confirmed a real capture lands "** TODO [#C] ..." prepended under Open Work. Manual verify filed under the Manual testing and validation parent. NOTE: the matching "<Project> Resolved Work" header for the wrap-up workflow is a separate concern, not handled here. +** DONE [#A] theme-studio: 2D gallery color picker for assignment dropdowns :feature:studio: +CLOSED: [2026-06-15 Mon] +Replaced the per-face color dropdown (mkColorDropdown popup in app.js) with a 2D grid in the palette-panel shape: galleryModel(cur,palette,ground) in app-core.js (pure; reuses columnsFromPalette) returns a default chip, an optional (gone) cell, and rows = ground strip then one row per family (members dark->light, one selected). 5 node tests + #gallerytest browser gate. Trigger and ‹ › step buttons unchanged; applies to all three tiers. From the roam inbox 2026-06-15. +** DONE [#C] theme-studio: palette display toggle for base colors vs full palette :feature: +CLOSED: [2026-06-15 Mon] +Added an arrow control on the palette: right collapses each column to its base color (ground steps collapse to bg/fg too), down shows the full spans. #paltoggletest covers it. Commit 5ab506d9. +** DONE [#A] theme-studio: flag gone color assignments with a distinctive border :feature:studio: +CLOSED: [2026-06-15 Mon] +Swatches whose assigned color resolves to "(gone)" now carry a solid red outline (distinct from the dashed unused-tile flag). #gonetest covers it. Commit 0529189a. +** DONE [#A] theme-studio: flag unused palette tiles and columns :feature: +CLOSED: [2026-06-15 Mon] +usedPaletteHexes reverse-lookup over syntax/ui/package assignments + ground; a tile referenced nowhere gets a dashed outline, an all-unused column gets a dashed box. Biased safe (never flags a used color). Node tests + #unusedtest. Commit 7e7b871f. +** DONE [#C] theme-studio: equalize style and box button cluster sizing :refactor:quick:studio: +CLOSED: [2026-06-15 Mon] +Shrank .sbtn from 26x24 to the 17x15 box-button size (font 13px), so the style and box clusters match and the row returns to roughly its pre-cluster height. Commit 44128931. +** DONE [#A] Face and font diagnostic popup at point :feature: +CLOSED: [2026-06-15 Mon] +Read-only popup diagnosing why text at point paints as it does (face stack by source, merged attributes, real font vs declared family, theme/config/inherit provenance). Spec: [[id:98f065cf-8bd5-46a0-ac24-da94d66855ad][face-font-diagnostic-popup-spec-implemented.org]]. Building in modules/face-diagnostic.el: pure core cj/--face-diagnosis-at returns the report plist; cj/describe-face-at-point renders it into a read-only help buffer. From the roam inbox — "do this one first." +*** 2026-06-15 Mon @ 12:19:41 -0500 Phase 1 — core read model + buffer classifier landed +modules/face-diagnostic.el: cj/--face-diagnosis-at returns groups 0-2 (buffer classification, character context, face stack by source) via small pure helpers. 17 ERT tests (tests/test-face-diagnostic.el), byte-compile clean. Not yet wired into init.el; the interactive command and keybinding land in Phase 4. +*** 2026-06-15 Mon @ 12:26:52 -0500 Phase 2 — merged attributes + real font landed +cj/--face-diag-merged-attributes folds the ordered, remap-expanded spec stack ("computed"); cj/--face-diag-real-font reports font-at or "unavailable" under batch. Settles spec decision #7 (hand-fold, tested on overlay-over-text-prop, default-remap, and face-symbol fixtures). 23 ERT tests total, byte-compile clean. +*** 2026-06-15 Mon @ 12:30:30 -0500 Phase 3 — provenance trace landed +cj/--face-diag-provenance returns per-face provenance: themes from theme-face, config from saved/customized-face, the :inherit chain, and the attributes still unspecified that fall to the default. Version-sensitive internals sit behind small tolerant accessors. 30 ERT tests total, byte-compile clean. +*** 2026-06-15 Mon @ 12:37:16 -0500 Phase 4 — render + popup wiring landed +cj/describe-face-at-point renders the diagnosis into the read-only *Face Diagnosis* buffer (cj/face-diagnostic-mode), with region-scan mode and an out-of-scope banner; required in init.el; live-verified in the daemon (it already surfaces the auto-dim remaps). Command name settled as cj/describe-face-at-point. Deferred to follow-up: clickable face-name buttons (plain text for now) and the module-header allowlist entry; the keybinding is Craig's to pick. +** DONE [#B] dwim-shell: zip overwrites its own name, backup timestamp never expands, dired menu key dead :bug:quick:solo: +CLOSED: [2026-06-13 Sat] +From the 2026-06 config audit, =modules/dwim-shell-config.el=: +- =:338= — single-file zip is =zip -r '<<fne>>.<<e>>' '<<f>>'= — reconstructs the input filename as the archive ("Zip file structure invalid"; directories produce =foo.=). Should be ='<<fne>>.zip'= like the tar-gzip sibling. +- =:549= — backup destination single-quotes =$(date ...)= so the substitution is literal: =foo.txt.$(date +%Y%m%d_%H%M%S).bak=. Move it outside the quotes or format-time-string in Elisp. +- =:932= — dired-mode binding "M-S-d" is unreachable (Meta+Shift+d generates M-D); the dirvish binding two lines down is correctly "M-D". Fix + the stale commentary at dirvish-config.el:30. +Fixed 2026-06-13: zip single-file template now ='<<fne>>.zip'=; backup uses =format-time-string= in Elisp (real =YYYYMMDD_HHMMSS= stamp, dropped the now-unneeded =date= util); dired key M-S-d→M-D + dirvish-config.el:30 doc corrected. Both command strings extracted into top-level builders (=cj/dwim-shell--zip-single-file-command=, =cj/dwim-shell--dated-backup-command=) so they're unit-testable without the dwim-shell-command package — the command defuns live in its use-package :config, which the batch harness doesn't load. 2 builder tests green in make; live daemon confirms all three (backup stamp, .zip, dired M-D). Real backup/zip run + the dired keypress are a VERIFY. +** DONE [#B] ERC: double mention notifications + tautological server list :bug:quick:solo: +CLOSED: [2026-06-14 Sun] +From the 2026-06 config audit, =modules/erc-config.el=: +- =:281= — =erc-modules= includes the built-in =notifications= module AND :config adds =cj/erc-notify-on-mention= to the same hook — every mention fires two desktop notifications. Pick one path (keep the custom one, slated for messenger unification). +- =:100= — =cj/erc-connected-servers=: inside =with-current-buffer=, the free =erc-server-process= is the buffer's own local value, so the eq test is tautologically true — returns ALL ERC buffers (channels, dead connections). Use =erc-server-buffer-p= + =erc-server-process-alive=. +- =:238= — =user-whole-name= read at load but =user-constants= only required at compile time (same trap as auth-config/keyboard-macros). +Fixed 2026-06-14: removed =notifications= from =erc-modules= (kept the custom =cj/erc-notify-on-mention=, so one notification per mention); rewrote =cj/erc-connected-servers= to filter on =(erc-server-buffer-p)= + =(erc-server-process-alive)= instead of the tautological self-eq; moved =user-constants= to a runtime require. New test-erc-config-connected-servers.el (live-server-only + empty cases) 2 green; module byte-compiles. erc-config not reloaded into the daemon (live IRC session) — takes effect on restart. VERIFY for the one-notification + real-server-list behavior. +** DONE [#B] help-config: three defects in one small file :bug:quick:solo: +CLOSED: [2026-06-13 Sat] +From the 2026-06 config audit, =modules/help-config.el=: +- =:67= — =cl-return-from= inside a plain =defun= (no cl-block): declining the save prompt signals "No catch for tag" instead of canceling. =cl-defun= or restructure. +- =:108= — =:hook (info-mode . info-persist-history-mode)= is dead twice: Info's hook is =Info-mode-hook= (capital I), and =info-persist-history-mode= doesn't exist anywhere. Implement the intent or delete. +- =:111= — auto-mode-alist maps .info to an interactive command that KILLS the buffer mid find-file — programmatic =find-file-noselect= of any .info destroys buffers and pops Info windows. Drop the entry; keep the explicit command. Zero test coverage on this module (the two broken paths are exactly the untested ones). +Fixed 2026-06-13: (1) extracted the save/cancel/open decision into a pure =cj/--info-open-plan= and routed =cj/open-with-info-mode= through it — no more =cl-return-from=, declining cancels cleanly; (2) deleted the dead =:hook= and the empty =:preface=; (3) dropped the destructive =auto-mode-alist= .info entry (kept =cj/open-with-info-mode= as an M-x command and =cj/browse-info-files= on C-h i). New test-help-config.el covers the planner (open / save-then-open / cancel) — 3 green; module loads clean. Stale daemon state (the .info auto-mode entry + the bogus info-mode-hook entry) cleared by hand so the running session is correct without a restart. Interactive Info open + find-file-no-longer-destructive are a VERIFY. +** DONE [#B] markdown live preview clobbered by markdown-mode :bug:quick:solo: +CLOSED: [2026-06-13 Sat] +=modules/markdown-config.el:54= defines bare =markdown-preview=, which markdown-mode redefines the moment the first .md loads — the impatient-mode live preview is dead and F2 silently runs the package command (agent verified in the live daemon). Also =:61= guards on =(boundp 'httpd-process)=, a variable that doesn't exist in simple-httpd — use =(httpd-running-p)=. And the =:config= =(setq imp-set-user-filter 'markdown-html)= at line 41 is doubly dead (function-not-variable, symbol names nothing) — delete. Rename to =cj/markdown-preview=, rebind F2. From the 2026-06 config audit. +Fixed 2026-06-13: renamed the defun to =cj/markdown-preview= and rebound =<f2>=; guard is now =(httpd-running-p)=; deleted the dead =(setq imp-set-user-filter 'markdown-html)= (impatient-mode use-package is now =:defer t= only). Added a guard test (server-down → user-error); 4/4 green; live daemon confirms =cj/markdown-preview= defined and guarding. Browser-render check is a VERIFY. +** DONE [#B] modeline runs synchronous git on the redisplay path, unguarded :bug:solo: +CLOSED: [2026-06-14 Sun] +=modules/modeline-config.el:173,154,145= — the mode-line :eval calls vc-backend/vc-state/vc-working-revision (synchronous git) on TTL expiry; a slow or unmounted filesystem stalls ALL redisplay. The cache key computes =file-truename= on every render (the "one stat per refresh" comment is wrong), and nothing is condition-case-wrapped, so a signal lands inside the mode-line eval. Defer the truename behind the TTL check; wrap the fetch in condition-case caching nil. From the 2026-06 config audit. +Fixed 2026-06-14 (Approach A): dropped =file-truename= from =cj/modeline-vc-cache-key= (key is now =(file show-remote)=, no per-render stat; a moved symlink is caught at the next TTL refresh when vc-backend resolves the link fresh). Wrapped =cj/modeline-vc-fetch= in =condition-case ... (error nil)= so a git signal on a slow/unmounted FS degrades to no-VC-info instead of breaking redisplay. Rewrote the two truename cache-key tests to assert the cheap key; added a fetch-swallows-vc-errors test. 9 modeline vc tests green; live daemon confirms the 2-element key and nil-on-error fetch. +** DONE [#B] org-faces: custom header-row face layer + theme-studio app :feature:theme-studio:spec: +CLOSED: [2026-06-15 Mon] +Named, theme-agnostic faces for org TODO keywords and priorities, wired via org-todo-keyword-faces / org-priority-faces, plus a dedicated theme-studio "org-faces" app beside elfeed and mu4e. Spec: [[id:35578114-8c29-43af-97a2-fdfea01a802e][org-faces-spec-implemented.org]]. All four decisions resolved (prefix org-faces-, real defface defaults, auto-dim repointed to org-faces-*-dim, all 10 keywords). Phase 1 (modules/org-faces-config.el + 5 ERT tests), Phase 2 (auto-dim-config.el repoint), Phase 3 (theme-studio org-faces app) all landed and verified; the build-theme round-trip is confirmed mechanically. Residual visual check is a VERIFY under Manual testing and validation. +** DONE [#B] slack-config lifecycle gaps :bug:quick:solo: +CLOSED: [2026-06-14 Sun] +From the 2026-06 config audit, =modules/slack-config.el=: +- =:265= — w / @ / # bound to commands neither autoloaded nor in :commands — void-function before slack loads. Add to :commands. +- =:246= — =cj/slack-close-all-buffers= reads =slack-current-buffer= (declared but unbound) without the boundp guard its sibling has — void-variable on C-; S Q before slack loads. +- =:259= — raw =global-set-key= for C-; S bypasses =cj/register-prefix-map= (signal/erc use it); invisible to the keybindings registry and the planned unification enumeration. +Fixed 2026-06-14: added =slack-message-write-another-buffer=, =slack-message-embed-mention=, =slack-message-embed-channel= to =:commands= (w/@/# now autoload); guarded =cj/slack-close-all-buffers= with =buffer-local-boundp= (no void-variable on C-; S Q before slack loads); switched =global-set-key= to =(cj/register-prefix-map "S" cj/slack-keymap "slack")= (+require keybindings). New test-slack-config-close-all.el green; module loads, C-; S registered in the registry. Not reloaded into the live daemon (active slack session) — restart to apply. VERIFY for the pre-load key safety. +** DONE [#B] theme-studio color columns :feature:studio: +CLOSED: [2026-06-13 Sat] +:PROPERTIES: +:LAST_REVIEWED: 2026-06-11 +:END: +Show the palette as hue-grouped strips (dark→light) over the existing flat, individually-editable palette. Grouping is by OKLCH hue from the hex, so renaming a color never moves it. A per-strip count control generates a symmetric ramp (N → base ±N) from the strip's most-saturated color; regenerate is authoritative, repointing surviving-step references by lightness rank and leaving removed-step references a visible "(gone)". The ground strip is synthesized from the bg/fg assignments and pinned first; the standalone ramp panel is removed. Designed in [[file:docs/theme-studio-color-families-spec.org][docs/theme-studio-color-families-spec.org]]. Codex-reviewed Ready 2026-06-10 after response folded: pivoted from name-derived families to hex-derived families over a flat palette, which designs out the name-grammar/import-inference and chip-ownership blockers. All review findings dispositioned; both open decisions resolved. Builds on and supersedes the palette-ramps v1 ramp UI. + +All six phases landed 2026-06-10 (commits ebe18d51, 74db9a52, 111687b0, e7ae18c4, 77783126, f6ab0001, 9daeff15, and the Phase 6 commit); =make theme-studio-test= green (98 node tests, 16 browser gates). Code-complete and self-verified. The hue-adjacent warm-color grouping limitation is filed as a separate research task (=~/color-sorting.org=). Remaining: the manual aesthetic/fidelity sign-off under the Manual testing parent (hue grouping reads right, regenerate-replace reads as deliberate, removed-step "(gone)" is clear). Mark this DONE once that passes. +Retired 2026-06-13: the current implementation no longer uses color-derived hue families. Palette entries carry stable structural column ids, generated colors stay in their originating column, renames do not move tiles, and =#columntest= / =#roundtriptest= pin the behavior. +*** 2026-06-10 Wed @ 01:17:45 -0500 Family model core landed +Phase 1 (commit =ebe18d51=, grouping reworked in =77783126=). =familiesFromPalette=, =regenFamily=, =rankByLightness=, =stepRepointPlan= in app-core.js, pure and hex-derived. Grouping started as gap-clustering + flat neutral threshold; after the design discussion it became nearest-hue-anchor bucketing (no single-linkage chaining) + a lightness-scaled neutral threshold (pale tints keep their hue, mid grays go neutral). regenFamily handles n=0 without ramp()'s clamp; stepRepointPlan maps survivors / lists removed by signed lightness rank. 20 node tests including the green/yellow split and the no-chaining case. Open: hue-adjacent warm colors still merge — research task above (=~/color-sorting.org=). +*** 2026-06-10 Wed @ 01:17:45 -0500 Family sort core landed +Phase 2 (commit =74db9a52=). =sortFamilies=/=sortFamilyMembers=: neutrals first, then chromatic by base hue (rounded so a hue hair doesn't outrank lightness), ties by base lightness then hex; members dark→light. Display-only; stored palette order untouched. 4 node tests. +*** 2026-06-10 Wed @ 01:17:45 -0500 Family-strip rendering landed +Phase 3 (commit =111687b0=, columns =e7ae18c4=). renderPalette restructured into the pinned ground strip + hue-sorted family columns (top→bottom dark→light), chips keep per-chip rename/remove/select, move-arrows/drag dropped. #familytest gate locks the structure + rename-stays-in-strip. Existing palette flows stay green. +*** 2026-06-10 Wed @ 01:17:45 -0500 Count control + regenerate landed +Phase 4 (commit =f6ab0001=). Per-chromatic-strip count input (0-4); setting N regenerates the family as base ±N, repointing survivor references by lightness rank and leaving removed-step references on their now-gone hex. Also fixed the neutral-threshold curve to taper at both lightness ends (symmetric Munsell) so chroma-eased dark/light extremes keep their hue. #counttest gate covers count up/down + the survivor/removed reference behavior. +*** 2026-06-10 Wed @ 01:17:45 -0500 Base edit + retire ramp panel landed +Phase 5 (commit =9daeff15=). Editing a family base recolors the whole family (shared =regenFamilyInPlace= with the count control); editing a ground swatch writes the bg/fg assignment. The standalone ramp panel (button, panel, JS, CSS, #ramptest) is removed — fan a color via its column's count instead. #baseedittest gate covers base-edit recolor + reference follow + the bg-swatch edit. +*** 2026-06-10 Wed @ 01:17:45 -0500 Warnings, seeding, export, README close-out landed +Phase 6 (commit =c175e2be=). Export stays a flat palette and import needs no reconstruction (#roundtriptest: export→import→export byte-identical). =seedPkgmap= reads the flat palette unchanged. The too-similar warning stays on the full palette — the planned ramp-step exemption was dropped after analysis: ramp steps are a stepL apart (well above the ΔE threshold) so they never warn, and exempting same-family pairs would hide genuine near-duplicates (caught by #deltatest). README documents families, the ground strip, the count control/regenerate, removed-step references, and the ramp-panel removal. +** DONE [#B] theme-studio delete entire color column :feature:quick:solo:studio: +:PROPERTIES: +:LAST_REVIEWED: 2026-06-13 +:END: +Add a column-level delete affordance to the palette. Deleting a normal color column removes the base color and every generated/imported tile in that column from the screen and from =PALETTE=. Ground remains pinned and non-deletable. Existing assignments that referenced removed tiles should follow the current deleted-color behavior: they remain on the old hex and appear as recoverable =(gone)= values rather than silently repointing. + +Acceptance notes: add a browser gate proving a column delete removes all entries with that stable =columnId=, leaves other columns alone, leaves ground non-deletable, and preserves any references to deleted hexes as gone values. + +Shipped 2026-06-13 in commit =2cf730d5=: normal column headers have a delete button, ground has no delete affordance, deleted names feed =lastGone= for recovery, and =#columntest= pins the behavior. +** DONE [#B] theme-studio palette ramps + contrast safety v1 :feature:studio: +CLOSED: [2026-06-13 Sat] +:PROPERTIES: +:LAST_REVIEWED: 2026-06-10 +:END: +The v1 build from [[file:docs/theme-studio-palette-ramps-spec.org][theme-studio-palette-ramps-spec.org]] (Ready, Codex-reviewed). Two coupled features: a ramp generator (one base color → harmonized tonal ramp) and background-contrast safety (worst-case floor over a face's foreground set + safe-lightness guidance). + +All five phases + the README close-out landed 2026-06-09 (commits 1d51a332, 9da6c663, e7021bfe, 1d8b9f9e, 843bbf08, 23926837); =make theme-studio-test= green (78 node tests, 12 browser gates). Code-complete and self-verified. Remaining: the aesthetic and real-Emacs-fidelity sign-off under the Manual testing parent below (does a ramp harmonize, does the safe band read clearly, does a "safe" tint actually read behind real syntax). Mark this DONE once that passes. +Retired 2026-06-13: this parent is no longer active planning work. The useful pieces are already in the current tool, and later structural-column work replaced the standalone ramp workflow. +*** 2026-06-09 Tue @ 18:40:20 -0500 Ramp generator core landed +Phase 1 (commit =1d51a332=). =ramp(baseHex, {n, stepL, chromaEase})= in app-core.js → ={steps: [{hex, clamped, offset}], adjusted}= or ={steps: [], error: 'bad-hex'}=. Holds the OKLCH hue, steps lightness by =stepL=, quadratic chroma-ease toward the extremes, gamut-clamps each step; knobs clamp to range with the clamped knob named in =adjusted= (n=2/stepL=0.08/chromaEase=0.5 defaults). 10 node tests (mid/near-white/near-black bases, hue-hold, chroma ease, knob clamping, malformed hex), suite 55→65, =make theme-studio-test= green. The app-core integrity stripper now drops =import= lines too. +*** 2026-06-09 Tue @ 19:06:46 -0500 Ramp UI in palette landed +Phase 2 (commit =9da6c663=). A "ramp" button opens a panel that generates from the current color and previews the steps (named per source swatch, clamp badge on out-of-gamut steps); the n/stepL/chroma-ease controls default to 2/0.08/0.5. Click a step or "add all" to insert adjacent to the source in -n..+n order; name collisions skip (no overwrite), hex duplicates add with a flag. New #ramptest gate pins count, ordered insertion, collision skip, and the clamp badge. Verified headless + screenshot. +*** 2026-06-09 Tue @ 18:53:16 -0500 Foreground-set + floor + L_max core landed +Phase 3 (commit =e7021bfe=). =fgSetFor=, =floor=, =lMax= and the =COVERED_FACES= constant in app-core.js, all pure and explicit-state. fgSetFor returns {set:[{hex,label}]} or a structured reason ('out-of-scope'/'empty'); floor returns {ratio, limitingHex, limitingLabel}; lMax scans L from black to bracket the dark-side crossing then binary-searches it (tol 0.001), status ok/none/all/clamp. 13 node tests including the keyword-blue #67809c fixture and lMax's none/all/clamp branches. Suite 65→78, =make theme-studio-test= green. +*** 2026-06-09 Tue @ 19:06:46 -0500 Worst-case contrast readout landed +Phase 4 (commit =1d8b9f9e=). The five covered overlay faces show the worst-case floor over their foreground set (live syntax colors + default fg) and name the limiting foreground; a syntax-color edit repaints them. Out-of-scope faces keep the single-pair cell; an empty set reads "no fg set". Verdict is WCAG AA by default. New #contrasttest gate pins the readout, the keyword-blue limiting case, the single-pair fallback, and the no-set string. +*** 2026-06-09 Tue @ 19:06:46 -0500 Safe-lightness picker guidance landed +Phase 5 (commit =843bbf08=). The OKLCH picker gets a "safe for" selector over the covered faces; the C×L plane shades the lightness band too light to keep that face readable, with the L_max ceiling (via =lMax= at the current chroma) as the band's lower edge. A too-dark foreground shades the whole plane. New #safetest gate pins band-shows-for-covered-face and hides-when-none. Verified headless + screenshot. +*** 2026-06-09 Tue @ 19:06:46 -0500 README + test-surface close-out landed +Commit =23926837=. README documents the ramp controls and defaults, the worst-case floor / limiting foreground, the five covered faces, the safe-lightness guidance, and WCAG-drives-PASS-FAIL with APCA as a diagnostic; the browser-gate list is updated. =make theme-studio-test= carries all new node tests and the #ramptest/#contrasttest/#safetest gates. All acceptance criteria met. +** DONE [#B] theme-studio preview face mislinks (org, erc, flycheck) :bug:quick:solo:studio: +CLOSED: [2026-06-13 Sat] +:PROPERTIES: +:LAST_REVIEWED: 2026-06-11 +:END: +Found by Craig 2026-06-11 during the manual-test walk (org case), then a full audit of all 20 bespoke previews confirmed three mislinks; the rest are clean: + +1. app.js:564 (org) — "Heading three" carries data-face org-headline-todo on a line with no TODO keyword; org-headline-todo's docstring says it applies to the part of the headline after the TODO keyword. Fix: add an org-todo keyword span mirroring the DONE line at app.js:563 (stars = org-level-3, keyword = org-todo, text = org-headline-todo). +2. app.js:765-766 (erc) — swapped: craig's own message text is erc-default-face and bob's is erc-input-face. erc-input-face is "ERC face used for your input"; swap them. +3. app.js:720 (flycheck) — swapped: brackets carry flycheck-delimited-error and the content flycheck-error-delimiter. In flycheck's delimiters highlighting style the delimiter strings get error-delimiter and the enclosed text gets delimited-error; swap them. + +Pin with a browser-gate assertion that these preview elements link the right faces (e.g. the org headline-todo span sits after an org-todo span; the erc my-message line uses input-face). +Fixed 2026-06-13: org heading three now has an =org-todo= keyword before =org-headline-todo=, flycheck delimiters/content are mapped to =flycheck-error-delimiter= / =flycheck-delimited-error= correctly, and ERC own/remote message text use =erc-input-face= / =erc-default-face=. Added =#previewlinktest= to pin all three mappings. +** DONE [#B] theme-studio spans should stop at bg and fg bounds :bug:quick:solo:studio: +CLOSED: [2026-06-15 Mon] +:PROPERTIES: +:LAST_REVIEWED: 2026-06-13 +:END: +Done in commit 25e2d2ad: regenColumn ramps the dark side toward bg and the light side toward fg, bounded by the ground endpoints; pure black/white duplicates still skipped. Node tests + the #counttest bounds assertion pin it. +From the roam inbox: spanning a color should not generate colors beyond the current ground endpoints. No generated color should be darker than =bg= or lighter than =fg=. If the darker side hits =#000000=, do not create duplicate black tiles; if the lighter side hits =#ffffff=, do not create duplicate white tiles. Apply this to normal column spans and ground spans as appropriate, then pin with Node tests for the ramp/column plan and a browser gate for the palette UI. +** DONE [#B] vertico-prescient clobbers orderless filtering :bug:quick:solo: +CLOSED: [2026-06-13 Sat] +=modules/selection-framework.el:250= — =vertico-prescient-mode= defaults =vertico-prescient-enable-filtering t=, overriding =completion-styles= to prescient inside vertico sessions; the orderless config at :151 is dead exactly where it matters. Set =vertico-prescient-enable-filtering nil= — orderless matches, prescient sorts (and this resolves the dead =vertico-sort-function= finding in the buffer/window-libs child the other way around). From the 2026-06 config audit. +Fixed 2026-06-13: added =:custom (vertico-prescient-enable-filtering nil)= to the vertico-prescient use-package. Live daemon confirmed filtering nil + =completion-styles (orderless basic)= with the mode re-enabled — orderless matches, prescient sorts. No ERT test (framework defcustom, unexercisable in the stubbed-use-package harness); the in-session matching check is a VERIFY under Manual testing and validation. +** DONE [#C] Swap buffer delete/diff keys — destructive on capital :refactor:quick:solo: +CLOSED: [2026-06-13 Sat] +=modules/custom-buffer-file.el:515= binds =d= = =cj/delete-buffer-and-file= and =D= = =cj/diff-buffer-with-file=. Destructive commands should be the capital, and diff is the one hit often (when saving a buffer changed on disk). Swap them: =D= = delete, =d= = diff. From the roam inbox. +Swapped 2026-06-13: =cj/buffer-and-file-map= now binds =d= = =cj/diff-buffer-with-file=, =D= = =cj/delete-buffer-and-file=. keymap-lookup test added; live daemon re-bound by hand (defvar-keymap won't reassign a bound var on reload). Keypress check is a VERIFY under Manual testing and validation. +** DONE [#C] theme-studio: remove redundant reset button on package faces :refactor:quick:studio: +CLOSED: [2026-06-15 Mon] +Removed the per-row reset column (package faces was the only tier with one). It was not equivalent to the bulk reset next to "lock all": per-row reset one face, bulk resets every unlocked face. Single reset path now, matching syntax/ui tiers. Commit 7a7b1c16. +** DONE [#C] theme-studio: rename preview sample names :feature:quick:solo:studio: +CLOSED: [2026-06-15 Mon] +Renamed the preview personas Alice→Christine and Eve→Evan across the mu4e/signel/telega previews, with the mu4e header spacing adjusted to stay aligned. Commit 44128931. +** DONE [#C] theme-studio Rust + Zig language previews :feature:studio: +:PROPERTIES: +:LAST_REVIEWED: 2026-06-11 +:END: +Requested by Craig 2026-06-11: add Rust and Zig code samples to the language previews (samples.py currently carries Elisp, Go, Python, TypeScript, Java, C, C++, Shell). Each sample should exercise the treesit token categories distinctive to its language (Rust: lifetimes, macros, attributes, traits; Zig: comptime, builtins, error unions), then regenerate theme-studio.html and extend the test surface. + +Shipped 2026-06-13: Rust and Zig were added to =samples.py= and =generate.py=, with generator tests pinning the language-specific category coverage. +** DONE [#C] theme-studio separate tile selection from name editing :feature:quick:solo:studio: +:PROPERTIES: +:LAST_REVIEWED: 2026-06-13 +:END: +From the roam inbox: clicking a palette tile should have predictable intent. Single-click anywhere on a tile should select the whole color tile for editing/assignment. Double-clicking the name should enter name-edit mode with the cursor at the beginning of the name. Add browser-gate coverage for both paths so accidental single-click name edits do not regress. + +Shipped 2026-06-13: tile names are read-only until double-clicked; single-click selects the tile, and =#columntest= pins single-click vs double-click behavior. +** CANCELLED [#D] Desktop quick-capture: Note + Recipe types :feature:solo: +CLOSED: [2026-06-15 Mon] +Superseded 2026-06-15: the desktop popup was simplified to a single Task into the org-roam inbox (no Bug/Event, no template menu), so adding Note/Recipe types to the popup subset no longer applies. |
