aboutsummaryrefslogtreecommitdiff
path: root/modules
Commit message (Collapse)AuthorAgeFilesLines
* fix(dev-fkeys): run javascript test files through the npx runnerCraig Jennings18 hours1-2/+3
| | | | The language detector classifies js/jsx and the test-file detector recognizes JS tests, but the dispatch had no javascript arm, so C-F6 on a JS test errored though the typescript npx path runs it fine. javascript now shares that arm.
* fix(calendar): skip short months for plain monthly-on-the-31st rulesCraig Jennings18 hours1-2/+18
| | | | The stepper kept day-of-month verbatim, so Jan 31 stepped to Feb 31 and encode-time normalized it into a phantom Mar 3 occurrence. Plain monthly rules now advance to the next month that has the day, per RFC 5545.
* fix(comments): render padded dividers at LENGTH exactlyCraig Jennings18 hours1-0/+5
| | | | available-width omitted the doubled semicolon and the space after comment-start that the emit path adds, so dividers rendered LENGTH+2 (elisp) or LENGTH+1 wide. The budget now mirrors every term min-length already counts.
* fix(music): queue all dired-marked files and de-anonymize header adviceCraig Jennings18 hours1-5/+16
| | | | | | cj/music-add-dired-selection gated dired-get-marked-files behind use-region-p, so files marked with m fell to the single-file branch and only the point file was queued. dired-get-marked-files already honors marks, a region, or point on its own. The header-refresh advice on the four EMMS toggles was an anonymous lambda, which can't be advice-removed and stacked a copy on every :config reload. It's now a named function installed remove-then-add.
* fix(undead-buffers): keep C-u on wrapper commands from spamming the undead listCraig Jennings18 hours1-5/+11
| | | | The delegated cj/kill-buffer-or-bury-alive reads current-prefix-arg, so a prefixed wrapper call took the add-to-undead-list branch for every buffer: nothing killed, list spammed. The four wrappers now bind the prefix to nil around the delegate.
* fix(dashboard): make the bookmarks override realCraig Jennings18 hours1-24/+33
| | | | The top-level defun was clobbered when use-package reloaded dashboard-widgets, so the override never ran. Deferring it kept it alive but exposed a second bug: dashboard-insert-section is a macro, unknown at module compile time, so the body compiled as a function call and crashed void-variable at render. The override now registers after dashboard-widgets and compiles with the macro known.
* fix(org-roam): declare capture-templates special for immediate insertCraig Jennings18 hours1-0/+4
| | | | Without the defvar, the byte-compiled let-binding in cj/org-roam-node-insert-immediate is a dead lexical binding and :immediate-finish never reaches org-roam-node-insert, so the immediate insert opened a capture buffer.
* fix(jumper): handle empty completing-read input without crashingCraig Jennings18 hours1-1/+6
| | | | An unmatched choice made the assoc lookup nil, which crashed the index arithmetic with wrong-type-argument. The jump prompt now gives a user-error and the remove prompt cancels.
* fix(external-open): guard looping video open behind executable-findCraig Jennings18 hours1-0/+5
| | | | The command fires via the find-file advice, so visiting a video without mpv on PATH threw an opaque call-process error mid-visit.
* fix(slack): friendly error on reaction command before slack loadsCraig Jennings18 hours1-1/+4
| | | | slack-current-buffer is declared but void until slack.el loads, so a cold C-; S ! signaled void-variable instead of the not-in-a-Slack-buffer message.
* fix(flyspell): escape the + so org src blocks are actually skippedCraig Jennings18 hours1-2/+4
| | | | The skip entry used "#+" (one-or-more #), which matches no real begin_src line, so ispell spell-checked inside every org code block.
* refactor(go): pin grammar revision via struct accessor, not raw indexCraig Jennings18 hours1-2/+3
|
* refactor(mu4e): collapse dead completion conditionalCraig Jennings18 hours1-6/+3
|
* refactor(coverage): replace double-negative empty guard with whenCraig Jennings18 hours1-1/+1
|
* refactor(epub): drop unused col-px binding in nov-center-imagesCraig Jennings18 hours1-2/+1
|
* fix(erc): declare erc-server-process specialCraig Jennings18 hours1-0/+1
|
* fix(auth): declare plstore-open for the byte-compilerCraig Jennings18 hours1-0/+1
|
* fix(dwim-shell): correct flip-vertically docstringCraig Jennings18 hours1-1/+1
|
* feat(agenda): suspend auto-dim while the agenda frame is openCraig Jennings25 hours1-0/+25
| | | | The refresh tick's selection swing marks the working window non-selected, and auto-dim's debounced dim lands after the tick, so the working frame visibly dimmed every five minutes. Spawn now turns auto-dim-other-buffers-mode off (remembering it was on) and closing the frame restores it. A spawn with auto-dim already off touches nothing. It also keeps the standing agenda fully lit instead of dimmed-when-unfocused.
* fix(agenda): hide the refresh tick's selection flickerCraig Jennings25 hours1-2/+14
| | | | The rebuild takes a second or two, and for its whole duration the agenda window was the selected window, so the cursor visibly went hollow in the working frame on every tick. It read as focus theft, though no focus function ever ran. The tick now inhibits redisplay (the synchronous rebuild blocks Emacs anyway, so this costs nothing) and restores selection before redisplay resumes. A tick that would fire during an active minibuffer is skipped.
* fix(agenda): repair the refresh tick, tighten the gutter, allow point motionCraig Jennings25 hours1-4/+27
| | | | | | | | Every 5-minute refresh failed. The F command's general settings carried (org-agenda-sticky t), and those settings are baked into the buffer's series-redo-cmd and re-applied on each redo. With the buffer already existing, org-agenda-use-sticky-p turned true mid-redo and org-agenda-prepare threw 'exit ("use r to refresh") to a catch that doesn't exist inside org-agenda-redo. I removed sticky from the command settings (the spawn wrapper still binds it, which is where the buffer gets its name) and kept the redo path free of sticky, since org-agenda-redo handles the in-place rebuild itself. Verified with a real tick against a live frame: rebuild succeeds, the failure banner clears, the latch resets. The wide gutter between the source column and the item was the global agenda prefix format's 25-char category pad. The F view now sets its own format with a 10-char pad. C-a hit the deny catch-all. I allowlisted read-only point motion (C-a, C-e, C-f, C-b) and isearch (C-s, C-r).
* fix(agenda): keep the agenda frame a single full-height agenda windowCraig Jennings25 hours1-4/+17
| | | | | | | | The frame spawned with the working buffer in its top quarter and the agenda below at 75%. The sticky setting inside the F custom command applies too late to name the buffer, so it rendered as plain *Org Agenda*, and that name matches the 0.75 below-selected display rule in org-agenda-config.el, which split the brand-new frame. I bind org-agenda-sticky and current-window dynamically around the render, so the buffer is *Org Agenda(F)* (no display rule matches it) and it takes the frame's sole window. A delete-other-windows after the render is the belt in case any future rule still splits. The frame is also named "Full Agenda" now so it's recognizable at a glance and WM rules can target it.
* fix(agenda): make C-x C-c close the agenda frame, not the daemonCraig Jennings26 hours1-0/+5
| | | | The catch-all denied it before, and letting the global through would be worse: save-buffers-kill-terminal on a make-frame frame has no client to close, so it kills the daemon. Inside the frame the gesture now runs the frame close.
* fix(agenda): stop the frame's catch-all from swallowing input machineryCraig Jennings26 hours1-18/+45
| | | | | | | | The default-deny [t] binding intercepted far more than Org commands. switch-frame events hit the deny handler, so every focus change into or out of the frame spammed the read-only message and broke frame switching. Mouse wheel, mouse-1 clicks, and the help prefix were dead. And S-<f8>/C-M-<f8> were never bound in the mode map, so the frame's own toggle couldn't close it from inside. I punched the machinery through the catch-all (an explicit nil shadows the [t] default in the same map, so those events fall through to their global bindings) and bound the two frame controls per the spec's allowlist. Two redo-lifecycle bugs rode along, both from org-agenda-mode's kill-all-local-variables: the buffer-local kill-buffer-hook was stripped on the first refresh tick (killing the buffer would orphan the frame), and the failure banner was held in a buffer-local var while its overlay survives erase-buffer, so a banner could never be removed after a later success. The finalize re-enable now re-adds the hook, and the banner is tagged with an overlay property and found by scanning.
* fix(agenda): spawn the agenda frame tiled, not compositor-fullscreenCraig Jennings26 hours2-13/+21
| | | | I dropped (fullscreen . fullboth) from the spawned frame so a tiling WM places it side by side with the working frame. "Fullscreen" was the wrong word for the intent: a normal frame at its full tiled position, not one covering the whole output. The engage-routing and focus logic are unchanged and matter more now, since a task opens in the adjacent working pane.
* feat(agenda): add S-<f8> dedicated fullscreen agenda frameCraig Jennings27 hours2-3/+640
| | | | | | | | I added a standing agenda surface in its own fullscreen frame of the daemon, placeable on its own workspace while the working frames stay untouched. S-<f8> spawns, raises, or closes it. The force-rescan moves to C-M-<f8>. It shows a today-anchored seven-day view and refreshes every five minutes through org-agenda-redo, so the now-line and synced events stay current without re-scanning the file list. A default-deny minor mode keeps it read-only: only navigation, the engage keys (routed to the working frame), and the frame's own controls are allowed. A failed redo falls back to the last-good snapshot with cloned markers, so the frame is never blank or unrestricted. I made it a frame of the daemon rather than a second process, so it shares the live state (calendar-sync, edits, the now-line). There's no startup auto-open, and the Hyprland-managed-window variant is a vNext once this proves out.
* feat(fontaine): rename Coding to Coding M, add Coding L at 14ptCraig Jennings33 hours2-3/+8
| | | | | The Berkeley Mono code ladder is now Coding XS (11pt), Coding M (13pt), Coding L (14pt), and Coding XL (16pt).
* feat(ai-term): M-SPC cycles attached agents, M-S-SPC cycles allCraig Jennings33 hours3-29/+74
| | | | | | | | | | | | M-SPC now steps only through attached agents (the ones with a live on-screen buffer), so the fast chord stays among what's already up. M-S-SPC steps through all active agents and attaches a detached tmux session when it lands on one, which is what M-SPC used to do. I moved the shared step logic into cj/--ai-term-step-among and added cj/--ai-term-attached-agent-dirs to filter the queue to attached buffers. I bound both chords globally and in eat-semi-char-mode-map so they work from inside an agent buffer. C-; a n keeps cycling all.
* refactor: drop unreachable nil after user-error in recording checksCraig Jennings34 hours1-4/+2
|
* feat: add workflow font profilesCraig Jennings44 hours4-114/+280
| | | | | | I replaced the mixed Fontaine presets with seven named profiles that persist across restarts. The mode line, echo area, and minibuffer stay in Berkeley Mono. Nov applies the shared Reading profile inside each EPUB buffer without changing the global selection.
* fix(shell): require external-open, scope tramp revert, drop dead configCraig Jennings3 days3-21/+11
| | | | | | | | - dirvish-config, dwim-shell-config: both call cj/xdg-open (external-open) but relied on init.el requiring it before them, and dirvish's require comment misattributed it to system-utils. Added the require to both and corrected the comment and both Runtime-requires headers. - tramp-config: revert-without-query was set to '(".*") inside the TRAMP module, so every file in Emacs reverted without confirmation. Scoped it to the TRAMP path regexp, so local files keep their revert prompt and remote files skip the round-trip. - tramp-config: removed the sshfast method (referenced nowhere, scp and sshx handle everything) and a duplicate tramp-own-remote-path add-to-list. Tests: new runtime-requires assertions for both modules, red first under make test-file. The tramp :config edits are verified in the daemon. Suite green.
* fix(dev): repair hunk jump, F4 hook leak, async clone, and nil guardsCraig Jennings3 days6-53/+123
| | | | | | | | | | | | - vc-config: C-; v d matched literal +/- source text via consult-line, not gutter hunks. It now completes over git-gutter's hunk list and jumps to the chosen line. - vc-config: clipboard clone ran git synchronously, freezing every frame for the whole clone. It now runs async with a sentinel that opens the clone on success and surfaces the process buffer on failure. - vc-config: magit-blame bound D and S to the same command. D is now difftastic-magit-diff, matching the transient. - vc-config: dropped the :commands autoload for git-timemachine-show-selected-revision, a function the package never defined. The phantom appeared in M-x and errored. - dev-fkeys: both F4 chained-compile handlers armed a hook on the global compilation-finish-functions before their compile ran, so a quit left it live and the next unrelated compile fired the chain. The one-shot hook now installs buffer-locally in the compilation buffer, same shape as the projectile cache-revert hooks. - test-runner: outside a project with no global test directory, three commands crashed with wrong-type-argument on a nil path. Discovery now returns nil and the commands signal user-error. - diff-config: removed the global "-w" ediff default, which made every session ignore whitespace (indentation-only changes compared as identical). Whitespace-ignore stays available as ediff's per-session toggle. - restclient-config: the C-; R bindings went through raw global-set-key, silently depending on keybindings.el loading first. They now use a prefix keymap registered like the other C-; prefixes. - httpd-config: simple-httpd loaded on a 1s timer and created www/ on every startup. It now defers until impatient-mode needs it, and the doc root is created at package load.
* refactor(go): drop exec-path registration now duplicated at top levelCraig Jennings3 days1-1/+0
|
* fix(prog): cover classic modes and warn for missing dev toolsCraig Jennings3 days4-11/+71
| | | | | | Classic go-mode and js-mode buffers got none of the ts-mode setup, and web-mode got the format key without the promised company/flyspell/LSP. All three now run the shared setup. The web-mode LSP attach guards on the HTML language server, so machines without it stay silent instead of prompting. gopls, clangd, clang-format, bash-language-server, shfmt, and shellcheck now warn at load when missing. The shfmt, shellcheck, and clang-format blocks gate on :if, which evaluates once at startup, so an absent tool silently disabled that setup until the next restart. I also put ~/go/bin on exec-path at load so the gopls check doesn't misreport a gopls installed there.
* refactor(media): extract the playback sentinel as a named builderCraig Jennings3 days1-10/+14
| | | | | - cj/media--play-sentinel returns the per-process closure. - Three new tests pin the reap-on-exit behavior.
* refactor: launch external processes with argv lists, not shellsCraig Jennings3 days2-41/+78
| | | | | | | | - open-this-file-with splits the typed command and calls call-process. - media-play-it resolves streams via a yt-dlp capture, then start-process. - Paths, URLs, and player args never meet a shell. - yt-dlp stderr is captured separately so warnings can't parse as URLs. - Stream resolution now blocks briefly. mpv (the default) is unaffected.
* feat(music): add a delete-playlist commandCraig Jennings3 days1-1/+34
| | | | | | | - D in the playlist buffer deletes a saved playlist. - Candidates span both m3u sources: local library and MPD dir. - A typed yes/no confirms deletion. - Deleting the loaded playlist's file clears its association.
* refactor(music): single builder for the number-gutter stringCraig Jennings3 days1-13/+15
|
* feat(completion): right-align custom category annotationsCraig Jennings3 days2-23/+33
| | | | marginalia-align has been right for months, but annotations from the custom completion categories (radio stations, music files, and everything built through the system-lib table helpers) never went through marginalia. They rendered unaligned. A registration helper adds a builtin registry entry per category at table construction, which routes the table's own annotation function through marginalia's aligned field. The radio table converts from an affixation function with hand-rolled padding to a plain annotation, since marginalia now owns the alignment. Self-maintaining: any future custom category built through the helpers registers itself.
* feat(music): freeze the playlist header while the track list scrollsCraig Jennings3 days1-3/+37
| | | | The header block (playlist name, now-playing hero, key legend) is an overlay string anchored at buffer position 1, so scrolling the list carried it off the top, key legend included. A handler on the buffer-local window-scroll-functions re-anchors the overlay at the window's display start on every scroll, so the block stays pinned at the top of the window. The header refresh timer now anchors at the same position instead of the buffer top, so the once-a-second update can't yank a scrolled header back.
* fix(music): unstick arrows and number placement at the playlist topCraig Jennings3 days1-1/+12
| | | | | | The playlist header is a multi-line overlay string anchored at buffer position 1. With point on the top row, next-line's visual motion stepped through the header's screen lines. Those all map back to position 1, so arrows never moved point. The buffer now moves by logical lines (each row is one), which skips the header display entirely. The same shared anchor put row 1's number above the header instead of beside its track: both overlay strings sit at position 1 and the number rendered first. Number overlays now outrank the header's priority, so the number lands next to its own row.
* feat(music): mark the current playlist row on its numberCraig Jennings3 days1-2/+36
| | | | Emacs draws the filled cursor only in the selected window, and the playlist dock is glanced at from other windows constantly. The pinned cursor was invisible exactly when it was wanted. The current row's number now renders in inverse video, updated as point moves and re-applied after each renumber. The mark shows whether or not the window has focus, and the row band from hl-line was already sticky.
* fix(music): raise the playlist dock default to half the frameCraig Jennings3 days1-6/+6
|
* fix(music): show the cursor on the row number and open the dock tallerCraig Jennings3 days1-3/+17
| | | | | | With point pinned at the row start, redisplay drew the cursor after the number's before-string, on the album-art thumbnail, where a block cursor is invisible. A cursor property on the number string makes redisplay draw it on the number. The dock also kept reopening too short: its height memory remembered whatever size the window had at toggle-off, including squeezes from window churn. The squeezed fraction then overrode the default on every later open. The default rises to a third of the frame, and the toggle discards captured heights below it (a deliberate enlargement still sticks for the session).
* feat(music): pin the playlist cursor to the number gutterCraig Jennings3 days1-1/+15
| | | | Vertical motion over thumbnail rows and the stretch-space that right-aligns the metadata drifts point to arbitrary visual columns, usually line end. The rows aren't editable text, so the cursor now lives at the row start (the number column). A buffer-local post-command snap sends every landing back there. Kill, shift (C-up/C-down), and play already act on the whole row, so all track operations happen from the number. An active isearch keeps point until it ends, then the snap lands on the match's row.
* feat(music): playing-aware playlist landing and music-only addsCraig Jennings3 days1-16/+73
| | | | | | Opening the playlist used to jump to EMMS's selected track, which stays set while stopped, so the view opened deep in the list with point mid-row. It now lands on the playing track's row (beginning of line, upper third of the window) when a song is playing, and at the top of the list when stopped. Both entry points share the logic, and the playlist buffer gets hl-line so the current row stays findable on album art. Also fixed: directory adds handed the raw tree to emms-add-directory-tree, which adds every file it finds. Cover art and liner notes became playlist rows. All three add paths now route through a filtered walk that keeps only accepted music extensions. The m3u loader applies the same filter to local paths (stream URLs pass through), so old playlists with saved cover lines stop re-importing them.
* feat(music): tag completion, aligned listings, numbered playlist rowsCraig Jennings3 days1-18/+119
| | | | | | | | | | The radio tag search now completes over popular tags from radio-browser's /json/tags endpoint (station-count order, capped at 500, cached per session), so the prompt offers real tags instead of a blind guess. Free-form input still works, and a failed fetch degrades to plain input. Station listings align into columns: the votes field pads to a fixed width, and each annotation pads out to the widest candidate name. Playlist rows carry a numeric overlay prefix, rebuilt after every change by a debounced after-change hook, so the cursor stays visible on cover-art thumbnails and each row's position is readable. Overlays leave the buffer text to EMMS. Also fixed: the search now trims queries before they hit the API (a trailing space used to reach it as %20 and match nothing).
* docs(ai-term): match reuse-edge docstring to delete-window realityCraig Jennings3 days1-4/+7
| | | | The docstring promised toggle-off puts the displaced buffer back via quit-restore-window. Toggle-off deliberately deletes the window instead: the slot's quit-restore parameter goes stale when several agents share it. That staleness is the "F9 shows another agent" bug the delete-window design killed.
* fix(ai-term): key teardown session names off the buffer nameCraig Jennings3 days2-12/+35
| | | | ghostel retargets default-directory via OSC 7 on every cd in the agent shell, so close (and quit with no project argument) computed the wrong aiv- session name after a cd. The kill then missed the real session and orphaned the agent, or hit a different project's session. The buffer name ("agent [basename]") never changes, so teardown now derives the basename from it.
* fix(calendar-sync): honor BYDAY rules and drop cancelled eventsCraig Jennings3 days2-27/+183
| | | | | | | | The expander ignored BYDAY on monthly and yearly rules and stepped DTSTART's day-of-month instead, so a "2nd Wednesday" series rendered on the 12th of every month, the wrong weekday most months. It now resolves nth-weekday entries (2WE, -1TU), bare weekdays with BYSETPOS, and yearly BYMONTH+BYDAY, so each occurrence lands on the day the rule names. Nothing read the VEVENT STATUS property, so cancelled events rendered as normal meetings: a cancelled instance of a series reappeared at its original time, and standalone cancelled events stayed on the agenda. parse-event now drops STATUS:CANCELLED events, which also kills cancelled series masters. A cancelled RECURRENCE-ID override removes its occurrence instead of overriding it. The fixes are coupled: until BYDAY generates the right dates, a cancelled override can't match the occurrence it removes.