diff options
Diffstat (limited to 'docs/design/theme-studio-package-faces-spec.org')
| -rw-r--r-- | docs/design/theme-studio-package-faces-spec.org | 586 |
1 files changed, 0 insertions, 586 deletions
diff --git a/docs/design/theme-studio-package-faces-spec.org b/docs/design/theme-studio-package-faces-spec.org deleted file mode 100644 index 7f00b3279..000000000 --- a/docs/design/theme-studio-package-faces-spec.org +++ /dev/null @@ -1,586 +0,0 @@ -#+TITLE: theme-studio — package faces (tier 3), starting with org-mode -#+AUTHOR: Craig Jennings -#+DATE: 2026-06-07 - -* Status - -Spec / Craig's first-round answers folded in (2026-06-07). Proposes a third tier -for the theme-studio (scripts/theme-studio/) that lets a theme colorize -package-specific faces, built one application at a time. v1 apps: org-mode -(incl. org-agenda), magit, elfeed. Codex review incorporated (2026-06-07): added -implementation phases, acceptance criteria, the package-face inventory source -(hybrid, split), and state/export semantics. Rubric now =Ready=. -All opens resolved (Craig, 2026-06-07/08): inheritance is modeled (show each -face's resolved color in the table + preview, override what looks bad); inventory -is hybrid-and-split (org/magit/elfeed bespoke first, generated all-package -inventory as a later phase); the custom color picker is built after tier 3. -Implementation tasks live in =todo.org=. - -* Background — the three tiers - -The theme-studio already models two tiers of faces: - -1. *Syntax* — the font-lock / tree-sitter categories (keyword, string, type, - comment, etc.), in the "code/color assignments" table. -2. *UI* — Emacs's built-in interface faces (cursor, region, mode-line, fringe, - line numbers, isearch, and the rest), in the "ui faces" table with the live - mock-frame preview. - -Tier 3 is *package faces*: faces a package declares with =defface= so a theme -can color the package as it wishes. The running config has 1,146 such faces -across 186 packages (magit 111, lsp-mode 97, telega 91, web-mode 82, org ~30 -core, and a long tail). No theme colors all of them; quality themes hand-pick -the packages the user actually lives in and theme those. - -This spec adds a tier-3 section to the tool, structured so applications are -added one at a time. org-mode ships first. - -* Goal - -A new "package faces" section with: - -1. An *application dropdown* — pick which package's faces to edit. v1 ships - org-mode (including org-agenda), magit, and elfeed; the rest of Craig's - packages (calibredb, ghostel, mu4e, IRC, org-drill, dirvish + dired, slack) - follow one at a time. -2. A *face table* for the selected app — one row per face in the app's complete - set, each with a foreground dropdown, a background dropdown, bold / italic - toggles, an optional inherit, and a relative-height stepper, all drawing from - the same palette as the other tables. Grouped, with a text filter for the - large apps. -3. A *preview pane* for the selected app — a realistic mock of that package - rendered with the live theme, the way the ui-faces mock-frame shows the UI - faces in a buffer. org-mode gets a mock org document. - -The export (=theme.json=) gains a =packages= object so the build step can set -these faces too. - -* UI placement - -A new top-level section under the ui-faces row: - -#+begin_example -<h1>package faces</h1> -[ application: (org-mode v) ] -<div class="cols stretch"> - left = the selected app's face table (fg / bg / B / I per face) - right = the selected app's preview pane (e.g. the org document mock) -</div> -#+end_example - -Same two-column stretch layout as the ui-faces row, so the preview matches the -table's height. - -* Data model - -A single data structure drives everything, keyed by application: - -#+begin_src js -APPS = { - "org-mode": { - label: "org-mode", - faces: [ - // face, human label, default {fg, bg, bold, italic} - ["org-document-title", "document title", {fg:"gold", bold:true}], - ["org-level-1", "heading 1", {fg:"blue", bold:true}], - ["org-level-2", "heading 2", {fg:"gold"}], - ["org-level-3", "heading 3", {fg:"regal"}], - ["org-todo", "TODO keyword", {fg:"terracotta", bold:true}], - ["org-done", "DONE keyword", {fg:"sage", bold:true}], - ["org-link", "link", {fg:"blue"}], // base `link` - ["org-code", "inline code", {fg:"terracotta"}], - ["org-verbatim", "verbatim", {fg:"steel"}], - ["org-block", "src block body", {fg:"white", bg:"bg-dim"}], - ["org-block-begin-line","block delim", {fg:"pewter", bg:"bg-dim"}], - ["org-table", "table", {fg:"steel"}], - ["org-date", "timestamp", {fg:"steel"}], - ["org-tag", "tag", {fg:"tan"}], - ["org-special-keyword","keyword/drawer", {fg:"pewter"}], - ["org-meta-line", "#+meta line", {fg:"pewter"}], - ["org-checkbox", "checkbox", {fg:"gold"}], - ["org-headline-done", "done headline", {fg:"pewter"}], - ], - preview: "org" // names the preview renderer - }, - // magit, elfeed, ... added later with the same shape -} -#+end_src - -Defaults reference palette *names* (blue, gold, ...) resolved to hexes at load, -so a curated app seeds sensibly from the current palette. The user reassigns -any face from the palette dropdowns exactly like the other tables. - -State mirrors the other tiers: a =PKGMAP= of -={app: {face: {fg, bg, bold, italic, inherit, height, source}}}=, edited live, rendered into -the table and the preview. The =APPS= block above shows ~18 org faces only as a -shape illustration; the real org entry is the complete set below. - -** Data model — org face set (complete) - -Per the completeness decision, org's table lists org's entire own =defface= set -(org-faces.el + org-agenda.el), ~88 faces, grouped. Seed defaults for the -prominent groups; the long tail seeds to fg or an =inherit= of its group base, -which the user overrides. The groups (face names verbatim from the running -Emacs): - -- *Document:* org-document-title, org-document-info, org-document-info-keyword -- *Headings:* org-level-1 .. org-level-8, org-headline-todo, org-headline-done -- *Status / keywords:* org-todo, org-done, org-priority, org-tag, org-tag-group, - org-special-keyword, org-drawer, org-property-value, org-checkbox, - org-checkbox-statistics-todo, org-checkbox-statistics-done, org-warning -- *Links / dates / refs:* org-link, org-footnote, org-date, org-sexp-date, - org-date-selected, org-target, org-macro, org-cite, org-cite-key -- *Blocks / code / quote:* org-block, org-block-begin-line, org-block-end-line, - org-code, org-verbatim, org-inline-src-block, org-quote, org-verse, - org-latex-and-related -- *Tables / columns:* org-table, org-table-header, org-table-row, org-formula, - org-column, org-column-title -- *Lists / meta / structure:* org-list-dt, org-meta-line, org-ellipsis, - org-hide, org-indent, org-archived, org-default, org-dispatcher-highlight -- *Agenda — structure & dates:* org-agenda-structure, - org-agenda-structure-secondary, org-agenda-structure-filter, org-agenda-date, - org-agenda-date-today, org-agenda-date-weekend, org-agenda-date-weekend-today, - org-agenda-current-time, org-agenda-done, org-agenda-dimmed-todo-face -- *Agenda — calendar & filters:* org-agenda-calendar-event, - org-agenda-calendar-sexp, org-agenda-calendar-daterange, org-agenda-diary, - org-agenda-clocking, org-agenda-column-dateline, org-agenda-restriction-lock, - org-agenda-filter-category, org-agenda-filter-effort, org-agenda-filter-regexp, - org-agenda-filter-tags -- *Scheduling / deadlines / clock:* org-scheduled, org-scheduled-today, - org-scheduled-previously, org-upcoming-deadline, org-upcoming-distant-deadline, - org-imminent-deadline, org-time-grid, org-clock-overlay, org-mode-line-clock, - org-mode-line-clock-overrun - -The org *preview* below stays a curated document exercising the prominent -faces; the *table* carries the complete set so every face is assignable, even -the ones the preview doesn't draw. magit and elfeed get the same treatment -(complete own-defface set in the table, a bespoke preview for the common faces). - -* The org preview - -A mock org document painted from PKGMAP["org-mode"] plus the palette ground/fg. -One bespoke renderer (=renderOrgPreview()=) drawing a representative document: - -#+begin_example -#+TITLE: Project Notes <- org-document-title -#+AUTHOR: ... <- org-meta-line / document-info - -* Inbox :work: <- org-level-1 + org-tag -** TODO Draft the spec <- org-level-2 + org-todo - SCHEDULED: <2026-06-08 Sun> <- org-special-keyword + org-date -** DONE Ship the tool <- org-level-2 + org-done (headline-done) -*** Heading three <- org-level-3 - A line with =inline code=, <- org-code - ~verbatim~, and a [[link]]. <- org-verbatim + org-link - - [X] a checkbox item <- org-checkbox - - #+begin_src elisp <- org-block-begin-line - (message "hi") <- org-block - #+end_src <- org-block-end-line - - | name | hex | <- org-table (header row org-table-header) - |------+---------| - | blue | #67809c | -#+end_example - -Each marked element is a span colored from the corresponding PKGMAP face. The -preview rebuilds whenever a package face or the palette changes, same as the -mock frame. - -org, magit, and elfeed get bespoke preview renderers (magit -> a status buffer -mock, elfeed -> a search-list mock). Every *other* package is still fully -themeable: its face *table* is always present and editable, only the rich -*preview* is replaced by a generic fallback — each face's name rendered in its -own colors on the ground. So a user can theme every package they have the -moment its face list is added; the bespoke preview is a polish layer on top, not -a gate. This is the v1 answer to "some will want to touch every package." - -* Export schema - -=theme.json= gains a =packages= key: - -#+begin_src json -{ - "name": "dupre", - "palette": [...], - "assignments": {...}, - "bold": [...], "italic": [...], - "ui": {...}, - "packages": { - "org-mode": { - "org-level-1": {"fg":"#67809c","bg":null,"bold":true,"italic":false,"inherit":null,"height":1.3}, - "org-level-2": {"fg":"#e8bd30","bg":null,"bold":false,"italic":false,"inherit":"org-level-1","height":1.2}, - "org-todo": {"fg":"#cb6b4d","bg":null,"bold":true,"italic":false,"inherit":null} - } - } -} -#+end_src - -=inherit= is optional and =null= when absent. When set, the converter writes -=:inherit PARENT= plus only the overridden attributes. - -Only faces the user actually touched (or the curated defaults) are written. The -build step's converter sets each as a normal face. Backward compatible: a file -without =packages= loads fine. - -* Build-step consumption - -The eventual =theme.json= -> =dupre-*.el= converter already owns tiers 1 and 2. -Tier 3 adds, per package face: - -#+begin_src elisp -(org-level-1 ((t (:foreground "#67809c" :weight bold)))) -(org-todo ((t (:foreground "#cb6b4d" :weight bold)))) -#+end_src - -No new converter machinery — package faces are just more faces. This is the -TDD-worthy part (JSON in, valid faces out), same as the rest of the converter. - -* Scope for v1 - -- Build the section, the app dropdown, and the face tables + previews for the - three v1 apps: org-mode (incl. org-agenda), magit, elfeed. -- org's table carries its complete own-defface set (~88 faces, grouped above), - seeded with defaults; the org preview draws the prominent ones. -- Every other installed package is reachable in the dropdown with an editable - face table and the generic fallback preview, so any package can be themed. -- Wire export/import of the =packages= key (with the optional =inherit= and - =height= fields). -- Leave the converter for the separate build-step task (Elisp, per Craig); the - spec only needs the schema to be right. - -* Implementation phases - -Phased so each step ships without a broken intermediate, and the three bespoke -apps don't wait on the all-package inventory. - -1. *State + schema.* Add =PKGMAP= - ({app:{face:{fg,bg,bold,italic,inherit,height,source}}}) and the =APPS= - registry. Extend export/import with the =packages= key; old JSON (no - =packages=) still imports cleanly. No UI yet. -2. *Curated app data.* Complete own-defface face lists + seeded defaults for org - (incl. org-agenda), magit, elfeed, in =APPS= — including heading heights and - the fixed-pitch inherits. Pure data. -3. *Package face table UI.* App selector; grouped rows; fg/bg dropdowns + bold / - italic toggles + optional inherit + a relative-height stepper; per-face and - per-app reset; a text filter (org/magit are large); a contrast readout per - fg/bg. Built on a generalized face-control helper shared with the ui-faces - table, not a fork of =uiSelect=. -4. *Org preview.* =renderOrgPreview()=, live, refreshing on palette/face change. -5. *Magit + elfeed previews.* Bespoke mocks (magit status buffer, elfeed search - list). -6. *Generated all-package inventory* (the "theme every package" path). A build - step queries Emacs for installed packages' faces grouped by package, writes a - data file =generate.py= embeds; the dropdown then lists every package with an - editable table + the generic fallback preview. Lands after phases 1-5 without - blocking the three bespoke apps. -7. *Docs + validation.* README =packages= schema + inventory-refresh command; - regenerate HTML; fixtures + manual checklist. - -Phases 1-5 deliver the three high-value apps fully; phase 6 opens the long tail; -phase 7 documents. - -* Package face inventory source - -*Hybrid, split across phases.* Curated app metadata (org/magit/elfeed: complete -face lists, seeded defaults, bespoke previews) is hand-maintained in =APPS= and -ships in phases 2-5. A *generated* =PACKAGE_FACE_INVENTORY= — produced by a build -step that asks the running Emacs for each installed package's faces grouped by -package, written to a JSON/Python data file =generate.py= embeds — supplies the -generic fallback packages and ships in phase 6. - -Why hybrid and split: the static generator can't discover packages at runtime in -the browser, so "theme every package" needs a generated inventory; but making the -full inventory a prerequisite for the three bespoke apps invites the scope -explosion the review flagged. Splitting it lets v1's core ship first; the -inventory is additive. - -The generated inventory is an *input artifact* to =generate.py= (a committed data -file refreshed by an explicit command), never browser-side discovery. The refresh -command's dependency on a loaded Emacs config is documented. - -Decided (Craig, 2026-06-08): hybrid-and-split, as above. - -* State and export policy - -Each package face object carries a =source= marker so export can tell a seeded -default from a user edit from a deliberate clear: - -#+begin_src js -{ fg:"#67809c", bg:null, bold:true, italic:false, underline:false, strike:false, inherit:null, height:1.0, source:"default" } -// underline / strike: booleans -> the converter writes :underline t / :strike-through t -// height: float multiplier off the base font (1.0 = unchanged); see Relative height -// source: "default" (seeded) | "user" (edited) | "cleared" (user removed a default) -#+end_src - -Export policy: - -- Write =default= and =user= entries. -- Write =cleared= entries — they must suppress a curated default on reload. -- Omit untouched faces that have no default. -- When =inherit= is set, write =inherit= plus only the explicit overrides. -- Write =height= only when it differs from 1.0. -- Preserve package faces present in an imported file but absent from the current - inventory (or warn) — don't silently drop them. - -Import tolerates a missing =packages= key, unknown app keys, unknown face keys, -a missing =inherit=, and a missing =height= (defaults 1.0). A deleted palette -color leaves package face references in the same "(gone)" recoverable state -syntax colors use. Inheritance cycles are rejected (treated as no inheritance) -during preview resolution. - -* Relative height - -Some faces want to be bigger than body text — org headings above all, also -=org-document-title=. A face's =height= field is a *float multiplier* off the -base font (=1.3= = 1.3× the running font, whatever it is), never an absolute -point size, so it stays portable across fonts and machines. =1.0= means -unchanged. The base monospace family is *not* a theme/tool concern — it lives in -=modules/font-config.el=; the tool owns only relative size. - -*Height does not cascade through =inherit=.* This is the one attribute resolved -directly off the face, not through its inherit chain. Emacs multiplies float -heights along an inherit chain, so a level-2 that inherits level-1 (1.3) and -also sets 1.1 would render at 1.43 — almost never what's wanted. Headings should -each size off the *body*, so the seeded defaults set =org-level-1= 1.3, -=org-level-2= 1.2, =org-level-3= 1.15, etc., each independent, and the tool reads -=height= from the face while still resolving *color* through inherit. - -- *Schema:* the =height= float on the face object (above), default 1.0, omitted - from export when 1.0. -- *UI:* a small numeric stepper in the face row (range ~0.8–2.0, step 0.05); - meaningful only for the size-bearing faces but shown on every row at 1.0. -- *Preview:* the row renders at the scaled =font-size= so a heading visibly - grows in the mock. -- *Converter:* writes =:height 1.3= into the face spec when ≠ 1.0. - -Related, same mechanism: org's mixed-pitch faces (=org-block=, =org-code=, -=org-verbatim=, =org-table=, =org-meta-line=, =org-date=) seed =inherit: -"fixed-pitch"= so they stay monospace when a buffer switches to a proportional -font via =variable-pitch-mode= / =mixed-pitch=. The proportional family itself -stays in =font-config.el= (the presets already carry =:variable-pitch-family=); -the tool only carries the fixed-pitch inherit relationship, shown like any other -inherited value. - -* Acceptance criteria - -- Existing =dupre.json= (no =packages= key) imports cleanly. -- Export includes =packages= once defaults or edits exist; - =fg/bg/bold/italic/inherit/height/source= round-trip through import/export. -- A face =height= renders as a scaled font-size in the preview (heading visibly - grows) and is read off the face, not cascaded through =inherit=. -- org, magit, elfeed appear in the app selector with complete grouped face tables. -- (phase 6) generic inventory packages appear with editable tables + fallback - previews, the fallback visibly labeled as generic. -- A palette color update propagates to package faces the same way it does to - syntax / ui faces. -- =python3 scripts/theme-studio/generate.py= rebuilds =theme-studio.html=. -- README documents the =packages= schema, inheritance, and the inventory source. - -* Extensibility (adding the next app) - -1. Add an entry to =APPS= (label, curated face list with palette-name defaults, - preview key). -2. Optionally write a bespoke preview renderer; until then the generic fallback - renders. -3. Nothing else changes — the dropdown, table, export, and import are all - data-driven off =APPS= / =PKGMAP=. - -* Agreed decisions - -Craig's answers to the first review round, baked in (the body sections above -reflect these; this records the decisions): - -1. *Curated set is complete, not iterative.* For org, list its *entire* own - defface set (org-faces.el + org-agenda.el), ~88 faces, not a hand-picked - ~18. The user wants every choice present, not a set that grows on demand. - See "Data model — org face set" for the full grouped list. -2. *Seed curated defaults.* Seed sensible fg/bg and weight per face (headings, - title, TODO/DONE bold; agenda dates and deadlines colored by role). The user - reassigns from there. -3. *App order: org, magit, elfeed for v1.* Then the rest one at a time, drawn - from the packages Craig actually runs: calibredb, ghostel, mu4e, the IRC - client, org-drill, dirvish + dired, slack. A finite "most-used" list gets - picked later; we do not try to do everything at once. -4. *Generic fallback is real, not display-only.* Any package not given a - bespoke preview still gets a fully editable face table (so a user can theme - *every* package they have); only the rich preview is missing, replaced by a - swatch-in-context fallback. Bespoke previews ship for org, magit, elfeed. - -* Inheritance representation (decided) - -Each face carries an optional =inherit= field naming another face (or =null=). -The face's own =fg/bg/bold/italic= are *overrides* layered on top of what it -inherits. - -#+begin_src js -["org-level-2", "heading 2", {inherit:"org-level-1", fg:"gold"}] -// exports as: (org-level-2 ((t (:inherit org-level-1 :foreground "#e8bd30")))) -["org-agenda-date-today", "agenda today", {inherit:"org-agenda-date", bold:true}] -// exports as: (org-agenda-date-today ((t (:inherit org-agenda-date :weight bold)))) -#+end_src - -*Decision (Craig, 2026-06-07): model inheritance, show the resolved result, -override what looks bad.* The point is to see what a face ends up looking like -when it inherits, judge it in the preview, and fix only the ones that look -wrong: - -- Each face's *effective* color is resolved through its inherit chain and shown - in its table row, visibly marked "inherited from <face>" so it reads as - not-explicitly-set. The face's own =fg/bg/bold/italic= are overrides layered - on top. -- The mock preview on the right renders every face with its effective color, so - inherited faces are judged in context, not in the abstract. -- Overriding is one action: assign a color (or toggle weight) and the row flips - from inherited to explicit (=source: "user"=), shown at once in the table and - preview. -- Export writes =:inherit PARENT= for faces left inherited (carrying the - relationship, so they follow the parent the theme also sets) and explicit - attributes for the ones overridden — never a frozen copy of an inherited - color. - -Seeded defaults express the inherit relationships org itself uses out of the box -(heading levels off a base, =org-agenda-date= variants off =org-agenda-date=, -=org-code= / =org-verbatim= off =fixed-pitch=), so the table opens showing -org's real cascade, which the user then tunes. Inheritance cycles resolve to no -inheritance. - -* Custom color picker (proposal) - -Craig wants a custom in-page color picker to replace the native browser swatch. -The native =<input type=color>= opens the OS color chooser, which the page -cannot size or restyle; a custom picker is the only way to get a larger, -on-theme picker and to show the palette/contrast in the picker itself. - -Proposed widget — a popup anchored to the swatch, drawn in-page: - -- A *saturation/value square* (click or drag to set S and V) plus a *hue - slider* down the side. Standard HSV picker geometry. -- A *hex field* synced both ways with the square/slider (already exists in the - add-color row; the picker writes to it). -- The current *palette* shown as clickable chips along the bottom, so picking - an existing color is one click and the overlap problem (many roles, one - color) is visible while choosing. -- A live *contrast readout* against the current background (ratio + AAA / AA / - FAIL) updating as the color moves, so a color is judged for legibility at - pick time, not after assignment. -- Sized generously (the native popup's size was the original complaint); opens - on click of the swatch, closes on pick or click-away. - -Implementation: ~120 lines of vanilla JS/canvas (or CSS gradients) for the -square + slider, reusing the existing =rl()= / =contrast()= / =rating()= -helpers for the readout and =normHex()= for the field sync. No dependency. It -replaces the =<input type=color>= in the add-color row and, later, becomes the -picker the package-face dropdowns can also invoke. - -It stays *off* the tier-3 critical path: a separate task before or after the -package-face build, not folded into it, since folding it in widens the blast -radius for no dependency benefit. Build it only sooner if package-face editing -proves painful with the native swatch. - -Decided (Craig, 2026-06-08): after tier 3, as its own task. - -* Files touched - -- =scripts/theme-studio/generate.py= — the section, =APPS= data, the package - face table, =renderOrgPreview()=, export/import of =packages=. -- =scripts/theme-studio/theme-studio.html= — regenerated. -- (later) the =theme.json= -> =dupre-*.el= converter (Elisp) — consumes - =packages=. - -* Review dispositions - -Codex review (2026-06-07), =Not ready=. Findings processed: - -- *Modified — generated inventory (high, blocking).* Codex recommended a hybrid - inventory so every installed package is reachable. Accepted the hybrid, but - *split* it: the generated all-package inventory is its own phase (6), after the - three bespoke apps (phases 1-5), rather than a v1 prerequisite. Reason: Codex - named scope explosion as the main risk, and gating org/magit/elfeed on a - full-inventory mechanism is exactly that. The split keeps v1's core shippable - and makes "theme every package" additive. Confirm-with-Craig flagged as an - open. -- *Modified — preview depth (UX obs).* Codex suggested level 4-8 examples in the - org preview. The preview stays a curated document drawing the prominent faces - (incl. a couple of deeper levels as representative); the complete level set - lives in the *table*, which is where every face is assignable. A full 8-level - preview block would bloat the mock without adding assignability. - -Everything else in the review accepted as written: implementation phases, -acceptance criteria, the =source= state field + export policy, curated-vs-complete -wording, keeping the custom picker off the critical path, unknown-import -preservation, the test-strategy fixtures, and the UX/architecture/robustness -observations (grouping + filter, reset controls, package-fg/bg contrast readout, -generalized face-control helper, package style kept inside the package object, -"(gone)" recoverable state, inheritance-cycle rejection). - -* Review and iteration history - -** 2026-06-07 Sun @ 18:17:14 -0500 — Claude Code (emacs-d) — author + responder -- *What:* Folded Craig's first-round cj-comment answers into the body. Curated - org set changed from ~18 to org's complete own-defface set (~88, grouped, incl. - org-agenda). v1 apps fixed to org/magit/elfeed with the rest deferred to a - one-at-a-time list. Generic fallback clarified as a fully editable table for - every package (only the rich preview is bespoke). Answered the inheritance - question with an optional =inherit= field (absolute-default, opt-in cascade) - and added it to the schema. Added a custom-color-picker proposal at Craig's - request. Converted Open questions to Agreed decisions; two genuine opens - remain (inheritance confirm, picker timing). -- *Why:* Craig answered the five open questions inline and asked for the - spec-response fold plus a custom-picker proposal. Completeness over iterative - growth was his explicit call on the curated set. -- *Artifacts:* This spec. The converter language (Elisp) confirmed by Craig. - Note: Craig referenced a Codex spec review, but no - =theme-studio-package-faces-review.org= (or any =*-review.org=) exists in the - repo — the review file could not be found, so only the author-side cj-comment - fold was done; the Codex review remains to be processed when its file is - located. - -** 2026-06-07 Sun @ 18:20:16 -0500 — Codex — reviewer -- *What changed or was recommended:* Created the missing formal review and - marked the spec =Not ready=. The review recommends adding implementation - phases, acceptance criteria, an explicit package-face inventory source, and - default/user/cleared/inherited export semantics. -- *Why:* The direction is coherent and the first-round decisions are folded in, - but v1 now depends on behavior that is not yet implementable from the current - static generator without a defined inventory and state/export contract. -- *Artifacts:* [[file:theme-studio-package-faces-spec-review.org][theme-studio-package-faces-spec-review.org]] - -** 2026-06-07 Sun @ 18:28:02 -0500 — Claude Code (emacs-d) — responder -- *What:* Ran spec-response against the Codex review. Added Implementation - phases (7), Package face inventory source (hybrid, split), State and export - policy (=source= field + export rules), and Acceptance criteria. Wove in the - UX/architecture/robustness observations. Two reasoned modifies (generated - inventory split into its own phase; org preview stays curated rather than - all-8-levels), everything else accepted — see Review dispositions. Status - moved to =Ready with caveats=. Deleted the review file. -- *Why:* The four blocking findings were real implementation-contract gaps; the - inventory split answers Codex's own scope-explosion warning while still - reaching "theme every package." -- *Artifacts:* This spec (Review dispositions section); review file deleted per - the spec-response close-out. Three opens remain for Craig (inheritance confirm, - hybrid-inventory confirm, picker timing). - -** 2026-06-08 Mon @ 00:12:38 -0500 — Codex — reviewer -- *What changed or was recommended:* Reran spec-review after the response pass. - Assigned rubric =Ready= and did not create a new review file. The previous - blockers are now addressed: implementation phases, acceptance criteria, - hybrid/split inventory source, package-face state/export semantics, task - tracking, and the open inheritance/inventory/picker decisions are resolved. -- *Why:* The spec now gives an implementer concrete behavior, phase boundaries, - validation criteria, and deferred-work handling without forcing product - decisions during implementation. -- *Artifacts:* This spec; implementation tasks in [[file:../../todo.org][todo.org]]. - -** 2026-06-08 Mon @ 00:38:23 -0500 — Claude Code (emacs-d) — author -- *What:* Added a relative =height= field to the face schema (float multiplier - off the base font, default 1.0, omitted at 1.0), a new "Relative height" - section, a per-face stepper in the table, preview scaling, and converter - output. Established the rule that =height= is read off the face and does *not* - cascade through =inherit= (Emacs multiplies float heights along the chain). - Noted the mixed-pitch =fixed-pitch= inherits as the same-mechanism companion. - Brought Phase 1's shipped schema plumbing in line with the new field. -- *Why:* Craig asked to fold height in — it matters for org headings above all. - Font *family* stays in =modules/font-config.el=; the theme owns relative size - and the fixed-pitch inherit relationships only. -- *Artifacts:* This spec; =scripts/theme-studio/generate.py= phase-1 plumbing. |
