| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
| |
C-z forwarded to the pty sends SIGTSTP to the foreground job; with
eat-over-tmux, fg does not reliably bring the agent back. Swallow it in
eat-semi-char-mode-map so it never reaches the pty.
|
| |
|
|
|
|
| |
The leading modeline space set its height with a display (height 1.15) property, which scales against the buffer's default face. nov-mode's reading view remaps default to 18pt, so there the padding grew to 1.15x that and the bar rendered far taller than normal. The theme's absolute mode-line height couldn't help, since the padding space drives the strip height, not the face.
The padding now uses an absolute integer height anchored to the frame's default face, so a buffer that enlarges its own default (nov, text-scale) no longer inflates the bar. Normal buffers are unchanged, and the nov bar matches them.
|
| |
|
|
|
|
| |
tab-bar, tab-line, line-number, and line-number-current-line join mode-line in the chrome height seeds (apply_modeline_height_default generalized to apply_chrome_height_defaults), each pinned at absolute 130 so no bar or gutter tracks a buffer's enlarged default face. header-line and mode-line-inactive stay unseeded on purpose: both inherit mode-line, so the pin reaches them through the chain and their own value would duplicate state. The line-number pair is seeded individually because the generated theme's explicit specs leave their :inherit unspecified at runtime.
header-line, tab-bar, and tab-line also join the UI faces table, so all chrome heights are editable through the size column; the mock-completeness gate exempts the three faces the mock deliberately doesn't draw. WIP.json reconciled and the theme regenerated; every chrome face resolves :height 130 in the live daemon, pins and inherit chains both.
|
| |
|
|
|
|
| |
heightCssValue maps a face's height to CSS from its stored kind: a relative multiplier renders as em, an absolute 1/10pt value as true pt, with legacy objects falling back to integer/fractional inference. uiCss feeds it to the mock editor, so the mode-line, mode-line-inactive, and line-number bars visibly thicken with an absolute height while the buffer text stays put; paintUI scales the UI row's sample text; the package-preview span builder swaps its em-only sizing for the same kind-aware value.
faceCss now accepts a unit-carrying string for fontSize alongside the existing em number.
|
| |
|
|
|
|
|
|
| |
A new size column in the UI and package tables carries one numeric field plus an abs/rel toggle, exposed per the editable-height spec: chrome faces (mode-line family, line-number family, and header-line/tab-bar/tab-line when they arrive) default to absolute 1/10pt entry with a computed pt hint; the seeded heading faces (org-level-*, document title/info, agenda structure/dates, shr headings, and friends) default to a relative multiplier. Any other face carrying a live height exposes the control dynamically; the long tail gets none.
Absolute entry takes a positive integer only; relative entry clamps into the 0.1-2.0 range the old field used; garbage never reaches the model. The toggle writes heightMode explicitly and clears the number on a flip, since 130 tenth-points and 1.3x mean different things. The kind-unaware height field in the row expander is retired, and a non-default height now marks the size cell instead of the expander toggle.
The seeded set is named statically in app-core.js because the per-row default comes from the captured Emacs snapshot, which carries no heights for those faces. The #preview screenshot hash now accepts @ui/@code view keys so the harness can shoot the UI table.
|
| |
|
|
|
|
| |
JSON collapses 2.0 to 2 on save, so a height's number type can't say whether it's a fixed 1/10pt value or a relative multiplier. The face model now carries an explicit heightMode field (abs/rel) through seed, save/load, and export. build-theme.el coerces :height from the kind: abs exports an integer, rel a float, so a relative 2.0 renders as 2.0, never 2.
Faces saved before the field existed infer the kind once on load (JS: integer to abs, fractional to rel; Python keeps the authored type, so a float 2.0 seed stays relative) and persist it on the next save. The mode-line seed carries abs explicitly, and WIP.json's eight seeded heights are stamped with their kinds. Regenerating the theme from the stamped WIP.json produces an identical WIP-theme.el, so the round-trip holds.
|
| |
|
|
|
| |
Phases decomposed into build tasks; Phase 1 gains a load-time kind-inference
migration for saved themes that predate the explicit height-kind field.
|
| |
|
|
|
|
|
|
|
|
|
| |
mode-line's :height was unspecified, so a buffer that remaps its default
face larger (the nov reading view) inflated its modeline with it. Seed an
absolute 130 (1/10pt) on mode-line — build_uimap gains
apply_modeline_height_default, mirroring the hover-box default — and set
it in WIP.json. Also drop the stray :height 2 from mode-line-inactive
(a JSON integral-float collapse that rendered inactive bars at 0.2pt);
inactive now inherits mode-line's height. Theme regenerated and loaded
live; the editable-height spec covers making this tunable in the studio.
|
| |
|
|
|
|
|
|
|
|
| |
Code read surfaced two findings: JSON collapses integral relative floats to
absolute ints (WIP.json's mode-line-inactive height 2 is the live casualty),
so the face model carries the kind explicitly; and the minimal seed fix the
metadata referenced never shipped. All five open decisions settled: one field
plus abs/rel toggle, exposed on chrome plus already-seeded faces, raw 1/10pt
entry with a pt hint, row-sample plus mock-editor previews, seed fix owns the
artifact cleanup.
|
| |
|
|
|
|
| |
wttrin-state-file defaults to locate-user-emacs-file, which drops the state
file at the repo root as untracked clutter. Point it at data/ alongside the
other per-machine state files; moved the existing file there.
|
| |
|
|
|
|
| |
Stubbed from the 2026-07-02 nov-reading modeline-height diagnosis: the studio
exports per-face :height but offers no editable control, and the absolute-vs-
relative distinction is what separates a scaling heading from a fixed modeline.
|
| | |
|
| |
|
|
| |
ansi-color carried a bespoke spec and a full seed palette but its renderer was never written, so it silently fell back to the generic face list. Added renderAnsiColorPreview — a compilation/shell buffer exercising all 16 SGR palette faces, the faces eat and vterm color faces inherit from — and registered it. Last generic app in the ecosystem-coverage scope.
|
| |
|
|
| |
ghostel (the terminal behind the agent buffers) gets an eat-style scene through its own 16 ANSI color faces plus the default surface and the two fake-cursor faces; all-the-icons a dired listing where each file's leading marker carries its color face across the 8 hues, their light/dark variants, and the -alt accents. Both are pinned not-loaded apps whose only visibility is the studio preview, so they carry the highest realism bar. Face names are literal, not constructed, to satisfy the coverage gate.
|
| |
|
|
|
|
| |
nerd-icons-completion
tmr shows the list view and tmr-tabulated-view with every column face; wttrin the weather buffer with staleness header, key hints, and the stale modeline lighter; alert one line per severity; org-superstar the bullet swap across levels plus list items; nerd-icons-completion folder icons in the dir face. Phase 4 of the preview run — all 13 small-scene apps now sit in the face-coverage gate.
|
| |
|
|
|
|
| |
flyspell-correct
highlight-indent-guides shows both methods (character glyphs, column bands) plus the responsive top/stack variants at a marked point; yasnippet a mid-expansion snippet with the active field and the debug overlay; prescient primary/secondary match highlights; flyspell-correct the word under correction with its menu. All four join the face-coverage gate.
|
| |
|
|
| |
emms shows its three UIs (browser tree, playlist, metaplaylist); org-roam the backlinks buffer plus the dailies calendar mark; hl-todo comment keywords and the flymake diagnostic; symbol-overlay all eight pin faces on one defun plus the at-point default. All four join the face-coverage gate.
|
| |
|
|
| |
One C-x b consult-buffer scene drawn jointly by vertico, marginalia, consult, embark, and orderless; each app's preview shows the shared session with its own faces at work, then extends it with the states only it owns (vertico multiline, marginalia's full annotation catalog, consult line/grep/async, embark act + collect, orderless component cycling). All five join the face-coverage gate, so every one of their 72 faces appears verbatim in the renderers.
|
| |
|
|
| |
The web-mode preview is one mixed document: markup with every tag/attr variant, an inline CSS part, a generic template block (engine-agnostic on purpose), and a script part carrying a JSON island, JSX depths, nested template literals, SQL-in-a-string, a PHP preprocessor island, and JSDoc annotations. The realism gate now covers it, so all 81 faces are exercised. SAMPLES gains an HTML language, which also enriches the syntax and auto-dim previews.
|
| |
|
|
| |
Five daily-driver apps leave the generic face-name list: company (a completion popup with every row variant, inline ghost preview, tooltip search, and the echo frontend), company-box (the icons variant), transient (a magit-commit-style panel exercising every key class), magit-section (status-buffer headings with child counts and the highlighted section), and rainbow-delimiters (buffer-honest nesting depths 1-9 plus the cycle past 9 and both error faces). A new test gates realism: every face of each covered app must appear verbatim in its renderer, so a scene can't silently skip faces.
|
| |
|
|
| |
PINNED_PACKAGE_FACES is the curated record of packages retired from the config (ghostel, all-the-icons). A pinned package survives inventory regeneration, shows a 'not loaded' label + hover in the app dropdown, and keeps refreshing its face list from the live inventory while that still carries it. An attempted live regen made the fragility concrete: today's daemon session was missing 142 faces from lazily-loaded packages, so regen-from-live is inherently lossy and the pin is the only durable record.
|
| | |
|
| |
|
|
| |
Two speedrun-enabling pieces. A #preview=<app>&theme=<json> hash handler plus screenshot-previews.sh shoot any app's face table and live preview headlessly under a real theme (WIP.json by default), so preview work can be verified without a human clicking through the studio. The README gains the coverage policy: the studio themes popular packages even when uninstalled, pinning their faces rather than dropping them, and unloaded packages' previews matter more, not less.
|
| |
|
|
| |
The nine ai-term faces (the bypass-banner accent plus one per Claude Code /color name) join the studio as a bespoke app, seeded with their dupre hues. The preview mirrors what they paint: the banner line and a mock input box per session color. Tuning one here recolors live agents on their next redraw.
|
| |
|
|
| |
Every project now maps to a stable Claude Code session color: an override alist wins, else a character-sum hash of the project basename picks one of the eight names. When a fresh tmux session is created (never on reattach), a poller waits for the TUI to boot and types /color <name> itself, with the Enter deferred a beat so the slash-command menu can't swallow it. Two refusals keep the injection safe: the bypass banner must be on screen (a bare shell never gets typed into) and the prompt line must still be empty (typed-ahead input is never corrupted).
|
| |
|
|
| |
Claude Code's /color picks a session accent from eight names, each emitted as a fixed xterm-256 index (probed against v2.1.198 by cycling /color in a scratch tmux session and reading the SGR codes). Agent terminals now pin all eight indices plus the bypass banner to dupre faces, so any /color choice renders in the theme's palette instead of stock xterm hues. dupre has no orange or pink, so those borrow red+1 and magenta+1. If a Claude Code update moves an index, the stock hue comes back (the alist docstring carries the re-probe note).
|
| |
|
|
| |
Claude Code draws its accent (the bypass-permissions banner, borders, spinner) with xterm-256 palette codes, and the stock rose red is palette index 211. eat resolves those codes through a per-terminal face vector, so agent terminals now point index 211 at the new cj/ai-term-accent face (dupre blue #67809c) at creation. Every other eat terminal keeps the true palette. Per-project colors can later ride the same per-terminal mechanism.
|
| |
|
|
| |
Pressing d in the C-x C-s conflict menu (and the save-some loop) now enters a modal review instead of a peek-and-return toggle: point lands on the first hunk, arrows and TAB move through the changes, and the menu keys act from inside the diff. difftastic gets --context 1 and an explicit --width, since as a subprocess it can't detect the terminal and wrapped at 80 columns. A new m choice resolves the conflict in ediff. I kept the post-merge save re-asking once, so an abandoned merge can't silently overwrite the disk version.
|
| |
|
|
| |
F1, F10, F11, F12, and M-SPC are global popup keys (dashboard sweep, music, dirvish-side, terminal, agent swap) and fired over an in-progress capture. org-capture-mode's keymap is active exactly for the capture's duration and shadows the global map, so those keys now hit a blocker there that names the way out (C-c C-c to finalize, C-c C-k to abort). The C-; a prefix stays unblocked: a deliberate two-chord sequence isn't a stray keypress.
|
| |
|
|
| |
cj/mu4e-save-attachment-here now completes through an annotated table (category mu4e-attachment), so marginalia shows each attachment's MIME type and decoded size beside the filename. Unknown candidates annotate as nil. The existing picker test queries the function table via all-completions instead of car-mapping the old alist.
|
| |
|
|
| |
First spec-sort pass under the docs-lifecycle convention: color-families -> READY (its status field carried the 2026-06-10 confirmation), palette-columns and palette-ramps -> DRAFT. Each got the authoritative status heading with an :ID: UUID; six inbound links rewritten. subr-mock-migration-spec is -spec-named but lacks the spec spine, so it stays put until it's renamed or retrofitted.
|
| |
|
|
| |
The docs-lifecycle convention gives every formal spec under a project's docs/specs/ an :ID: and links cross-project with [[id:...]], but org-id-locations only indexes agenda files and visited files, so a fresh spec's id never resolved on click. org-spec-links.el enumerates every project's docs/specs/*.org into org-id-extra-files once org-id loads (a literal file list; org-id doesn't glob), and cj/org-id-refresh-spec-locations re-scans and updates org-id-locations for immediacy after new specs land. Verified live against a known cross-project spec id.
|
| |
|
|
| |
The message duplicated the modeline directly above it: the buffer name and the eat state icons already announce which agent is focused, so the echo just lingered as clutter. The no-other-agents echo stays, since it reports something the modeline can't.
|
| |
|
|
|
|
| |
The modeline now follows one layout rule: the left side is Emacs information (mode icon, eat state, modified/read-only, buffer name, @host, Narrow, VC branch, position, MACRO, process) and the right side is a systray for package indicators (recording, flycheck counts, misc-info). The VC branch and mode-line-process moved left to fit the rule.
eat buffers trade the [semi-char]:run text for two icons beside the mode icon: a keyboard glyph for the input mode (quiet when semi-char, warning otherwise, hover text explains where keys go, mouse-1/2/3 switch modes mirroring eat's own bindings) and a green play / red power-off for the process state. eat-config clears eat's buffer-local mode-line-process so nothing renders twice.
|
| |
|
|
| |
cj/markdown-preview now brings up the simple-httpd listener itself instead of signaling a user-error pointing at cj/markdown-preview-server-start. The two-command flow guarded against a surprise network listener, but the preview is the only reason the listener exists, so the guard was just an extra step. I kept the start command for manual use.
|
| |
|
|
| |
A grazed F2 followed by a stray 2, s, or b invokes two-column mode, which replaces the buffer's mode-line-format with its own retro layout and spawns a 2C/ companion buffer. That's what mangled the modeline in an agent terminal: eat semi-char buffers don't forward F2, so the prefix was armed everywhere. Nothing of mine uses global F2 (markdown-mode's binding lives in its own map), so I unbound both routes to 2C-command. cj/modeline-reset repairs a hijacked buffer.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
I rebuilt the custom modeline as pure segment helpers with thin :eval wiring:
- The nerd-icons mode icon replaces the mode-name text (cached per buffer, plain name on terminal frames), with the full mode name in the help-echo.
- New left-side segments: modified dot / read-only lock (file buffers only), remote @host tag, Narrow tag that widens on click, point-based percentage, region selection info, and a MACRO tag while a keyboard macro records.
- New right-side segments: mode-line-process (eat and compilation state was invisible) and flycheck per-severity counts with click-through to the error list, replacing the stock status text. Glyphs are nerd-icons private-use codepoints so emojify can't rewrite them, with text fallbacks when icons are unavailable.
- cj/modeline-reset kills a hijacked buffer-local mode-line-format (two-column mode, ediff, calc).
- Optional taller bar via cj/modeline-height-factor, a display height property on the padding space so the theme's mode-line faces stay untouched.
- Housekeeping: the dead user-constants require and stale commentary are gone, cj/modeline-vc-faces left the risky-local-variable list, and the cache-key defun is cj/--modeline-vc-cache-key so it no longer shadows the same-named defvar.
Percent signs from :eval strings go through the mode-line %-construct pass, so the position segment emits %% for a literal percent.
|
| |
|
|
| |
Sets wttrin-mode-line-tooltip-forecast-days to 3, the option added to wttrin in its 6c808ff (local checkout, release/0.4.0).
|
| |
|
|
|
|
|
|
| |
ai-term.el had grown to ~1,215 lines mixing project/tmux session discovery, window display policy, the EAT terminal backend, and the public commands, so a change to any one risked coupling to the others.
I extracted three layers, following the calendar-sync split shape: ai-term-sessions.el (discovery, tmux naming and parsing, launch command, picker ordering), ai-term-display.el (display-buffer actions and rule, toggle state, server-window routing), and ai-term-backend-eat.el (terminal create/reattach, pty send, EAT keymap). The backend file is named for its backend so a future one lands as a sibling. ai-term.el stays the public face (options, commands, keymap, shutdown), every name unchanged, so existing (require 'ai-term) callers and all 30 test files work as before.
The extracted layers forward-declare the face's defcustoms rather than requiring it, keeping the graph acyclic. I dropped the unused cl-lib and host-environment requires and added the three modules to the header-contract list.
|
| |
|
|
| |
Delete file (D) ran with no confirmation at all; erase, clear-to-top/bottom, and revert were a single keystroke from destroying unsaved edits; and raw revert-buffer prompted even when there was nothing to lose. Policy now: delete always confirms, naming the file (the VC path keeps vc-delete-file's own prompt); erase/clear/revert confirm only when a file-visiting buffer has unsaved edits, and stay fast otherwise. The delete workhorse is split into an unconfirmed internal so its existing tests keep exercising the file mechanics; 13 new tests cover the policy.
|
| |
|
|
| |
The "block remote images" comment sat on mu4e-view-show-images / mu4e-view-image-max-width, which are obsolete since mu4e 1.7 and ignored by the shr view — the real gate is gnus-blocked-images. The comment now documents the actual policy (remote blocked, embedded inline), and cj/mu4e-toggle-remote-images echoes the effective state after each refresh instead of leaving you to guess what it did.
|
| |
|
|
| |
package-check-signature was nil, skipping verification everywhere. allow-unsigned verifies signatures when an archive provides them while still accepting the unsigned local mirror and .localrepo packages. gnu-elpa-keyring-update installs at bootstrap (non-fatal on failure) so an expired GNU archive key stops being a reason to turn verification off.
|
| |
|
|
| |
treesit-auto-install was t, so opening a file could silently trigger a network download and compiler build mid-edit. It now prompts, and cj/install-treesit-grammars is the deliberate fresh-machine bootstrap that installs everything in one command.
|
| |
|
|
| |
cj/org-babel-toggle-confirm landed on C-; k as a placeholder. It's an org-babel concern, so it now lives on the org menu as C-; O b, and C-; k is free again. The binding registers after org-config loads so a standalone load of this module still works.
|
| |
|
|
| |
With a single agent open and focused, the rotation wrapped back to the same agent and echoed a misleading "Agent: <name>" as if it had swapped. Now it says there are no other ai-terms to switch to. A sole agent that is displayed but not selected still gets selected, and the no-agents picker fallback is unchanged.
|
| |
|
|
| |
The wf-recorder integration tests passed video-recordings-dir to the real capture pipeline, and the test file's defvar stub of that variable is clobbered by user-constants' defconst on load — so every suite run left ~0.5s screen captures in the real recordings directory. Pass an explicit make-temp-file dir and delete it in the unwind, matching what the capture probe already did.
|
| |
|
|
| |
At speed 3 the native compiler emits direct calls for functions in the same compilation unit, bypassing the symbol's function cell. Any cl-letf mock of a module's own helper then silently runs the real code: the recording tests' mocked wayland check and device validation were bypassed, and make test launched real wf-recorder screen captures. Speed 2 is the highest level that preserves redefinition semantics. A meta test now pins the setting; the local eln cache needs one flush so stale speed-3 artifacts recompile.
|
| |
|
|
|
|
| |
EAT 0.9.4's parser accepts more charset-designation final bytes than its store step maps. A designation like ESC ( A (UK) isn't one of the two it handles ("0" and "B"), so it stores nil as that slot's charset. The next character written then fails (cl-assert charset) in eat--t-write. Since writes run off the output-queue timer, it repeats once per output chunk. An agent terminal that emits one of these bytes throws "cl-assertion-failed (charset)" hundreds of times and stops rendering.
I added filter-args advice on eat--t-set-charset that coerces a nil charset to us-ascii before it's stored, so an unmapped designation falls back to plain ASCII instead of wedging. Patching the vendored pcase would be cleaner, but a package update reverts it. The advice loads with eat, since the target is an internal function.
|
| |
|
|
|
|
| |
Audio-only recordings were written as AAC in an MP4/.m4a container. The stop path SIGINTs ffmpeg, and if the MP4 muxer doesn't write its moov trailer before exit, the file has no moov atom and won't decode. ffmpeg and AssemblyAI both reject it. Three recordings were lost that way and had to be rebuilt with untrunc. The video path already avoids this by using Matroska, which needs no finalize pass.
I switched the audio-only path to FLAC. FLAC frames are self-contained, so an abruptly stopped recording still decodes, with no trailer to miss at close. It's also lossless, dropping the 64k AAC encode that degraded speech before transcription. AssemblyAI recommends a lossless source and accepts FLAC directly. The transcription path passes audio files through untouched.
|
| | |
|