| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
| |
Two pickers have bare-name candidates worth a category: the Signal recipient picker, where a name maps to a phone or UUID, and org-contacts find, where a name maps to an email. I tag each with a custom category and a table annotation-function that shows the looked-up value. marginalia has no annotator for these custom categories, so it leaves the table's annotation in place. The other Tier-2 candidates from the survey (ai-term projects, the mu4e contact list) already carry their info inline, so a category adds nothing and I left them bare.
|
| |
|
|
|
|
| |
Separate the 27 formal specs from working notes. Specs move to docs/specs/, notes stay in docs/design/. Each spec carries its lifecycle in the filename (-spec, -spec-doing, -spec-implemented, -spec-superseded) plus an authoritative ID and STATUS property drawer. The status came from checking each spec against the code, not the doc's own field: 6 implemented, 8 in progress, 12 not started, 1 superseded.
Inbound links become org-id links so future status renames don't break them; code-comment paths repoint to docs/specs/. Working notes, inventories, reviews, and brainstorms stay in docs/design/.
|
| |
|
|
| |
Incoming messages now notify through cj/signel--notify, installed as the fork's signel-notify-function. It suppresses the toast while that chat is in the selected window of a focused frame, collapses and truncates the body to 120 characters, and sends through the notify script (info type, --silent unless cj/signel-notify-sound is set). Without the script on PATH it falls back to notifications-notify and warns at load. The decisions are in the Notification slice addendum of docs/design/signal-client.org.
|
| |
|
|
|
|
|
|
| |
The C-; M Signal prefix didn't take effect on a fresh Emacs launch. signal-config.el was the only feature module that bound into cj/custom-keymap directly, wrapped in (with-eval-after-load 'keybindings (when (boundp 'cj/custom-keymap) ...)). The boundp guard turned a load-order miss into a silent no-op, so the binding never landed at startup. A later live-reload always papered over it because keybindings was loaded by then.
I switched to the documented cj/register-prefix-map helper and added (require 'keybindings) at the top, matching every other prefix map. The require guarantees keybindings loads before registration, so the guard is gone.
I verified at a full emacs --batch init.el launch, the actual failing scenario, that C-; M resolves to the signel prefix. I added a contract test asserting the registration, since the boundp guard was robust under unit timings and only failed at full launch.
|
| |
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
|
|
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.
|