<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotemacs/scripts/theme-studio/theme-studio.html, branch main</title>
<subtitle>My Emacs configuration
</subtitle>
<id>https://git.cjennings.net/dotemacs/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/dotemacs/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/'/>
<updated>2026-07-04T23:08:01+00:00</updated>
<entry>
<title>feat(theme-studio): add seeding-engine model and pure seed() (phase 1)</title>
<updated>2026-07-04T23:08:01+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-04T23:08:01+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=f407959b4c30fa809722b62266640711c916a772'/>
<id>urn:sha1:f407959b4c30fa809722b62266640711c916a772</id>
<content type='text'>
I turned the coloring guide's role/seed table into an executable engine. A new pure module, seed-core.js, holds the seed model as data: a named palette whose accent shades are OKLCH-generated from the dupre anchors (reusing the colormath core), the role-to-treatment table, and a face-to-role map per owned tier, plus a pure seed() that projects the table onto syntax, UI, and org faces.

seed() owns three default sources: syntax, UI, and org among packages. It returns packages.org-mode only, so the roughly twenty non-org bespoke packages keep their curated APPS seeds untouched. The output already matches the shape the import path consumes, so phase 2 can wire it to open-seeded without a new format.

Builtins land on blue-grey and calls on a quieter gold, the two shades dupre lacked. Definitions come out gold and bold, state faces tint the background with no foreground, links underline, and the org heading ramp descends in lightness with level 1 strongest.

The module inlines into the page below the colormath core like the other pure cores, so the browser runs the code the Node tests import. #seedtest asserts the representative faces resolve correctly and that magit keeps its curated seed. test-seed-core.mjs covers the model, each tier, and seed()'s purity.
</content>
</entry>
<entry>
<title>fix(theme-studio): persist regexp/doc/negation/warning syntax faces</title>
<updated>2026-07-04T16:35:22+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-04T16:35:22+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=0be572e43f8118f6c678a536b3d97d7e976e840f'/>
<id>urn:sha1:0be572e43f8118f6c678a536b3d97d7e976e840f</id>
<content type='text'>
Five syntax categories (rxgb, rxgc, dmark, neg, warn) rendered as editable rows but had no COLS entry, so build_syntax never gave them a SYNTAX slot. They exported nothing and re-imported nothing, so every saved theme dropped them. I added the five to COLS, where the default-face seed fills their real colors and they round-trip through save and import. A regression test now asserts every editable category has a COLS slot.
</content>
</entry>
<entry>
<title>feat(theme-studio): pin the remaining chrome at absolute heights</title>
<updated>2026-07-03T04:02:48+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-03T04:02:48+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=298e24aa8a0fdd88d2ae8ecb514b3e18b2b4bb5b'/>
<id>urn:sha1:298e24aa8a0fdd88d2ae8ecb514b3e18b2b4bb5b</id>
<content type='text'>
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.
</content>
</entry>
<entry>
<title>feat(theme-studio): previews render the chosen face height</title>
<updated>2026-07-03T03:50:27+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-03T03:50:27+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=0ffd6f5a450e716e7ef3297d4bec2fda36649cdf'/>
<id>urn:sha1:0ffd6f5a450e716e7ef3297d4bec2fda36649cdf</id>
<content type='text'>
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.
</content>
</entry>
<entry>
<title>feat(theme-studio): inline height control on the exposed face rows</title>
<updated>2026-07-03T03:40:16+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-03T03:40:16+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=3581c7d1c05eb514aa5462b1142605541fb64d9e'/>
<id>urn:sha1:3581c7d1c05eb514aa5462b1142605541fb64d9e</id>
<content type='text'>
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.
</content>
</entry>
<entry>
<title>feat(theme-studio): explicit absolute-vs-relative face height kind</title>
<updated>2026-07-03T03:01:54+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-03T03:01:54+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=630cfddc7060c7019815f8e82f87fb629aefebfa'/>
<id>urn:sha1:630cfddc7060c7019815f8e82f87fb629aefebfa</id>
<content type='text'>
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.
</content>
</entry>
<entry>
<title>fix(theme-studio): pin mode-line at an absolute height</title>
<updated>2026-07-03T02:09:55+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-03T02:09:55+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=1e10023c65acb36b5e6c378771d8a1773b1b3f9e'/>
<id>urn:sha1:1e10023c65acb36b5e6c378771d8a1773b1b3f9e</id>
<content type='text'>
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.
</content>
</entry>
<entry>
<title>feat(theme-studio): wire the ansi-color scene, closing its dangling preview key</title>
<updated>2026-07-02T18:29:04+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-02T18:29:04+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=8800e3a2cb6d7b96f520171308165ebc7511e536'/>
<id>urn:sha1:8800e3a2cb6d7b96f520171308165ebc7511e536</id>
<content type='text'>
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.
</content>
</entry>
<entry>
<title>feat(theme-studio): scenes for pinned ghostel and all-the-icons</title>
<updated>2026-07-02T18:24:08+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-02T18:24:08+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=a51a4c4c3f98b8cae9e739896cd2f8bc1ebbe94d'/>
<id>urn:sha1:a51a4c4c3f98b8cae9e739896cd2f8bc1ebbe94d</id>
<content type='text'>
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.
</content>
</entry>
<entry>
<title>feat(theme-studio): scenes for tmr, wttrin, alert, org-superstar, nerd-icons-completion</title>
<updated>2026-07-02T18:15:42+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-02T18:15:42+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=38d41d7d9ae616dd54f37c18f4d999dc1501d063'/>
<id>urn:sha1:38d41d7d9ae616dd54f37c18f4d999dc1501d063</id>
<content type='text'>
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.
</content>
</entry>
</feed>
