aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-08 02:34:02 -0500
committerCraig Jennings <c@cjennings.net>2026-06-08 02:34:02 -0500
commit9d3bc3f5c9da33ace5a0bbd73ed78d4fcd43f224 (patch)
tree3377c5c96c09930f04ab37c27b37cfe916e082d6
parentc05364951bf59edde7d0d0eb35013dfc077d40cf (diff)
downloaddotemacs-9d3bc3f5c9da33ace5a0bbd73ed78d4fcd43f224.tar.gz
dotemacs-9d3bc3f5c9da33ace5a0bbd73ed78d4fcd43f224.zip
docs(theme-selector): rewrite README for the full tool (tier-3 phase 7)
I rewrote the README to cover what the tool actually is now: the three face tiers (syntax, UI, package) plus the palette, the in-page color picker with its AA/AAA legibility mask, the package-faces section with bespoke org/magit/elfeed previews and the generic fallback, modeled inheritance and relative height (with the note that the font family stays in font-config.el), the theme.json packages schema with inherit/height/source, export-versus-save, and the inventory-refresh command with its loaded-config dependency. It also documents that theme-selector.html is generated.
-rw-r--r--scripts/theme-selector/README.md111
-rw-r--r--todo.org4
2 files changed, 91 insertions, 24 deletions
diff --git a/scripts/theme-selector/README.md b/scripts/theme-selector/README.md
index a8be0a10..5ed49c14 100644
--- a/scripts/theme-selector/README.md
+++ b/scripts/theme-selector/README.md
@@ -1,10 +1,12 @@
# theme-selector
-A self-contained tool for designing Emacs color themes by eye. It renders six
-languages of tree-sitter-tokenized code, a category→color assignment table, a
-UI-faces table, and an editable palette into one HTML page you drive in the
-browser. Reassign colors, toggle weight/slant, edit the palette, then export a
-`theme.json` that a build step turns into `themes/<name>-*.el`.
+A self-contained tool for designing Emacs color themes by eye. One generated
+HTML page drives the whole theme: a palette, the syntax (font-lock /
+tree-sitter) layer, the built-in UI faces with a live mock-frame preview, and
+package-specific faces (org, magit, elfeed, plus every other installed package).
+Reassign colors against the palette, judge legibility with live WCAG-contrast
+readouts, then export a `theme.json` that a build step turns into
+`themes/<name>-*.el`.
## Run
@@ -26,20 +28,68 @@ hyprctl keyword decoration:dim_inactive false
## Files
-- `generate.py` — emits the HTML+JS. Edit here to change layout or behavior.
-- `samples.py` — the language code samples and the default category→color map
- (`COLS`). `generate.py` reads the part before the `cols=` marker.
+- `generate.py` — emits the HTML+JS, and embeds the package data. Edit here to
+ change layout or behavior.
+- `samples.py` — the six language code samples and the default syntax
+ category→color map (`COLS`). `generate.py` reads the part before the `cols=`
+ marker.
+- `package-inventory.json` — generated map of every installed package to the
+ faces it defines (see Package faces below). A committed data artifact.
+- `build-inventory.el` — refreshes `package-inventory.json` from a running
+ Emacs.
- `theme-selector.html` — generated output. Regenerate; don't hand-edit.
## What it captures
-- Background and foreground (the `default` face's `:background` / `:foreground`).
-- The syntax layer: every font-lock / tree-sitter category (keyword, string,
- function, type, comment, and the rest), each with normal/bold/italic.
-- UI faces: cursor, region, mode-line, fringe, line numbers, isearch, paren
- match, link, error/warning/success, and more — foreground and background per
- face.
-- The palette itself: add by hex or swatch, remove, rename, drag to reorder.
+Three tiers of faces, plus the palette:
+
+- **Palette** — named colors. Add by hex or with the in-page color picker
+ (saturation/value square, hue slider, palette reuse chips, live contrast
+ readout, and an any / AA+ / AAA legibility mask). Remove, rename, reorder with
+ arrows or drag. The colors serving as background and foreground are locked.
+- **Syntax** — every font-lock / tree-sitter category (keyword, string,
+ function, type, comment, and the rest), each with normal/bold/italic and a
+ contrast rating. Click a category to flash its tokens in the code; click a
+ token to flash its row.
+- **UI faces** — cursor, region, mode-line, fringe, line numbers, isearch, paren
+ match, link, error/warning/success, and the rest, foreground and background
+ per face, shown in a live mock Emacs buffer.
+- **Package faces** — per-package face tables with a live preview (below).
+
+## Package faces
+
+Pick an application from the dropdown to edit its faces. Each row has a
+foreground and background dropdown, bold/italic toggles, an `inherit` dropdown
+(base faces like `fixed-pitch`/`link` plus the app's own faces), a relative
+height stepper, a contrast readout, and a per-face reset. There's a per-app
+reset and a text filter for the large sets.
+
+org-mode, magit, and elfeed have bespoke previews (a mock org document, a magit
+status buffer, an elfeed search list). Every other installed package is reachable
+too, with an editable table and a generic preview (each face name in its own
+colors), so any package can be themed.
+
+**Inheritance** is modeled, not flattened: a face's effective color is resolved
+through its `inherit` chain and shown in the table and preview; setting an
+explicit color overrides it. `height` is a float multiplier off the base font
+and is read directly off the face (not cascaded through `inherit`, since Emacs
+multiplies float heights along a chain). The base monospace family is *not* the
+theme's job — it lives in `modules/font-config.el`; the tool owns only relative
+size and the `fixed-pitch` inherit relationships.
+
+### Refreshing the package inventory
+
+The reachable packages come from `package-inventory.json`. Regenerate it from a
+running Emacs (so it reflects what's actually installed), then rebuild the HTML:
+
+```bash
+emacsclient -e '(load "/home/cjennings/.emacs.d/scripts/theme-selector/build-inventory.el")'
+python3 generate.py
+```
+
+`build-inventory.el` groups each face by the package whose file defines it; it
+depends on the target Emacs having those packages loaded. Built-in faces are
+skipped (they're covered by the syntax and UI tiers).
## theme.json contract
@@ -47,21 +97,38 @@ The export (and what a build step consumes):
```json
{
- "name": "dupre-revision",
+ "name": "dupre",
"palette": [["#67809c", "blue"], ["#e8bd30", "gold"]],
- "assignments": {"kw": "#67809c", "str": "#5d9b86", "bg": "#0d0b0a", "p": "#cdced1"},
+ "assignments": {"kw": "#67809c", "str": "#5d9b86", "bg": "#000000", "p": "#ffffff"},
"bold": ["kw", "fnd"],
"italic": [],
- "ui": {"region": {"fg": null, "bg": "#264364"}, "cursor": {"fg": null, "bg": "#a9b2bb"}}
+ "ui": {"region": {"fg": null, "bg": "#264364"}, "cursor": {"fg": null, "bg": "#a9b2bb"}},
+ "packages": {
+ "org-mode": {
+ "org-level-1": {"fg": "#67809c", "bg": null, "bold": true, "italic": false,
+ "inherit": null, "height": 1.3, "source": "default"}
+ }
+ }
}
```
-The theme name is both the `name` field and the download filename
-(`<name>.json`, sanitized). Upload a `theme.json` to start from a prior theme.
+- `assignments` maps syntax category keys to hexes; `bg` is the `default` face
+ background, `p` the foreground.
+- `ui` and `packages` faces carry `fg`/`bg` (hex or `null`), `bold`, `italic`,
+ and for package faces `inherit` (a face name or `null`), `height` (a float,
+ omitted at 1.0), and `source` (`"default"` seeded, `"user"` edited,
+ `"cleared"`).
+- The theme name is both the `name` field and the download filename. Import a
+ `theme.json` to start from a prior theme; a file with no `packages` key still
+ loads.
+
+`export` always downloads a fresh file; `save` (shown once a name is entered)
+writes the same file in place via the File System Access API.
## Next step (not yet built)
A `theme.json` → `themes/<name>-palette.el` + `-faces.el` + `-theme.el`
-converter. That step is the correctness-sensitive part and is the one worth
-TDD: JSON in, valid Emacs palette + faces out, every face mapped, WCAG-contrast
+converter, in Elisp. That step is the correctness-sensitive part and the one
+worth TDD: JSON in, valid Emacs palette + faces out, every face (syntax, UI, and
+package) mapped, `:inherit`/`:height` written correctly, and WCAG-contrast
asserted on the result.
diff --git a/todo.org b/todo.org
index 1c55f36a..830baed2 100644
--- a/todo.org
+++ b/todo.org
@@ -84,8 +84,8 @@ Bespoke =renderMagitPreview()= (status buffer: head/branches, untracked, a diff
*** 2026-06-08 Mon @ 02:32:44 -0500 Phase 6 — generated all-package inventory landed
=build-inventory.el= (loaded into a running Emacs) groups every installed package's faces by the defining package and writes =package-inventory.json=. =generate.py= embeds it and merges each package into the dropdown as an editable generic app, leaving org/magit/elfeed bespoke. 40 apps now (3 bespoke + 37 inventory, 643 faces). Committed data artifact, refreshed by reloading the .el; never browser-side discovery. Verified: node, self-test PASS, app count + bespoke-preserved checks.
-*** TODO [#B] Phase 7 — docs + validation :solo:
-README: =packages= schema, inheritance behavior, inventory-refresh command and its loaded-config dependency; note =theme-selector.html= is generated. Regenerate HTML. Spec phase 7.
+*** 2026-06-08 Mon @ 02:34:01 -0500 Phase 7 — docs landed
+Rewrote =README.md= for the full tool: three face tiers + palette, the in-page picker (with the AA/AAA mask), package faces (bespoke vs generic previews), modeled inheritance + relative height (family stays in font-config.el), the packages schema with inherit/height/source, export-vs-save, and the inventory-refresh command (=build-inventory.el=) + its loaded-config dependency. Notes =theme-selector.html= is generated. Test-surface fixtures tracked separately below.
*** TODO [#B] theme-selector tier 3 — test surface :test:solo:
Fixtures: =dupre.json= (no =packages=) imports clean; =packages= fg/bg/bold/italic/inherit/source round-trip; cleared/default/user export per policy; unknown package/face data preserved-or-warned; inheritance cycle resolves to none. Regression: palette color update propagates to package faces; deleted palette color leaves package refs "(gone)" recoverable. Generated output: =generate.py= rebuilds =theme-selector.html=. Spec: Acceptance criteria.