| Commit message (Collapse) | Author | Age | Files | Lines |
| | |
|
| | |
|
| | |
|
| |
|
|
| |
I folded the "Chat buffer placement + exit keys" task into a dated event-log entry under the DOING Signal parent. The work landed in dotemacs 998e9c7a (bottom-30 docking via display-buffer-alist plus tests) and signel-fork df02d79 (pop-to-buffer + signel--cancel-input + C-c C-k binding).
|
| |
|
|
|
|
|
|
| |
I added a display-buffer-alist entry matching "*Signel:" chat buffers and routing them through display-buffer-at-bottom with window-height 0.3. The signel fork's signel-chat now uses pop-to-buffer instead of switch-to-buffer, which is what makes the rule apply. Without that switch the buffer replaces the current window and skips display-buffer entirely.
Two new tests in test-signal-config.el lock the entry shape and the regex's buffer-name match set. A new test-signel-cancel-input.el covers the fork's C-c C-k handler. It clears the editable region between signel--input-marker and point-max, then quit-windows so the buffer survives the dismiss.
Closes the "Chat buffer placement + exit keys" task filed during the 2026-05-28 manual-verify walk.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Walked the open-task backlog twice tonight. The first pass was a content audit (is each task still factually accurate?). The second was a relevance/priority review. Together they surfaced enough drift to be worth landing as one batch rather than dribbling into the next session.
The audit filed three new completion-task parents, each with an audit-finding body and child-task recommendations: F-key Completion (roughly 75% shipped per evidence), Terminal GPG pinentry Completion (no trace of the prior branch on this machine, treat as fresh), and Localrepo Documentation (build is shipped, docs land in three artifacts, four gap-fix tasks spin out as siblings). Headline-indicators-wrap and Buttercup closed DONE, Rework-dev-F-keys cancelled as superseded.
Manual Testing and Validation became its own top-level task with the 10 misfiled verify children moved in. Walk started tonight (tests 1 and 2 verified, two signel bugs surfaced and fixed in the same session), deferred to 2026-05-29 for the message-sending tests.
The Buttercup eval doc captures the rubric I came to during the brainstorm: adopt the moment a project crosses the "test reader is no longer the test author at write-time" threshold. ERT stays the right default until then. None of my projects have crossed yet.
Lint pass resolved all 21 org-lint follow-ups inline rather than letting them accumulate in a hidden inbox: 5 wrong-prefix design-doc links (../docs/design/X.org should have been docs/design/X.org), 4 file:line bare references wrapped in code formatting, 1 timestamp moved out of org-link brackets, 1 nested src block converted to begin_example, the wttrin diagnostic's stale link replaced with a note about where the surviving record lives, 8 markdown-bold patterns converted to org italics, 2 verbatim ** TODO references trimmed so the linter stops misreading them as headings.
|
| |
|
|
|
|
|
|
|
|
| |
cj/signel--ensure-started in modules/signal-config.el was reading signel--process-name in the first branch of its cond before the use-package autoload of signel had fired. The forward-declared (defvar signel--process-name) at L137 silences the byte-compile warning but doesn't actually bind the variable. Its value comes from signel.el's defconst, which doesn't run until signel is loaded.
The first call to cj/signel-connect (C-; M SPC) after Emacs launch produced "Symbol's value as variable is void: signel--process-name" instead of starting the daemon. Surfaced tonight during the manual verify walk of the initiate-message workflow.
I added (require 'signel) at the top of cj/signel--ensure-started so signel loads before any of its variables get read. The require is idempotent, so callers that hit the function after signel is already loaded pay nothing.
The new ERT test test-signal-config-ensure-started-requires-signel-first asserts ordering: require must be the first call inside the function, not just called somewhere. A future refactor that moves the require below the cond would fail this test instead of passing silently.
|
| |
|
|
| |
The wrap-up's sync-child-priority pass bumped two intentionally-deprioritized children of the Signal parent: the [#C] handle-error leak and the [#D] groups vNext. Both are deliberate. The leak is bounded by the stop/start clrhash and the groups task is explicitly post-1:1-stability, so I tagged them :no-sync: to stick at their chosen priorities across future wrap-ups.
|
| |
|
|
|
|
| |
The earlier checklist was one sub-header with all the steps in a single bullet list, which means a partial walk-through can't be marked done piece by piece. Per the manual-testing convention, one sub-header per scenario is the format that lets a failing step promote cleanly to its own bug.
I split the original entry into 9 sub-headers, one per scenario, each with a "What we're verifying" line, the exact key chords or commands, and a single Expected outcome. The set covers connect, picker opens with names, send-a-message, Note-to-Self via the direct command (the spec's D3 critical check), Note-to-Self via the pinned picker entry, the clobber-fix survival test, dashboard, stop, and refresh.
|
| |
|
|
|
|
|
|
| |
initiate-message workflow
I shipped the picker, daemon guard, cache, keymap, and clobber fix as unit-tested code (commits 4daf2328 here, 4740d97 and 5ec56c0 in the signel fork). The unit tests cover branches and contracts but can't exercise a real signal-cli daemon against a linked phone. This checklist captures the live verification I'll walk through on my next restart of Emacs.
Each step lists the exact command or key, the expected observable outcome, and (implicitly) the failure signal: any deviation gets logged under the failing step and promoted to a TODO bug, per the verification convention.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
I built pieces 2-7 of the initiate-message workflow from docs/design/signal-client.org and added tests covering the fork's clobber-fix (commit 5ec56c0 over there). The picker is the user-facing change: a single key opens a name-based completing-read for any contact, with "Note to Self" pinned first.
The picker stack from the bottom up:
cj/signel--ensure-started is the daemon guard. With a live process it's a no-op. With signel-account set but no process it calls signel-start and pre-warms the contact cache. With signel-account nil it user-errors naming the remedy. Pre-warming on start means the picker feels instant on first use.
cj/signel--fetch-contacts issues a listContacts RPC through the new request-callback contract (signel--send-rpc with a success-callback). The callback runs the result through the verified cj/signal--parse-contacts and stores the (LABEL . RECIPIENT) alist in cj/signel--contact-cache, a cj-owned variable kept separate from signel's receive-time contact-map. An empty result populates the cache as nil, distinct from an RPC failure (which never invokes the callback so the prior cache survives). cj/signel-refresh-contacts is the user-facing command that clears and refetches.
cj/signel-message is the picker. Warm cache opens completing-read immediately. Cold cache kicks off a fetch and accept-process-outputs up to cj/signel-fetch-timeout seconds (3s default), then user-errors if the daemon hasn't responded so a wedged process can't hang Emacs. The candidate list pins "Note to Self" first (resolves to signel-account) with a display-sort metadata function that preserves the given order rather than alphabetizing.
cj/signel-message-self skips the picker and goes straight to signel-account. cj/signel-connect is the friendly verb on the prefix key.
cj/signel-prefix-map binds m / s / d / q / SPC and attaches under C-; M via with-eval-after-load keybindings so the binding survives load-order. l stays unbound for the future link command.
15 new ERT tests cover the ensure-started branches, the fetch + cache contract (issued, populated, empty), refresh-contacts, the picker's four scenarios (warm-cache contact, warm-cache Note to Self, cold-cache resolves in time, cold-cache timeout), message-self, and the keymap bindings. Plus 4 new tests in tests/test-signel-input-preservation.el for the fork's clobber fix: pending-input captures typed text and returns nil when empty; both signel--insert-msg and signel--insert-system-msg redraw the prompt without clobbering "halfwritten".
todo.org closes three tasks as dated event-log entries: the contact picker, the input clobber, and the use-package wiring.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The fork commit (4740d97 in the signel fork) added a request-callback table, extended signel--send-rpc with an optional success-callback, and routed result responses through signel--dispatch. These tests lock that contract from the consuming project so a future fork change can't silently break the picker that will read listContacts through it.
Five tests, Normal / Boundary / Error categories plus a reconnect-invalidation case:
- result-invokes-callback (Normal): a result response with a registered id fires the callback with the value and removes the handler.
- send-rpc-registers-success-callback (Normal): passing a success callback stores it under the returned id.
- unknown-id-is-noop (Boundary): a result with no registered id is silent — no receive or error handler fires, map stays empty.
- error-cleans-up-handler (Error): an error response removes the handler without firing the callback, so a retry starts clean.
- stop-clears-handler-map: signel-stop empties the map, so a restart can't replay stale callbacks waiting on responses that will never arrive.
The dispatch tests synthesize JSON alists directly. No live process is needed. The send-rpc test stubs get-process and process-send-string so it doesn't need a running signal-cli.
Refactor audit on signel.el surfaced one unrelated pre-existing smell I'm not fixing here: signel--handle-error reads from signel--request-buffer-map but never remhashes, so error responses leak request-id → buffer-name entries. Filed as a separate [#C] follow-up under the Signal parent task; the maps clear on stop/start so the impact is bounded to a single live session.
todo.org: the dispatch task flips to DOING (the fork commit is in, the test contract is locked) and gets the leak follow-up appended.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
I wrote an initiate-message workflow spec on top of the existing Signal client design doc, covering the keymap, name-based picker, message-to-self, and the whole flow. A follow-up review caught three blockers I'd hand-waved: signel had no JSON-RPC success-result dispatch path (so cj/signel--fetch-contacts couldn't actually receive listContacts results), D4's "auto-connect when linked" didn't define what "linked" meant or how process death surfaced, and the contact cache had no ownership or invalidation story.
I verified the central one against the fork. signel--dispatch handles only "receive" and error responses, so success results were dropped. Then I folded all three into an Architecture additions subsection: a request-callback table keyed by JSON-RPC id with cleanup on success/error/reconnect, a cj/signel--ensure-started contract with branches for live process / account-set / account-nil, and a cj-owned cj/signel--contact-cache separate from signel's receive-time map.
A second review pass caught the remaining sync/async boundary. completing-read is synchronous and the fetch is async. I resolved it with pre-warm on connect plus a bounded accept-process-output fallback for cold caches, so the warm case feels instant and a dead daemon can't hang Emacs.
The follow-up re-review then converged to Ready-with-caveats and surfaced one concrete code finding I'd missed: the #2 input-clobber fix has to cover both signel--insert-msg AND signel--insert-system-msg, since both delete from the prompt line through point-max. The pieces-to-build entry and the prompt-preservation regression test now name both paths.
A few smaller tightenings landed in the same pass. The listContacts assumption is now a researched fact (verified on 94 contacts), the two open questions on account source and fork remote are marked decided (defcustom in the gitignored local config, local checkout with no remote for now), and a forward-flag in the scope summary names the four notification details to spec before that later slice starts.
docs/design/signal-client-review.org carries the review as the closure record. todo.org gets two tasks: a [#B] for the JSON-RPC success-result dispatch (the first build step), and a [#D] for groups in the picker as a vNext after the 1:1 flow is stable.
Spec is Ready. Implementation order is pinned to the Pieces-to-build list. RPC dispatch first, then the guard, then fetch/cache, then the picker and keymap, then the clobber fix.
|
| |
|
|
|
|
|
|
|
|
| |
The *AI-Assistant* side window always opened at a fixed 0.4 width, so resizing it by hand was lost the next time it opened. Now the F-key toggle captures the panel's width when it closes and reopens at that width for the rest of the session, the same way the music playlist remembers its height.
The panel has three entry points that all open the same buffer: the toggle, loading a saved conversation, and escalating a quick-ask. I gave them one shared remembered-width var (cj/--ai-assistant-width, owned by ai-config; the other two forward-declare it to avoid a circular require), so the panel comes back at one consistent width whichever door opens it. Capture lives only in the toggle's close path; the other two just replay.
One latent edge: ai-conversations replays with its configurable side, which defaults to right. If that's ever set to top or bottom, the remembered width fraction would land as a height. It can't happen at the default, so I left it as a known edge rather than complicating the call now.
The escalation test needed a top-level defvar for the shared var: a value-less defvar in the module is only file-local to the byte-compiler, so without it the function read the var dynamically and hit void when the test loaded ai-quick-ask without ai-config. ai-quick-ask 13/13 and ai-conversations 47/47 green.
|
| |
|
|
|
|
|
|
| |
F9 brought the agent window down from the top of the frame. The toggle remembers where the window last sat and replays it, and "above" was a position it could capture and replay: move the window to the top with the buffer-move keys, toggle off, and the next toggle reopened it up there. The host default never picks the top, so a remembered "above" was the only way in.
I added an optional allowed-directions list to cj/window-toggle-capture-state, the helper both F9 (ai-vterm) and F12 (vterm-config) share. When the captured direction isn't in the list, it falls back to the default direction and clears the saved size, since that size was measured on the disallowed axis and wouldn't transfer. Both dispatchers now pass (right below left), so neither can remember a top placement. They go through the same helper, so the rule stays in one place.
Three tests cover the new branch: a permitted direction is kept, a disallowed one falls back with the size cleared, and an omitted list preserves the old unconstrained behavior so existing callers are unaffected.
|
| |
|
|
|
|
|
|
|
|
| |
A folded heading that also has a property drawer renders two glyphs after its right-aligned tag: the org-tidy inline dot (" ·") and the fold ellipsis (" ▾"). The tag-align reserve was 5 columns, and that " · ▾" spilled onto a second screen row, leaving a stray indicator line under the heading.
The trailing glyphs measure 4 columns nominally, so the old 5-column reserve looked like it should fit. It didn't: the fallback triangle renders wider than its reported width and the :align-to stretch rounds, so the real overflow exceeds the column count. I sized the new reserve (9) from a rendered measurement, not arithmetic, and the docstring now says so and tells the next reader to verify by screenshot. That mismatch between column math and what actually renders is what let the earlier reserve ship broken.
I verified the fix by rendering the real config off-screen at full width and reading the result: before, the two folded property-drawer headings wrapped "· ▾" to a second row; after, every heading is a single line.
tests/manual/headline-wrap/ holds the fixture and a README so the check can be repeated by opening the file and looking, which is the only honest way to test a redisplay bug like this.
|
| |
|
|
|
|
| |
Plain left-click on an Org file link keeps org's default of opening in the other window. I added S-mouse-1 and mouse-3 as a second gesture that opens the file link in the current window instead, for when I want the file to replace the buffer the link sits in.
The bindings live in org-mouse-map, the keymap org attaches to each link as a text property, rather than org-mode-map. That layer outranks both org-mode-map and the mouse-trap-mode emulation keymap, which otherwise swallows clicks in org buffers. Off a link the gesture does nothing, so a stray right-click is a silent no-op instead of a "No link found" error.
|
| |
|
|
| |
C-c p d (cj/open-project-daily-prep) always forced the prep doc into another window. I switched it to cj/--find-file-respecting-split, the same helper the sibling C-c p t (project todo) uses: it opens the other window when the frame is split and reuses the current window when it isn't. The two project-open commands now behave the same way.
|
| |
|
|
|
|
|
|
|
|
| |
The F10 music playlist opened at a fixed fraction every time, so any manual resize was lost the moment it was toggled closed. Now the toggle captures the window's size on close and reopens at that height, for the rest of the session.
The mechanism is generic, not music-specific. cj/window-size-fraction (geometry-lib) is the pure kernel: a clamped window/frame ratio. cj/side-window-capture-size and cj/side-window-display (toggle-lib) wrap it for any display-buffer-in-side-window consumer — height for top/bottom, width for left/right — storing the remembered fraction in a caller-supplied state var. It mirrors the direction-split toggle pattern the vterm dispatchers already use, but for atomic side windows that can't be split.
music-config wires F10 to it: cj/music-playlist-window-height is the default, cj/--music-playlist-height holds the remembered value (in-memory, resets each session).
12 new tests across the two libs, Normal/Boundary/Error each covered.
|
| |
|
|
|
|
|
|
| |
I reshaped the Signal client task into a parent with child issues (contact picker, notify behavior, the upstream input-clobber bug, link command, wiring) and recorded the fork decision and the linking milestone as dated entries.
I also filed the headline-indicator wrap bug: the org-tidy dot and the fold ellipsis spill to a second row on property-drawer headings that are folded, because the tag right-align reserve is too small once the heading runs long. The right-align fix itself is intact, so this is a follow-on, not a regression.
The cleanup passes archived the finished daily-prep task and synced three child priority cookies up to their parents.
|
| |
|
|
|
|
|
|
|
|
| |
I'm building a Signal client in Emacs on signal-cli (linked as a secondary device) with a fork of the signel package as the front end. signel is on MELPA but effectively abandoned, and the behavior I want needs internal edits, so owning a fork beats advising a dead package. Full rationale and the rejected alternatives are in docs/design/signal-client.org.
This lands the signal-cli-independent foundation: contact-list parsing for a completing-read picker, and the predicate that suppresses a notification for the chat being actively viewed. Both are pure and unit-tested without a linked account. cj/signal--parse-contacts was corrected against a live account (signal-cli 0.14 puts givenName/familyName at the top level, not under profile), and verified across all 94 real contacts.
The use-package wiring loads the fork from ~/code/signel, sources the account from a gitignored signal-config.local.el (a phone number is an identifier, not a credential, and this keeps it off the mirror without a GPG prompt), and turns off auto-open so an incoming message can't steal a window. Verified live: signel-start spawns the jsonRpc process, loads the account, and receives over the channel.
The fork edits (notify routing, the upstream input-clobber bug) and the contact-picker command are still to come.
|
| |
|
|
|
|
| |
Setting debug-on-quit t during startup turns C-g into a debugger trigger instead of an abort, so the normal "break out of a hang" reflex drops you into a recursive-edit you can't C-g out of. Worse, debug-on-error is also on during startup. An init error pops the debugger before the emacs-startup-hook cleanup runs, and debug-on-quit stays t for the whole session. I hit exactly that: a quit cascaded into a debugger loop that needed killall to escape.
I dropped debug-on-quit from the startup block and removed its now-redundant cleanup line. debug-on-error stays on during startup so init errors are still caught. C-g stays an escape hatch.
|
| |
|
|
| |
I closed the daily-prep keybinding task (shipped in 8e5efcab) and rewrote the popper-config removal into a dated log entry (1cca84c5). I also recorded the researched Signal-client candidates and the Claude-Code-in-eat findings against their tasks, and folded the "no remote install" constraint into the TRAMP task.
|
| |
|
|
| |
assets/M-x_butterfly.png.bak was a tracked duplicate of the banner PNG, an accidental backup that got committed. I removed it and added *.bak to .gitignore so editor and image backups stay out of the tree.
|
| |
|
|
|
|
| |
prog-general uses code-dir, projects-dir, and snippets-dir (all from user-constants) in its projectile and yasnippet configs, but it only declared them as compile-time defvars and leaned on init.el loading user-constants first. That load-order assumption broke when the test suite ran combined: if another test file loaded prog-general before user-constants, yasnippet's :config hit a void snippets-dir and yas-global-mode never enabled, so eight yas-activation tests failed.
I replaced the three compile-time defvars with a runtime (require 'user-constants) so the module declares its real dependency and is self-contained. The combined prog-general suite is now 15/15, and the module no longer depends on init.el's load order for constants it reads at load time.
|
| |
|
|
| |
popper-config was use-package :disabled t, so use-package elided the whole form and none of it ran. It was a no-op sitting in the load graph, required only from init.el. I removed the module and its require. No behavior change. validate-modules passes and init still loads clean. The config stays in git history if popper is ever wanted again.
|
| |
|
|
|
|
| |
I added cj/open-project-daily-prep on C-c p d. It opens inbox/today-prep.org under the Projectile project root in another window, project-scoped, so a project without a prep file just reports it rather than erroring. The binding had only ever been eval'd live into the daemon in a past session and vanished on the next restart, so this persists it to the module.
Freeing d meant reworking the deadgrep bindings. deadgrep-in-dir moves to C-c p G (replacing plain deadgrep, which stays M-x-callable), and deadgrep-here keeps C-c p g. Plain project-wide deadgrep dropped off the projectile prefix because it overlapped the context-aware and arbitrary-directory variants. Tests cover the open, missing-file, and not-in-a-project paths.
|
| |
|
|
|
|
| |
Close the window-placement and tag-alignment work that shipped (Slack, mu4e, dupre tags and the right-align feature), and queue the open follow-ups: the EAT consolidation, the ai-vterm/mu4e/Slack window tickets, and a Signal-client task. Record the researched Signal-client candidates and the Claude-Code-in-eat findings against their tasks, log the decision to remove the disabled popper-config, and fold the "no remote install" constraint into the TRAMP task.
Fix the two invalid org tags (no-sync, which has a hyphen org rejects, so it read as title text) to nosync, and tag the previously untagged GPTel and Signal tasks. Realign every heading's tags to a single space to match org-tags-column 0, which reverts stale column-77 padding that had drifted in.
|
| |
|
|
| |
Research notes weighing the three Emacs terminal backends (vterm, eat, ghostel) on maintenance risk, rendering fidelity, and best-fit role. Referenced by the "Consolidate to EAT" task as the basis for that evaluation.
|
| |
|
|
|
|
| |
mu4e's main view displays with a display-buffer-full-frame action, which tears down every other window on launch, so opening mu4e from a split collapsed the layout. mu4e's own mu4e-display-buffer docstring points to display-buffer-alist as the supported override.
I added an entry routing the *mu4e-main* buffer to the current window (reuse a window already showing it first, then same-window), so launching mu4e in a split keeps the rest of the layout intact. It's registered eagerly rather than inside mu4e's deferred config so it applies on the first launch. Tests cover the entry registration and that the main buffer no longer collapses a split under mu4e's full-frame action.
|
| |
|
|
|
|
| |
slack-buffer-function defaulted to switch-to-buffer-other-window, which gives no real guarantee about placement: with three or more panes it picks a least-recently-used window, and it offers nothing that keeps Slack out of the window point is in. So opening a room in a split could land it wherever, including over the buffer I was working in.
I set slack-buffer-function to cj/slack--display-buffer, a pop-to-buffer call with inhibit-same-window and a reuse / use-some-window / pop-up-window action list. In a split it reuses one of the other windows and leaves the selected window alone; with a lone window it splits. Tests cover both the split-placement case and the selected-window-preserved invariant.
|
| |
|
|
| |
The right-aligned tags stopped one column short of the edge, which kept the line from wrapping but left no room for the folded-heading ellipsis (org-ellipsis, " ▾") that org appends after the tags. The ▾ overflowed onto its own line and showed up as a stray triangle between headings. I widened the margin to 5 columns so the tag and the trailing ellipsis both stay on the heading line.
|
| |
|
|
|
|
| |
org-tags-column only right-aligns tags to a fixed column, and it does so by baking literal spaces into the file, so it can't track window width or splits. I set it to 0 (org keeps a single space, no padding) and add a font-lock rule that stretches that space with a display property pinned to the window's right edge via :align-to. That value resolves at redisplay, so the tags follow the window width and adapt to splits live, and nothing alignment-specific is written to disk.
The tags stop one column short of the edge on purpose. A glyph filling the final column wraps a non-truncated line, which dropped every tagged heading's tags onto the next line in the first cut. org-agenda-tags-column gets 'auto, the native equivalent for agenda views. Tests cover the heading regexp and the align-spec helper.
|
| |
|
|
| |
org-level-1..4 inherit dupre-heading-1..4, which set :weight bold, so org headings rendered bold. I overrode the weight to normal on the org-level faces themselves rather than on dupre-heading-*, so the info-manual, markdown, and shr headings that also inherit those faces keep their bold. I also dropped the bold from org-document-title for consistency.
|
| |
|
|
|
|
| |
The prettify-symbols alist already mapped #+begin_src and #+end_src to a lambda, but only some markers actually rendered as one. prettify-symbols-default-compose-p decides composition from a syntax heuristic on the characters around the match, and inside org's src-block fontification that heuristic vetoed most of the markers. In todo.org only one of five composed.
I added cj/prettify-compose-block-markers-p, a compose predicate that always composes the block markers (case-insensitive, since the alist carries upcased variants) and defers to the default for everything else like lambda. Every marker composes now. Tests cover the marker branch and the deferral to the default.
|
| |
|
|
| |
org-block-begin-line and org-block-end-line carried a bg+1 (#252321) background, so the #+begin_src and #+end_src lines showed a lighter box that matched neither the block body nor the normal background. I pointed both at the theme background so the delimiter lines sit flush, keeping their gray foreground.
|
| |
|
|
|
|
| |
A level-2 heading's whole line carries org-level-2 (height 157). org layers that level face onto a bare TODO keyword, but drops it when the heading has a priority cookie, so the keyword fell back to the body height (143). The same keyword rendered at 157 without a cookie and 143 with one, which read as inconsistent keyword sizes across the Linear and todo views.
I gave org-todo, org-done, and the dupre-org-* keyword and priority faces an explicit :height of 143 so they no longer float with the heading face. The height is absolute on purpose. A float would re-inherit the level height inside the face list and bring the inconsistency back. Keywords now render at the body height everywhere, a step down from the heading title.
|
| |
|
|
|
|
| |
org-todo was intense-red (#ff2a00), the brightest red in the palette. Every TODO-state keyword that isn't in org-todo-keyword-faces falls back to org-todo. So the Linear states (IN-PROGRESS, BACKLOG-PRIORITIZED, and the rest) all rendered in that hot red, and the whole view shouted.
I moved org-todo to red-1 (#a7502d) and org-warning to red (#d47c59). They're distinct now, so an overdue deadline reads a shade warmer than a plain keyword. intense-red stays reserved for the FAILED face.
|
| |
|
|
| |
Bring .claude/hooks/validate-el.sh and .claude/rules/interaction.md back in line with the shared bundle, and add the new keybinding-display.md rule.
|
| |
|
|
|
|
|
|
| |
The butterfly banner is a transparent PNG. On this X11 build Emacs composites image alpha against one background color and caches the flat pixmap. So when auto-dim remaps a non-selected dashboard's background to near-black, the cached image keeps its old composite and the transparent edges show as a lighter rectangle.
I exempted the *dashboard* buffer from dimming through the fork's never-dim-buffer hook, so its background never shifts. Live alpha compositing would need a pgtk build, which is out because of its fractional-scaling input lag, and every theme-level workaround changes dimming for all buffers. Scoping the exemption to one short-lived buffer is the narrow fix. The trade is no focus cue when the dashboard is shown in a split.
I also dropped the :mask heuristic prop from the prior banner commit. The PNG already carries a real alpha channel, so heuristic masking was the wrong tool. Once the background is stable, the native alpha over the theme background reads clean on its own. I added Normal/Boundary/Error tests for the predicate.
|
| |
|
|
| |
The previous #151311 was already the palette's darkest entry. #0d0b0a is about 40% darker again. Both bg and bg+0 move together because bg+0 feeds hl-line, which should stay equal to the background.
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
| |
cj/verify-or-create-dir and cj/verify-or-create-file caught every creation failure and only messaged it, so a broken environment for a path the config actually needs stayed quiet until some later module failed in a more confusing way. I gave both an optional required flag and routed failures through a shared cj/--report-path-failure: a required failure raises a prominent display-warning, an optional one is still just logged so it never blocks startup.
The initializer now groups its paths by that distinction. Required: the backbone directories (sync, org, roam) and the calendar stubs (gcal/pcal/dcal), since org-agenda-list hangs prompting for those when they're missing. Optional: the secondary dirs and the content files, each populated by its own workflow. I went with a warning rather than a user-error for required failures so a directory hiccup surfaces loudly without aborting init.
Added error-path tests: an optional failure logs and never warns, and a required dir or file failure raises a user-constants warning.
|
| |
|
|
|
|
|
|
| |
(require 'user-constants) created ~8 directories and ~10 org/calendar files at load time, via a top-level dolist for the calendar stubs and a top-level call to cj/initialize-user-directories-and-files. That meant any bare require — tests, byte-compile, batch tools — wrote to disk. It's why a stray sync/org/ tree kept appearing in the repo during test runs.
I removed both top-level forms and folded the gcal/pcal/dcal creation into the initializer. The path defconsts stay exactly as they were, so every consumer that just reads a path is unaffected. init.el now calls the initializer right after requiring the module, guarded by (unless noninteractive), so interactive and daemon startup create everything in the same order as before while a bare require stays side-effect-free.
Added tests/test-user-constants.el: loading the module creates nothing, and the initializer creates the backbone dirs and the configured files. Updated the module header — top-level side effects are now none and it's safe to load in tests.
|
| |
|
|
| |
hardening tasks
|
| |
|
|
| |
custom-file points at a throwaway temp file so Customize edits never persist — deliberate, since config lives in Elisp, but silent. A user who clicks "Save for Future Sessions" loses the edit on exit with no hint. I advise custom-save-all (the chokepoint both customize-save-variable and the Customize button funnel through) with a one-shot :before warning that explains the discard and points at the Elisp init files. The advice removes itself after firing, so it warns once per session, and the body never runs at load, so startup stays quiet.
|
| |
|
|
| |
The eshell SSH-jump aliases (gocj, gosb, gowolf) were hardcoded inline in the alias setup, which tied the module to my machines. I moved them into a cj/eshell-ssh-hosts defcustom (an alias→remote-path alist, defaulting to my current hosts) and build the aliases by iterating it. A different machine can override the variable or set it to nil instead of editing the module. Extracted a pure cj/--eshell-ssh-alias-commands helper so the alias construction is testable without a live eshell.
|
| |
|
|
|
|
| |
Two font-config robustness fixes. The font-browser (cj/display-available-fonts) hardcoded a "Light Blue" foreground for each family label, which goes nearly unreadable on a light theme. I switched it to font-lock-keyword-face so the label follows the theme's contrast, keeping it bold.
The emoji-fontset cond ran once at module load behind (env-gui-p). In daemon mode there's no GUI frame at load, so env-gui-p is nil and the fontset never gets set — a later emacsclient -c GUI frame then has no emoji font. I wrapped it in cj/setup-emoji-fontset (GUI-guarded, idempotent) and, mirroring how the fontaine preset is already applied, run it from server-after-make-frame-hook in daemon mode and directly otherwise. The daemon TTY-then-GUI path can't be exercised in batch, so I left a manual-test entry for it.
|
| |
|
|
| |
tasks :discuss:
|