From f2654a0083a94d0e0dc7cbb6094aae6116a1c318 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Mon, 8 Jun 2026 08:56:47 -0500 Subject: refactor(theme-studio): rename theme-selector to theme-studio The tool authors themes from scratch -- palette, faces across every tier, live preview, export to a loadable deftheme. It never selects among existing themes, so "selector" mis-described it. Renamed the directory, the generated HTML and its title, the design spec, and every reference in the code, README, tests, and todo. No behavior change. --- docs/design/theme-selector-package-faces-spec.org | 586 ----------- docs/design/theme-studio-package-faces-spec.org | 586 +++++++++++ scripts/theme-selector/README.md | 166 --- scripts/theme-selector/build-inventory.el | 31 - scripts/theme-selector/build-theme.el | 244 ----- scripts/theme-selector/generate.py | 1116 --------------------- scripts/theme-selector/package-inventory.json | 723 ------------- scripts/theme-selector/samples.py | 269 ----- scripts/theme-selector/theme-selector.html | 738 -------------- scripts/theme-studio/README.md | 166 +++ scripts/theme-studio/build-inventory.el | 31 + scripts/theme-studio/build-theme.el | 244 +++++ scripts/theme-studio/generate.py | 1116 +++++++++++++++++++++ scripts/theme-studio/package-inventory.json | 723 +++++++++++++ scripts/theme-studio/samples.py | 269 +++++ scripts/theme-studio/theme-studio.html | 738 ++++++++++++++ tests/test-build-theme.el | 8 +- 17 files changed, 3877 insertions(+), 3877 deletions(-) delete mode 100644 docs/design/theme-selector-package-faces-spec.org create mode 100644 docs/design/theme-studio-package-faces-spec.org delete mode 100644 scripts/theme-selector/README.md delete mode 100644 scripts/theme-selector/build-inventory.el delete mode 100644 scripts/theme-selector/build-theme.el delete mode 100644 scripts/theme-selector/generate.py delete mode 100644 scripts/theme-selector/package-inventory.json delete mode 100644 scripts/theme-selector/samples.py delete mode 100644 scripts/theme-selector/theme-selector.html create mode 100644 scripts/theme-studio/README.md create mode 100644 scripts/theme-studio/build-inventory.el create mode 100644 scripts/theme-studio/build-theme.el create mode 100644 scripts/theme-studio/generate.py create mode 100644 scripts/theme-studio/package-inventory.json create mode 100644 scripts/theme-studio/samples.py create mode 100644 scripts/theme-studio/theme-studio.html diff --git a/docs/design/theme-selector-package-faces-spec.org b/docs/design/theme-selector-package-faces-spec.org deleted file mode 100644 index def6a430f..000000000 --- a/docs/design/theme-selector-package-faces-spec.org +++ /dev/null @@ -1,586 +0,0 @@ -#+TITLE: theme-selector — 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-selector (scripts/theme-selector/) 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-selector 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 -

package faces

-[ application: (org-mode v) ] -
- 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) -
-#+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-selector/generate.py= rebuilds =theme-selector.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 " 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 == 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 == 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-selector/generate.py= — the section, =APPS= data, the package - face table, =renderOrgPreview()=, export/import of =packages=. -- =scripts/theme-selector/theme-selector.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-selector-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-selector-package-faces-spec-review.org][theme-selector-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-selector/generate.py= phase-1 plumbing. diff --git a/docs/design/theme-studio-package-faces-spec.org b/docs/design/theme-studio-package-faces-spec.org new file mode 100644 index 000000000..7f00b3279 --- /dev/null +++ b/docs/design/theme-studio-package-faces-spec.org @@ -0,0 +1,586 @@ +#+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 +

package faces

+[ application: (org-mode v) ] +
+ 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) +
+#+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 " 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 == 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 == 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. diff --git a/scripts/theme-selector/README.md b/scripts/theme-selector/README.md deleted file mode 100644 index 8f63383c1..000000000 --- a/scripts/theme-selector/README.md +++ /dev/null @@ -1,166 +0,0 @@ -# theme-selector - -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/-*.el`. - -## Run - -```bash -python3 generate.py # writes theme-selector.html beside this script -``` - -Then open it in Chrome (Firefox had color-rendering flakiness during design): - -```bash -WAYLAND_DISPLAY=wayland-1 google-chrome-stable theme-selector.html -``` - -During color work, disable Hyprland inactive-window dimming so colors read true: - -```bash -hyprctl keyword decoration:dim_inactive false -``` - -## Files - -- `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 - -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. - -Twenty applications have bespoke previews that exercise nearly all of their -faces: org-mode (a document plus an agenda view), magit (a status buffer plus -blame, reflog, sequence, bisect, and signature rows), elfeed (a search list and -log), ghostel (a mock terminal with the 16 ANSI colors), mu4e (a headers list, -message view, and compose stub), dashboard, lsp-mode (signatures, inlay hints, -symbol highlights, rename), git-gutter, flycheck (a diagnostic line plus an -error-list buffer), dired, dirvish (attribute columns, vc states, media, proc, -narrow), calibredb (a library listing and detail view), erc (an IRC channel), -org-drill (a cloze flashcard), org-noter, signel (a Signal chat), pearl (a -ticket), slack (a channel with mrkdwn, attachments, blocks, and dialogs), and -telega (chat entities, reactions, buttons, and webpage rendering), and shr (the -built-in HTML renderer behind nov, eww, elfeed's article view, and HTML mail, so -theming it themes all of them). 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. Clicking a face row flashes that face in the preview, and clicking -a preview element flashes its row. - -**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 - -The export (and what a build step consumes): - -```json -{ - "name": "dupre", - "palette": [["#67809c", "blue"], ["#e8bd30", "gold"]], - "assignments": {"kw": "#67809c", "str": "#5d9b86", "bg": "#000000", "p": "#ffffff"}, - "bold": ["kw", "fnd"], - "italic": [], - "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, - "underline": false, "strike": false, - "inherit": null, "height": 1.3, "source": "default"} - } - } -} -``` - -- `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`, - `underline`, `strike`, 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 converter writes `underline` as - `:underline t` and `strike` as `:strike-through t`. -- 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. - -## Build step — `build-theme.el` - -`build-theme.el` converts a `theme.json` into a single self-contained -`themes/-theme.el` deftheme. JSON in, valid Emacs faces out, across all -four tiers: `default` from `assignments.bg`/`.p`, the syntax categories mapped -to their font-lock / tree-sitter faces (with the `bold`/`italic` sets applied), -the UI faces passed through, and the package faces with `:inherit`/`:height` -and weight/slant written. - -```bash -emacs --batch -l scripts/theme-selector/build-theme.el \ - --eval '(build-theme/convert-file "scripts/theme-selector/dupre-revised.json" "themes")' -``` - -Output is a flat generated deftheme, not the palette/faces/theme trio the -original dupre ships — a `theme.json` carries resolved per-face hex, not dupre's -semantic-mapping layer, so a flat deftheme is the faithful output and never -clobbers the curated dupre files. - -One mapping limitation: the `dec` (decorator) syntax key has no independent -Emacs face. Emacs renders decorators with `font-lock-type-face`, which the `ty` -key already owns, so `dec` is omitted from the output and decorators follow the -type color (as they do in stock Emacs). Tests live in -`tests/test-build-theme.el` (Normal / Boundary / Error, plus a WCAG-contrast -assertion on the round-tripped result). diff --git a/scripts/theme-selector/build-inventory.el b/scripts/theme-selector/build-inventory.el deleted file mode 100644 index 52e14baa1..000000000 --- a/scripts/theme-selector/build-inventory.el +++ /dev/null @@ -1,31 +0,0 @@ -;;; build-inventory.el --- emit package->faces inventory for theme-selector -*- lexical-binding: t -*- -;;; Commentary: -;; Loaded into a running Emacs (emacsclient -e '(load ".../build-inventory.el")') -;; to write package-inventory.json next to itself: a JSON object mapping each -;; installed (elpa/straight) package to the faces it defines, grouped by the -;; package that owns the face's definition file. Built-in faces are skipped. -;; generate.py embeds the JSON so the theme-selector dropdown can reach every -;; installed package (tier-3 phase 6, the "theme every package" path). -;;; Code: - -(require 'json) - -(let ((h (make-hash-table :test 'equal))) - (dolist (f (face-list)) - (let* ((file (ignore-errors (symbol-file f 'defface))) - (pkg (and (stringp file) - (string-match "/\\(?:elpa\\|straight/build\\|site-lisp\\)/\\([a-zA-Z0-9._-]+?\\)-[0-9][^/]*/" file) - (match-string 1 file)))) - (when pkg (push (symbol-name f) (gethash pkg h))))) - (let (al) - (maphash (lambda (k v) (push (cons (intern k) (sort v #'string<)) al)) h) - (setq al (sort al (lambda (a b) (string< (symbol-name (car a)) (symbol-name (car b)))))) - (with-temp-file (expand-file-name - "package-inventory.json" - (file-name-directory (or load-file-name buffer-file-name - "~/.emacs.d/scripts/theme-selector/"))) - (let ((json-encoding-pretty-print t)) - (insert (json-encode al) "\n"))))) - -(provide 'build-inventory) -;;; build-inventory.el ends here diff --git a/scripts/theme-selector/build-theme.el b/scripts/theme-selector/build-theme.el deleted file mode 100644 index fe080c0d1..000000000 --- a/scripts/theme-selector/build-theme.el +++ /dev/null @@ -1,244 +0,0 @@ -;;; build-theme.el --- Convert a theme-selector theme.json into a deftheme -*- lexical-binding: t -*- - -;; Author: Craig Jennings - -;;; Commentary: - -;; The last link in the theme-selector pipeline: turn a theme.json exported by -;; the tool (see scripts/theme-selector/README.md and -;; docs/design/theme-selector-package-faces-spec.org) into a single, -;; self-contained, loadable Emacs deftheme written to themes/-theme.el. -;; -;; Four tiers come out of the JSON: -;; - default -- background from assignments.bg, foreground from .p -;; - syntax -- assignments. -> font-lock / tree-sitter faces, with -;; the bold / italic category sets applied -;; - ui -- the ui keys are already real face names; fg/bg passthrough -;; - packages -- per-package face specs with :inherit / :height / weight / -;; slant -;; -;; Usage (from a shell or a running Emacs): -;; -;; emacsclient -e '(progn (load ".../build-theme.el") -;; (build-theme/convert-file ".../dupre-revised.json"))' -;; -;; or in batch: -;; -;; emacs --batch -l build-theme.el \ -;; --eval '(build-theme/convert-file "dupre-revised.json" "themes")' -;; -;; The output is a flat generated deftheme, not the hand-authored -;; palette/faces/theme trio that the original dupre theme ships -- a theme.json -;; carries resolved per-face hex, not dupre's semantic-mapping layer, so a flat -;; deftheme is the faithful output and never clobbers the curated dupre files. - -;;; Code: - -(require 'json) -(require 'subr-x) - -(defconst build-theme/--syntax-face-map - '((kw . (font-lock-keyword-face)) - (bi . (font-lock-builtin-face)) - (pp . (font-lock-preprocessor-face)) - (fnd . (font-lock-function-name-face)) - (fnc . (font-lock-function-call-face)) - (ty . (font-lock-type-face)) - (prop . (font-lock-property-name-face font-lock-property-use-face)) - (con . (font-lock-constant-face)) - (num . (font-lock-number-face)) - (str . (font-lock-string-face)) - (esc . (font-lock-escape-face)) - (re . (font-lock-regexp-face)) - (doc . (font-lock-doc-face)) - (cm . (font-lock-comment-face)) - (cmd . (font-lock-comment-delimiter-face)) - (var . (font-lock-variable-name-face font-lock-variable-use-face)) - (op . (font-lock-operator-face)) - (punc . (font-lock-punctuation-face font-lock-bracket-face - font-lock-delimiter-face font-lock-misc-punctuation-face))) - "Map each theme.json syntax-category key to the font-lock faces it colors. -A category may fan out to several faces (e.g. punc covers bracket and -delimiter too). The dec (decorator) key is deliberately absent: Emacs has -no dedicated decorator face -- it renders decorators with -`font-lock-type-face', which the ty key already owns -- so coloring dec -independently is not possible without clobbering types.") - -;;; --------------------------------------------------------------------------- -;;; Pure helpers - -(defun build-theme/--hex-p (s) - "Non-nil when S is a \"#rrggbb\" hex color string." - (and (stringp s) (string-match-p "\\`#[0-9a-fA-F]\\{6\\}\\'" s))) - -(defun build-theme/--attrs (inherit fg bg bold italic underline strike height) - "Build a face-attribute plist from the given fields, in canonical order. -INHERIT is a face symbol or nil. FG and BG are hex strings or nil. BOLD, -ITALIC, UNDERLINE, and STRIKE are booleans. HEIGHT is a float multiplier; 1.0 -(or nil) is omitted as the default. Only set attributes are written, so a -fully-nil face yields an empty plist." - (let (plist) - (when (and height (numberp height) (/= height 1.0)) - (setq plist (list :height height))) - (when strike (setq plist (append (list :strike-through t) plist))) - (when underline (setq plist (append (list :underline t) plist))) - (when italic (setq plist (append (list :slant 'italic) plist))) - (when bold (setq plist (append (list :weight 'bold) plist))) - (when bg (setq plist (append (list :background bg) plist))) - (when fg (setq plist (append (list :foreground fg) plist))) - (when inherit (setq plist (append (list :inherit inherit) plist))) - plist)) - -(defun build-theme/--face-spec (face attrs) - "Wrap FACE and its ATTRS plist as a `custom-theme-set-faces' spec. -Return nil when ATTRS is empty, so cleared faces emit nothing." - (when attrs - (list face (list (list t attrs))))) - -(defun build-theme/--obj-get (obj key) - "Value of KEY in alist OBJ, or nil." - (cdr (assq key obj))) - -(defun build-theme/--inherit-symbol (value) - "Coerce an inherit VALUE (a face-name string, symbol, or nil) to a symbol." - (cond ((null value) nil) - ((symbolp value) value) - ((stringp value) (intern value)) - (t nil))) - -;;; --------------------------------------------------------------------------- -;;; Tiers - -(defun build-theme/--default-spec (assignments) - "Build the `default' face spec from ASSIGNMENTS bg / p." - (let ((bg (build-theme/--obj-get assignments 'bg)) - (fg (build-theme/--obj-get assignments 'p))) - (build-theme/--face-spec 'default (build-theme/--attrs nil fg bg nil nil nil nil nil)))) - -(defun build-theme/--syntax-face-specs (assignments bold italic) - "Build syntax-tier face specs from ASSIGNMENTS plus the BOLD and ITALIC sets. -BOLD and ITALIC are lists of category-key symbols. Each category fans out to -the font-lock faces in `build-theme/--syntax-face-map'." - (let (specs) - (dolist (pair build-theme/--syntax-face-map) - (let* ((cat (car pair)) - (faces (cdr pair)) - (hex (build-theme/--obj-get assignments cat))) - (when hex - (let ((attrs (build-theme/--attrs nil hex nil - (memq cat bold) (memq cat italic) nil nil nil))) - (dolist (face faces) - (when-let ((spec (build-theme/--face-spec face attrs))) - (push spec specs))))))) - (nreverse specs))) - -(defun build-theme/--ui-face-specs (ui) - "Build UI-tier face specs from the UI alist (face -> {fg,bg,bold,italic})." - (let (specs) - (dolist (entry ui) - (let* ((face (car entry)) - (obj (cdr entry)) - (attrs (build-theme/--attrs nil - (build-theme/--obj-get obj 'fg) - (build-theme/--obj-get obj 'bg) - (build-theme/--obj-get obj 'bold) - (build-theme/--obj-get obj 'italic) - (build-theme/--obj-get obj 'underline) - (build-theme/--obj-get obj 'strike) - nil))) - (when-let ((spec (build-theme/--face-spec face attrs))) - (push spec specs)))) - (nreverse specs))) - -(defun build-theme/--package-face-specs (packages) - "Build package-tier face specs from the PACKAGES alist (app -> face -> spec)." - (let (specs) - (dolist (app packages) - (dolist (entry (cdr app)) - (let* ((face (car entry)) - (obj (cdr entry)) - (attrs (build-theme/--attrs - (build-theme/--inherit-symbol (build-theme/--obj-get obj 'inherit)) - (build-theme/--obj-get obj 'fg) - (build-theme/--obj-get obj 'bg) - (build-theme/--obj-get obj 'bold) - (build-theme/--obj-get obj 'italic) - (build-theme/--obj-get obj 'underline) - (build-theme/--obj-get obj 'strike) - (build-theme/--obj-get obj 'height)))) - (when-let ((spec (build-theme/--face-spec face attrs))) - (push spec specs))))) - (nreverse specs))) - -(defun build-theme/--all-specs (data) - "Build the full ordered face-spec list from parsed theme.json DATA." - (let ((assignments (build-theme/--obj-get data 'assignments)) - (bold (mapcar #'intern (build-theme/--obj-get data 'bold))) - (italic (mapcar #'intern (build-theme/--obj-get data 'italic))) - (ui (build-theme/--obj-get data 'ui)) - (packages (build-theme/--obj-get data 'packages))) - (delq nil - (append - (list (build-theme/--default-spec assignments)) - (build-theme/--syntax-face-specs assignments bold italic) - (build-theme/--ui-face-specs ui) - (build-theme/--package-face-specs packages))))) - -;;; --------------------------------------------------------------------------- -;;; Rendering - -(defun build-theme/--render (name specs) - "Render a deftheme file body for theme NAME from face SPECS, as a string." - (concat - (format ";;; %s-theme.el --- Generated by theme-selector -*- lexical-binding: t -*-\n" name) - "\n;;; Commentary:\n" - (format ";; Generated from %s.json by scripts/theme-selector/build-theme.el.\n" name) - ";; Do not hand-edit; re-run the converter.\n" - "\n;;; Code:\n\n" - (format "(deftheme %s\n \"Generated by theme-selector.\")\n\n" name) - (format "(custom-theme-set-faces\n '%s\n" name) - ;; Each spec is quoted: custom-theme-set-faces is a function, so an - ;; unquoted (face ((t ...))) would be evaluated as a call. Specs hold - ;; only literal strings, symbols, and numbers, so a plain quote suffices. - (mapconcat (lambda (spec) (concat " '" (prin1-to-string spec))) specs "\n") - ")\n\n" - (format "(provide-theme '%s)\n" name) - (format ";;; %s-theme.el ends here\n" name))) - -(defun build-theme/--parse (json-file) - "Parse JSON-FILE into an alist, with null/false as nil and arrays as lists. -Signal a `file-missing' error when JSON-FILE does not exist." - (unless (file-readable-p json-file) - (signal 'file-missing (list "Cannot read theme.json" json-file))) - (with-temp-buffer - (insert-file-contents json-file) - (goto-char (point-min)) - (json-parse-buffer :object-type 'alist :array-type 'list - :null-object nil :false-object nil))) - -;;; --------------------------------------------------------------------------- -;;; Entry point - -(defun build-theme/convert-file (json-file &optional out-dir) - "Convert JSON-FILE (a theme.json export) into a deftheme file. -Write themes/-theme.el, where is the JSON name field, into -OUT-DIR (default: the themes/ directory of this repo). Return the written -path." - (let* ((data (build-theme/--parse json-file)) - (name (build-theme/--obj-get data 'name)) - (specs (build-theme/--all-specs data)) - (dir (or out-dir - (expand-file-name - "../../themes" - (file-name-directory (or load-file-name buffer-file-name - default-directory))))) - (out (expand-file-name (format "%s-theme.el" name) dir))) - (unless (and (stringp name) (string-match-p "\\`[a-zA-Z][a-zA-Z0-9-]*\\'" name)) - (error "Invalid theme name in %s: %S" json-file name)) - (make-directory dir t) - (with-temp-file out - (insert (build-theme/--render name specs))) - out)) - -(provide 'build-theme) -;;; build-theme.el ends here diff --git a/scripts/theme-selector/generate.py b/scripts/theme-selector/generate.py deleted file mode 100644 index 8e76c1740..000000000 --- a/scripts/theme-selector/generate.py +++ /dev/null @@ -1,1116 +0,0 @@ -import json, os -HERE=os.path.dirname(os.path.abspath(__file__)) -ns={} -src=open(os.path.join(HERE,'samples.py')).read() -exec(src[:src.index('cols=')], ns) -SAMPLES={"Elisp":ns['ELS'],"Go":ns['GOS'],"Python":ns['PYS'],"TypeScript":ns['TSS'],"Java":ns['JAS'],"C":ns['CS'],"C++":ns['CPS'],"Shell":ns['SHS']} -COLS=ns['COLS'] -MAP={k:v[0] for k,v in COLS.items()}; BOLD={k:v[1] for k,v in COLS.items()}; MAP['str']='#5d9b86'; MAP['bg']='#000000' -PALETTE=[["#67809c","blue"],["#e8bd30","gold"],["#9b5fd0","regal"],["#2ba178","emerald"],["#5d9b86","sage"], - ["#cb6b4d","terracotta"],["#be9e74","tan"],["#ffffff","white"],["#a9b2bb","silver"],["#838d97","steel"], - ["#5e6770","pewter"],["#2f343a","gunmetal"],["#264364","navy"],["#000000","ground"],["#1a1714","bg-dim"]] -CATS=[["bg","background (ground)","Aa Bb 123"],["p","fg · default text","other / whitespace"],["kw","keyword","class def if return"],["bi","builtin","len echo printf"], - ["pp","preprocessor","#include #define"],["fnd","function · def","resolve push"], - ["fnc","function · call","printf rsync get"],["dec","decorator","@dataclass"], - ["ty","type / class","int str Order Queue"],["prop","property / field","id name items"], - ["con","constant","None nil NULL true"],["num","number","8080 100 -1"], - ["str","string",'"dupre" "fmt"'],["esc","escape","\\n \\t"],["re","regexp","/^#[0-9a-f]+/"], - ["doc","docstring",'"""..."""'],["cm","comment","# reject nil"],["cmd","comment delim","# // ;;"], - ["var","variable / use","value key self"],["op","operator",": = -> =="], - ["punc","punctuation","{ } ( ) ;"]] -UI_FACES=[["cursor","cursor","Aa|"],["region","region (selection)","selected text"], - ["hl-line","hl-line (current line)","current line"],["highlight","highlight","hover"], - ["mode-line","mode-line","status active"],["mode-line-inactive","mode-line-inactive","status idle"], - ["fringe","fringe","| |"],["line-number","line-number"," 42"], - ["line-number-current-line","line-number-current-line","> 42"],["minibuffer-prompt","minibuffer-prompt","M-x "], - ["isearch","isearch (match)","match"],["lazy-highlight","lazy-highlight","other match"], - ["isearch-fail","isearch-fail","no match"],["show-paren-match","show-paren-match","( )"], - ["show-paren-mismatch","show-paren-mismatch",") ("],["link","link","https://"], - ["error","error","error!"],["warning","warning","warning"], - ["success","success","ok"],["vertical-border","vertical-border","|"]] -UIMAP={"cursor":{"fg":None,"bg":"#a9b2bb"},"region":{"fg":None,"bg":"#264364"}, - "hl-line":{"fg":None,"bg":"#1a1714"},"highlight":{"fg":None,"bg":"#2f343a"}, - "mode-line":{"fg":"#cdced1","bg":"#2f343a"},"mode-line-inactive":{"fg":"#838d97","bg":"#1a1714"}, - "fringe":{"fg":None,"bg":"#0d0b0a"},"line-number":{"fg":"#5e6770","bg":None}, - "line-number-current-line":{"fg":"#e8bd30","bg":"#1a1714"},"minibuffer-prompt":{"fg":"#67809c","bg":None}, - "isearch":{"fg":"#0d0b0a","bg":"#e8bd30"},"lazy-highlight":{"fg":"#0d0b0a","bg":"#838d97"}, - "isearch-fail":{"fg":"#cb6b4d","bg":None},"show-paren-match":{"fg":None,"bg":"#264364"}, - "show-paren-mismatch":{"fg":"#0d0b0a","bg":"#cb6b4d"},"link":{"fg":"#67809c","bg":None}, - "error":{"fg":"#cb6b4d","bg":None},"warning":{"fg":"#e8bd30","bg":None}, - "success":{"fg":"#5d9b86","bg":None},"vertical-border":{"fg":"#2f343a","bg":None}} -# link is underlined by default (matches the built-in link face). -UIMAP["link"]["underline"]=True -# Tier-3 package faces (Phase 2): complete own-defface sets for org/magit/elfeed, -# built from face-name lists + a curated seed-color map. Prominent faces are -# seeded; the long tail seeds to the default foreground for the user to tune. -ORG_FACES=("org-document-title org-document-info org-document-info-keyword " - "org-level-1 org-level-2 org-level-3 org-level-4 org-level-5 org-level-6 org-level-7 org-level-8 " - "org-headline-todo org-headline-done 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 org-link org-footnote org-date org-sexp-date " - "org-date-selected org-target org-macro org-cite org-cite-key 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 org-table org-table-header org-table-row org-formula org-column " - "org-column-title org-list-dt org-meta-line org-ellipsis org-hide org-indent org-archived " - "org-default org-dispatcher-highlight 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 " - "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 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").split() -MAGIT_FACES=("magit-section-heading magit-section-secondary-heading magit-section-heading-selection " - "magit-section-highlight magit-section-child-count magit-diff-added magit-diff-added-highlight " - "magit-diff-removed magit-diff-removed-highlight magit-diff-context magit-diff-context-highlight " - "magit-diff-file-heading magit-diff-file-heading-highlight magit-diff-file-heading-selection " - "magit-diff-hunk-heading magit-diff-hunk-heading-highlight magit-diff-hunk-heading-selection " - "magit-diff-hunk-region magit-diff-lines-heading magit-diff-lines-boundary magit-diff-base " - "magit-diff-base-highlight magit-diff-our magit-diff-our-highlight magit-diff-their " - "magit-diff-their-highlight magit-diff-conflict-heading magit-diff-conflict-heading-highlight " - "magit-diff-revision-summary magit-diff-revision-summary-highlight magit-diff-whitespace-warning " - "magit-diffstat-added magit-diffstat-removed magit-branch-current magit-branch-local " - "magit-branch-remote magit-branch-remote-head magit-branch-upstream magit-branch-warning " - "magit-head magit-tag magit-hash magit-filename magit-dimmed magit-keyword magit-keyword-squash " - "magit-refname magit-refname-stash magit-refname-wip magit-refname-pullreq magit-log-author " - "magit-log-date magit-log-graph magit-header-line magit-header-line-key magit-header-line-log-select " - "magit-process-ok magit-process-ng magit-mode-line-process magit-mode-line-process-error " - "magit-bisect-good magit-bisect-bad magit-bisect-skip magit-blame-heading magit-blame-highlight " - "magit-blame-hash magit-blame-name magit-blame-date magit-blame-summary magit-blame-dimmed " - "magit-blame-margin magit-cherry-equivalent magit-cherry-unmatched magit-signature-good " - "magit-signature-bad magit-signature-untrusted magit-signature-expired magit-signature-expired-key " - "magit-signature-revoked magit-signature-error magit-reflog-commit magit-reflog-amend " - "magit-reflog-merge magit-reflog-checkout magit-reflog-reset magit-reflog-rebase " - "magit-reflog-cherry-pick magit-reflog-remote magit-reflog-other magit-sequence-pick " - "magit-sequence-stop magit-sequence-part magit-sequence-head magit-sequence-drop magit-sequence-done " - "magit-sequence-onto magit-sequence-exec magit-left-margin").split() -ELFEED_FACES=("elfeed-search-date-face elfeed-search-title-face elfeed-search-unread-title-face " - "elfeed-search-feed-face elfeed-search-tag-face elfeed-search-unread-count-face " - "elfeed-search-filter-face elfeed-search-last-update-face elfeed-log-date-face " - "elfeed-log-error-level-face elfeed-log-warn-level-face elfeed-log-info-level-face " - "elfeed-log-debug-level-face").split() -ORG_SEED={ - "org-document-title":{"fg":"gold","bold":True,"height":1.5},"org-document-info":{"fg":"steel"}, - "org-document-info-keyword":{"fg":"pewter","inherit":"fixed-pitch"}, - "org-level-1":{"fg":"blue","bold":True,"height":1.3},"org-level-2":{"fg":"gold","height":1.2}, - "org-level-3":{"fg":"regal","height":1.15},"org-level-4":{"fg":"emerald","height":1.1}, - "org-level-5":{"fg":"terracotta"},"org-level-6":{"fg":"tan"},"org-level-7":{"fg":"sage"},"org-level-8":{"fg":"steel"}, - "org-headline-done":{"fg":"pewter"},"org-todo":{"fg":"terracotta","bold":True},"org-done":{"fg":"sage","bold":True}, - "org-priority":{"fg":"gold","bold":True},"org-tag":{"fg":"tan"},"org-tag-group":{"fg":"tan"}, - "org-special-keyword":{"fg":"pewter"},"org-drawer":{"fg":"pewter"},"org-property-value":{"fg":"steel"}, - "org-checkbox":{"fg":"gold","inherit":"fixed-pitch"},"org-checkbox-statistics-todo":{"fg":"terracotta"}, - "org-checkbox-statistics-done":{"fg":"sage"},"org-warning":{"fg":"terracotta","bold":True}, - "org-link":{"fg":"blue"},"org-footnote":{"fg":"blue"},"org-date":{"fg":"steel","inherit":"fixed-pitch"}, - "org-sexp-date":{"fg":"steel"},"org-date-selected":{"fg":"ground","bg":"gold"},"org-target":{"fg":"regal"}, - "org-macro":{"fg":"regal"},"org-cite":{"fg":"blue"},"org-cite-key":{"fg":"blue"}, - "org-block":{"fg":"white","bg":"bg-dim","inherit":"fixed-pitch"}, - "org-block-begin-line":{"fg":"pewter","bg":"bg-dim","inherit":"fixed-pitch"}, - "org-block-end-line":{"fg":"pewter","bg":"bg-dim","inherit":"fixed-pitch"}, - "org-code":{"fg":"terracotta","inherit":"fixed-pitch"},"org-verbatim":{"fg":"steel","inherit":"fixed-pitch"}, - "org-inline-src-block":{"fg":"terracotta","inherit":"fixed-pitch"},"org-quote":{"fg":"silver","italic":True}, - "org-verse":{"fg":"silver","italic":True},"org-latex-and-related":{"fg":"gold"}, - "org-table":{"fg":"steel","inherit":"fixed-pitch"},"org-table-header":{"fg":"white","bold":True,"bg":"gunmetal"}, - "org-formula":{"fg":"terracotta"},"org-column":{"bg":"gunmetal"},"org-column-title":{"fg":"white","bold":True,"bg":"gunmetal"}, - "org-list-dt":{"fg":"gold","bold":True},"org-meta-line":{"fg":"pewter","inherit":"fixed-pitch"}, - "org-ellipsis":{"fg":"pewter"},"org-hide":{"fg":"ground"},"org-indent":{"fg":"ground"}, - "org-archived":{"fg":"pewter"},"org-dispatcher-highlight":{"fg":"gold","bold":True,"bg":"navy"}, - "org-agenda-structure":{"fg":"blue","bold":True,"height":1.1},"org-agenda-structure-secondary":{"fg":"blue"}, - "org-agenda-structure-filter":{"fg":"terracotta","bold":True},"org-agenda-date":{"fg":"steel","height":1.05}, - "org-agenda-date-today":{"fg":"gold","bold":True,"height":1.05},"org-agenda-date-weekend":{"fg":"steel","bold":True}, - "org-agenda-date-weekend-today":{"fg":"gold","bold":True},"org-agenda-current-time":{"fg":"gold"}, - "org-agenda-done":{"fg":"sage"},"org-agenda-dimmed-todo-face":{"fg":"pewter"}, - "org-agenda-calendar-event":{"fg":"white"},"org-agenda-calendar-sexp":{"fg":"steel"}, - "org-agenda-calendar-daterange":{"fg":"steel"},"org-agenda-diary":{"fg":"sage"}, - "org-agenda-clocking":{"bg":"navy"},"org-agenda-column-dateline":{"bg":"gunmetal"}, - "org-agenda-restriction-lock":{"bg":"terracotta"},"org-agenda-filter-category":{"fg":"gold","bold":True}, - "org-agenda-filter-effort":{"fg":"gold","bold":True},"org-agenda-filter-regexp":{"fg":"gold","bold":True}, - "org-agenda-filter-tags":{"fg":"gold","bold":True},"org-scheduled":{"fg":"sage"}, - "org-scheduled-today":{"fg":"sage","bold":True},"org-scheduled-previously":{"fg":"terracotta"}, - "org-upcoming-deadline":{"fg":"gold"},"org-upcoming-distant-deadline":{"fg":"tan"}, - "org-imminent-deadline":{"fg":"terracotta","bold":True},"org-time-grid":{"fg":"tan"}, - "org-clock-overlay":{"bg":"navy"},"org-mode-line-clock":{"fg":"steel"},"org-mode-line-clock-overrun":{"fg":"terracotta","bold":True}} -MAGIT_SEED={ - "magit-section-heading":{"fg":"gold","bold":True},"magit-section-secondary-heading":{"fg":"tan","bold":True}, - "magit-section-heading-selection":{"fg":"gold","bg":"navy"},"magit-section-highlight":{"bg":"bg-dim"}, - "magit-section-child-count":{"fg":"pewter"},"magit-diff-added":{"fg":"sage"}, - "magit-diff-added-highlight":{"fg":"sage","bg":"bg-dim"},"magit-diff-removed":{"fg":"terracotta"}, - "magit-diff-removed-highlight":{"fg":"terracotta","bg":"bg-dim"},"magit-diff-context":{"fg":"pewter"}, - "magit-diff-context-highlight":{"fg":"silver","bg":"bg-dim"},"magit-diff-file-heading":{"fg":"white","bold":True}, - "magit-diff-file-heading-highlight":{"fg":"white","bold":True,"bg":"bg-dim"}, - "magit-diff-hunk-heading":{"fg":"steel","bg":"gunmetal"},"magit-diff-hunk-heading-highlight":{"fg":"white","bg":"gunmetal"}, - "magit-diffstat-added":{"fg":"sage"},"magit-diffstat-removed":{"fg":"terracotta"}, - "magit-branch-current":{"fg":"blue","bold":True},"magit-branch-local":{"fg":"blue"}, - "magit-branch-remote":{"fg":"sage"},"magit-branch-remote-head":{"fg":"sage","bold":True}, - "magit-head":{"fg":"blue","bold":True},"magit-tag":{"fg":"gold"},"magit-hash":{"fg":"pewter"}, - "magit-filename":{"fg":"steel"},"magit-dimmed":{"fg":"pewter"},"magit-keyword":{"fg":"regal"}, - "magit-keyword-squash":{"fg":"terracotta"},"magit-refname":{"fg":"pewter"},"magit-log-author":{"fg":"tan"}, - "magit-log-date":{"fg":"steel"},"magit-log-graph":{"fg":"pewter"}, - "magit-header-line":{"fg":"white","bold":True,"bg":"gunmetal"},"magit-process-ok":{"fg":"sage","bold":True}, - "magit-process-ng":{"fg":"terracotta","bold":True},"magit-mode-line-process":{"fg":"sage"}, - "magit-mode-line-process-error":{"fg":"terracotta"},"magit-bisect-good":{"fg":"sage"}, - "magit-bisect-bad":{"fg":"terracotta"},"magit-bisect-skip":{"fg":"gold"}, - "magit-blame-heading":{"fg":"steel","bg":"gunmetal"},"magit-blame-hash":{"fg":"pewter"}, - "magit-blame-name":{"fg":"tan"},"magit-blame-date":{"fg":"steel"},"magit-cherry-equivalent":{"fg":"regal"}, - "magit-cherry-unmatched":{"fg":"sage"},"magit-signature-good":{"fg":"sage"}, - "magit-signature-bad":{"fg":"terracotta","bold":True},"magit-signature-untrusted":{"fg":"gold"}, - "magit-signature-expired":{"fg":"tan"},"magit-diff-whitespace-warning":{"bg":"terracotta"}, - "magit-reflog-commit":{"fg":"sage"},"magit-reflog-amend":{"fg":"regal"},"magit-reflog-merge":{"fg":"sage"}, - "magit-reflog-checkout":{"fg":"blue"},"magit-reflog-reset":{"fg":"terracotta"},"magit-reflog-rebase":{"fg":"regal"}, - "magit-reflog-cherry-pick":{"fg":"sage"},"magit-reflog-remote":{"fg":"steel"},"magit-reflog-other":{"fg":"steel"}, - "magit-sequence-pick":{"fg":"white"},"magit-sequence-stop":{"fg":"terracotta"},"magit-sequence-done":{"fg":"pewter"}, - "magit-sequence-head":{"fg":"blue"}} -ELFEED_SEED={ - "elfeed-search-date-face":{"fg":"steel"},"elfeed-search-title-face":{"fg":"silver"}, - "elfeed-search-unread-title-face":{"fg":"white","bold":True},"elfeed-search-feed-face":{"fg":"sage"}, - "elfeed-search-tag-face":{"fg":"tan"},"elfeed-search-unread-count-face":{"fg":"gold"}, - "elfeed-search-filter-face":{"fg":"blue","bold":True},"elfeed-search-last-update-face":{"fg":"pewter"}, - "elfeed-log-date-face":{"fg":"steel"},"elfeed-log-error-level-face":{"fg":"terracotta","bold":True}, - "elfeed-log-warn-level-face":{"fg":"gold"},"elfeed-log-info-level-face":{"fg":"sage"}, - "elfeed-log-debug-level-face":{"fg":"pewter"}} -# ghostel (terminal): the 16 ANSI colors plus default and the fake cursor. -GHOSTEL_FACES=("ghostel-default ghostel-fake-cursor ghostel-fake-cursor-box " - "ghostel-color-black ghostel-color-red ghostel-color-green ghostel-color-yellow " - "ghostel-color-blue ghostel-color-magenta ghostel-color-cyan ghostel-color-white " - "ghostel-color-bright-black ghostel-color-bright-red ghostel-color-bright-green ghostel-color-bright-yellow " - "ghostel-color-bright-blue ghostel-color-bright-magenta ghostel-color-bright-cyan ghostel-color-bright-white").split() -GHOSTEL_SEED={ - "ghostel-default":{"fg":"#cdced1"},"ghostel-fake-cursor":{"fg":"#000000","bg":"silver"},"ghostel-fake-cursor-box":{"fg":"silver"}, - "ghostel-color-black":{"fg":"pewter"},"ghostel-color-red":{"fg":"terracotta"},"ghostel-color-green":{"fg":"emerald"},"ghostel-color-yellow":{"fg":"gold"}, - "ghostel-color-blue":{"fg":"blue"},"ghostel-color-magenta":{"fg":"regal"},"ghostel-color-cyan":{"fg":"sage"},"ghostel-color-white":{"fg":"silver"}, - "ghostel-color-bright-black":{"fg":"steel"},"ghostel-color-bright-red":{"fg":"#de4949"},"ghostel-color-bright-green":{"fg":"#84b068"},"ghostel-color-bright-yellow":{"fg":"#eed376"}, - "ghostel-color-bright-blue":{"fg":"#7a9abe"},"ghostel-color-bright-magenta":{"fg":"#b07fd0"},"ghostel-color-bright-cyan":{"fg":"#7fc0a8"},"ghostel-color-bright-white":{"fg":"white"}} -DASHBOARD_FACES=("dashboard-banner-logo-title dashboard-text-banner dashboard-heading " - "dashboard-items-face dashboard-navigator dashboard-no-items-face dashboard-footer-face dashboard-footer-icon-face").split() -DASHBOARD_SEED={ - "dashboard-banner-logo-title":{"fg":"gold","bold":True},"dashboard-text-banner":{"fg":"steel"},"dashboard-heading":{"fg":"blue","bold":True}, - "dashboard-items-face":{"fg":"#cdced1"},"dashboard-navigator":{"fg":"blue"},"dashboard-no-items-face":{"fg":"pewter"}, - "dashboard-footer-face":{"fg":"tan"},"dashboard-footer-icon-face":{"fg":"gold"}} -# mu4e is not in the generated inventory (not loaded when it was built), so its -# face list is curated from the set the dupre theme already themes. -MU4E_FACES=("mu4e-title-face mu4e-context-face mu4e-modeline-face mu4e-ok-face mu4e-warning-face " - "mu4e-header-title-face mu4e-header-key-face mu4e-header-value-face mu4e-header-face mu4e-header-highlight-face mu4e-header-marks-face " - "mu4e-unread-face mu4e-flagged-face mu4e-replied-face mu4e-forwarded-face mu4e-draft-face mu4e-trashed-face mu4e-moved-face mu4e-related-face " - "mu4e-contact-face mu4e-special-header-value-face mu4e-attach-number-face mu4e-url-number-face mu4e-link-face " - "mu4e-cited-1-face mu4e-cited-2-face mu4e-cited-3-face mu4e-cited-4-face mu4e-cited-5-face mu4e-cited-6-face mu4e-cited-7-face " - "mu4e-footer-face mu4e-region-code mu4e-system-face mu4e-highlight-face mu4e-compose-header-face mu4e-compose-separator-face").split() -MU4E_SEED={ - "mu4e-title-face":{"fg":"blue","bold":True},"mu4e-context-face":{"fg":"blue","bold":True},"mu4e-modeline-face":{"fg":"silver"},"mu4e-ok-face":{"fg":"sage","bold":True},"mu4e-warning-face":{"fg":"gold","bold":True}, - "mu4e-header-title-face":{"fg":"blue","bold":True},"mu4e-header-key-face":{"fg":"blue","bold":True},"mu4e-header-value-face":{"fg":"silver"},"mu4e-header-face":{"fg":"#cdced1"},"mu4e-header-highlight-face":{"bg":"gunmetal"},"mu4e-header-marks-face":{"fg":"gold"}, - "mu4e-unread-face":{"fg":"white","bold":True},"mu4e-flagged-face":{"fg":"gold"},"mu4e-replied-face":{"fg":"silver"},"mu4e-forwarded-face":{"fg":"silver"},"mu4e-draft-face":{"fg":"steel","italic":True},"mu4e-trashed-face":{"fg":"pewter","strike":True},"mu4e-moved-face":{"fg":"steel","italic":True},"mu4e-related-face":{"fg":"steel","italic":True}, - "mu4e-contact-face":{"fg":"#cdced1"},"mu4e-special-header-value-face":{"fg":"silver"},"mu4e-attach-number-face":{"fg":"blue","bold":True},"mu4e-url-number-face":{"fg":"blue","bold":True},"mu4e-link-face":{"fg":"blue","underline":True}, - "mu4e-cited-1-face":{"fg":"silver"},"mu4e-cited-2-face":{"fg":"steel"},"mu4e-cited-3-face":{"fg":"sage"},"mu4e-cited-4-face":{"fg":"pewter"},"mu4e-cited-5-face":{"fg":"tan"},"mu4e-cited-6-face":{"fg":"terracotta"},"mu4e-cited-7-face":{"fg":"regal"}, - "mu4e-footer-face":{"fg":"pewter"},"mu4e-region-code":{"bg":"bg-dim"},"mu4e-system-face":{"fg":"pewter","italic":True},"mu4e-highlight-face":{"fg":"gold","bold":True},"mu4e-compose-header-face":{"fg":"blue","bold":True},"mu4e-compose-separator-face":{"fg":"pewter"}} -LSP_FACES=("lsp-signature-face lsp-signature-highlight-function-argument lsp-signature-posframe " - "lsp-face-highlight-read lsp-face-highlight-write lsp-face-highlight-textual lsp-face-rename lsp-rename-placeholder-face " - "lsp-inlay-hint-face lsp-inlay-hint-parameter-face lsp-inlay-hint-type-face lsp-details-face " - "lsp-installation-buffer-face lsp-installation-finished-buffer-face").split() -LSP_SEED={ - "lsp-signature-face":{"fg":"silver"},"lsp-signature-highlight-function-argument":{"fg":"gold","bold":True},"lsp-signature-posframe":{"bg":"bg-dim"}, - "lsp-face-highlight-read":{"bg":"navy"},"lsp-face-highlight-write":{"bg":"#3d2f4a"},"lsp-face-highlight-textual":{"bg":"gunmetal"}, - "lsp-face-rename":{"bg":"gunmetal","bold":True},"lsp-rename-placeholder-face":{"fg":"gold","bold":True}, - "lsp-inlay-hint-face":{"fg":"pewter","italic":True},"lsp-inlay-hint-parameter-face":{"fg":"steel","italic":True},"lsp-inlay-hint-type-face":{"fg":"sage","italic":True}, - "lsp-details-face":{"fg":"pewter","italic":True},"lsp-installation-buffer-face":{"fg":"blue"},"lsp-installation-finished-buffer-face":{"fg":"sage"}} -GITGUTTER_FACES=("git-gutter:added git-gutter:modified git-gutter:deleted git-gutter:unchanged git-gutter:separator").split() -GITGUTTER_SEED={ - "git-gutter:added":{"fg":"emerald"},"git-gutter:modified":{"fg":"gold"},"git-gutter:deleted":{"fg":"terracotta"}, - "git-gutter:unchanged":{"fg":"pewter"},"git-gutter:separator":{"fg":"steel"}} -FLYCHECK_FACES=("flycheck-error flycheck-warning flycheck-info flycheck-fringe-error flycheck-fringe-warning flycheck-fringe-info " - "flycheck-delimited-error flycheck-error-delimiter flycheck-error-list-error flycheck-error-list-warning flycheck-error-list-info " - "flycheck-error-list-error-message flycheck-error-list-checker-name flycheck-error-list-column-number flycheck-error-list-line-number " - "flycheck-error-list-filename flycheck-error-list-id flycheck-error-list-id-with-explainer flycheck-error-list-highlight flycheck-verify-select-checker").split() -FLYCHECK_SEED={ - "flycheck-error":{"fg":"terracotta"},"flycheck-warning":{"fg":"gold"},"flycheck-info":{"fg":"blue"}, - "flycheck-fringe-error":{"fg":"terracotta"},"flycheck-fringe-warning":{"fg":"gold"},"flycheck-fringe-info":{"fg":"blue"}, - "flycheck-delimited-error":{"fg":"terracotta"},"flycheck-error-delimiter":{"fg":"terracotta"}, - "flycheck-error-list-error":{"fg":"terracotta"},"flycheck-error-list-warning":{"fg":"gold"},"flycheck-error-list-info":{"fg":"blue"}, - "flycheck-error-list-error-message":{"fg":"#cdced1"},"flycheck-error-list-checker-name":{"fg":"steel"}, - "flycheck-error-list-column-number":{"fg":"pewter"},"flycheck-error-list-line-number":{"fg":"pewter"},"flycheck-error-list-filename":{"fg":"blue"}, - "flycheck-error-list-id":{"fg":"steel"},"flycheck-error-list-id-with-explainer":{"fg":"steel","bold":True}, - "flycheck-error-list-highlight":{"bg":"gunmetal"},"flycheck-verify-select-checker":{"fg":"gold"}} -DIRED_FACES=("dired-header dired-directory dired-symlink dired-broken-symlink dired-special dired-set-id " - "dired-perm-write dired-mark dired-marked dired-flagged dired-ignored dired-warning").split() -DIRED_SEED={ - "dired-header":{"fg":"blue","bold":True},"dired-directory":{"fg":"blue","bold":True},"dired-symlink":{"fg":"sage"}, - "dired-broken-symlink":{"fg":"#de4949","bold":True},"dired-special":{"fg":"regal"},"dired-set-id":{"fg":"terracotta"}, - "dired-perm-write":{"fg":"silver"},"dired-mark":{"fg":"gold"},"dired-marked":{"fg":"gold","bold":True}, - "dired-flagged":{"fg":"terracotta","bold":True},"dired-ignored":{"fg":"pewter"},"dired-warning":{"fg":"gold","bold":True}} -DIRVISH_FACES=("dirvish-inactive dirvish-free-space dirvish-hl-line dirvish-hl-line-inactive " - "dirvish-file-modes dirvish-file-link-number dirvish-file-user-id dirvish-file-group-id dirvish-file-size dirvish-file-time " - "dirvish-file-inode-number dirvish-file-device-number dirvish-subtree-guide dirvish-subtree-state " - "dirvish-collapse-dir-face dirvish-collapse-empty-dir-face dirvish-collapse-file-face dirvish-emerge-group-title " - "dirvish-media-info-heading dirvish-media-info-property-key dirvish-narrow-match-face-0 dirvish-narrow-match-face-1 " - "dirvish-narrow-match-face-2 dirvish-narrow-match-face-3 dirvish-narrow-split dirvish-proc-running dirvish-proc-finished " - "dirvish-proc-failed dirvish-git-commit-message-face dirvish-vc-added-state dirvish-vc-edited-state dirvish-vc-removed-state " - "dirvish-vc-conflict-state dirvish-vc-locked-state dirvish-vc-missing-state dirvish-vc-needs-merge-face " - "dirvish-vc-needs-update-state dirvish-vc-unregistered-face").split() -DIRVISH_SEED={ - "dirvish-inactive":{"fg":"pewter"},"dirvish-free-space":{"fg":"sage"},"dirvish-hl-line":{"bg":"gunmetal"},"dirvish-hl-line-inactive":{"bg":"bg-dim"}, - "dirvish-file-modes":{"fg":"steel"},"dirvish-file-link-number":{"fg":"pewter"},"dirvish-file-user-id":{"fg":"blue"},"dirvish-file-group-id":{"fg":"steel"}, - "dirvish-file-size":{"fg":"sage"},"dirvish-file-time":{"fg":"pewter"},"dirvish-file-inode-number":{"fg":"pewter"},"dirvish-file-device-number":{"fg":"pewter"}, - "dirvish-subtree-guide":{"fg":"pewter"},"dirvish-subtree-state":{"fg":"steel"},"dirvish-collapse-dir-face":{"fg":"blue"}, - "dirvish-collapse-empty-dir-face":{"fg":"pewter"},"dirvish-collapse-file-face":{"fg":"silver"},"dirvish-emerge-group-title":{"fg":"gold","bold":True}, - "dirvish-media-info-heading":{"fg":"blue","bold":True},"dirvish-media-info-property-key":{"fg":"steel"}, - "dirvish-narrow-match-face-0":{"fg":"gold","bold":True},"dirvish-narrow-match-face-1":{"fg":"blue","bold":True}, - "dirvish-narrow-match-face-2":{"fg":"emerald","bold":True},"dirvish-narrow-match-face-3":{"fg":"regal","bold":True},"dirvish-narrow-split":{"fg":"pewter"}, - "dirvish-proc-running":{"fg":"gold"},"dirvish-proc-finished":{"fg":"sage"},"dirvish-proc-failed":{"fg":"terracotta"}, - "dirvish-git-commit-message-face":{"fg":"tan","italic":True},"dirvish-vc-added-state":{"fg":"sage"},"dirvish-vc-edited-state":{"fg":"gold"}, - "dirvish-vc-removed-state":{"fg":"terracotta"},"dirvish-vc-conflict-state":{"fg":"terracotta","bold":True},"dirvish-vc-locked-state":{"fg":"blue"}, - "dirvish-vc-missing-state":{"fg":"terracotta"},"dirvish-vc-needs-merge-face":{"fg":"gold"},"dirvish-vc-needs-update-state":{"fg":"gold"}, - "dirvish-vc-unregistered-face":{"fg":"pewter"}} -ORGDRILL_FACES=("org-drill-hidden-cloze-face org-drill-visible-cloze-face org-drill-visible-cloze-hint-face").split() -ORGDRILL_SEED={"org-drill-hidden-cloze-face":{"fg":"#000000","bg":"steel"},"org-drill-visible-cloze-face":{"fg":"gold","bold":True},"org-drill-visible-cloze-hint-face":{"fg":"pewter","italic":True}} -ORGNOTER_FACES=("org-noter-notes-exist-face org-noter-no-notes-exist-face").split() -ORGNOTER_SEED={"org-noter-notes-exist-face":{"fg":"sage"},"org-noter-no-notes-exist-face":{"fg":"pewter"}} -SIGNEL_FACES=("signel-timestamp-face signel-my-msg-face signel-other-msg-face signel-error-face").split() -SIGNEL_SEED={"signel-timestamp-face":{"fg":"pewter"},"signel-my-msg-face":{"fg":"blue"},"signel-other-msg-face":{"fg":"silver"},"signel-error-face":{"fg":"terracotta","bold":True}} -PEARL_FACES=("pearl-preamble-summary pearl-editable-comment pearl-readonly-comment pearl-modified-highlight pearl-modified-local pearl-modified-unknown").split() -PEARL_SEED={"pearl-preamble-summary":{"fg":"blue","bold":True},"pearl-editable-comment":{"fg":"silver"},"pearl-readonly-comment":{"fg":"pewter","italic":True},"pearl-modified-highlight":{"bg":"navy"},"pearl-modified-local":{"fg":"gold"},"pearl-modified-unknown":{"fg":"pewter"}} -CALIBREDB_FACES=("calibredb-search-header-library-name-face calibredb-search-header-library-path-face calibredb-search-header-total-face calibredb-search-header-filter-face calibredb-search-header-sort-face calibredb-search-header-highlight-face " - "calibredb-id-face calibredb-title-face calibredb-author-face calibredb-format-face calibredb-size-face calibredb-tag-face calibredb-date-face calibredb-mark-face calibredb-series-face calibredb-publisher-face calibredb-pubdate-face " - "calibredb-language-face calibredb-comment-face calibredb-archive-face calibredb-favorite-face calibredb-file-face calibredb-ids-face calibredb-highlight-face calibredb-current-page-button-face calibredb-mouse-face " - "calibredb-title-detailed-view-face calibredb-edit-annotation-header-title-face").split() -CALIBREDB_SEED={ - "calibredb-search-header-library-name-face":{"fg":"blue","bold":True},"calibredb-search-header-library-path-face":{"fg":"pewter"},"calibredb-search-header-total-face":{"fg":"sage"},"calibredb-search-header-filter-face":{"fg":"gold"},"calibredb-search-header-sort-face":{"fg":"steel"},"calibredb-search-header-highlight-face":{"fg":"gold","bold":True}, - "calibredb-id-face":{"fg":"pewter"},"calibredb-title-face":{"fg":"blue","bold":True},"calibredb-author-face":{"fg":"sage"},"calibredb-format-face":{"fg":"steel"},"calibredb-size-face":{"fg":"pewter"},"calibredb-tag-face":{"fg":"tan"},"calibredb-date-face":{"fg":"pewter"},"calibredb-mark-face":{"fg":"gold","bold":True},"calibredb-series-face":{"fg":"regal"},"calibredb-publisher-face":{"fg":"steel"},"calibredb-pubdate-face":{"fg":"pewter"}, - "calibredb-language-face":{"fg":"steel"},"calibredb-comment-face":{"fg":"silver","italic":True},"calibredb-archive-face":{"fg":"pewter"},"calibredb-favorite-face":{"fg":"gold"},"calibredb-file-face":{"fg":"blue"},"calibredb-ids-face":{"fg":"pewter"},"calibredb-highlight-face":{"fg":"gold","bold":True},"calibredb-current-page-button-face":{"fg":"blue","bold":True},"calibredb-mouse-face":{"bg":"gunmetal"}, - "calibredb-title-detailed-view-face":{"fg":"gold","bold":True},"calibredb-edit-annotation-header-title-face":{"fg":"blue","bold":True}} -ERC_FACES=("erc-header-line erc-timestamp-face erc-notice-face erc-default-face erc-current-nick-face erc-my-nick-face erc-my-nick-prefix-face erc-nick-default-face erc-nick-prefix-face erc-button-nick-default-face " - "erc-nick-msg-face erc-direct-msg-face erc-action-face erc-keyword-face erc-pal-face erc-fool-face erc-dangerous-host-face erc-error-face erc-input-face erc-prompt-face erc-command-indicator-face erc-information " - "erc-button erc-bold-face erc-italic-face erc-underline-face erc-inverse-face erc-spoiler-face erc-fill-wrap-merge-indicator-face erc-keep-place-indicator-arrow erc-keep-place-indicator-line").split() -ERC_SEED={ - "erc-header-line":{"fg":"white","bg":"gunmetal","bold":True},"erc-timestamp-face":{"fg":"pewter"},"erc-notice-face":{"fg":"steel"},"erc-default-face":{"fg":"#cdced1"},"erc-current-nick-face":{"fg":"gold","bold":True},"erc-my-nick-face":{"fg":"gold","bold":True},"erc-my-nick-prefix-face":{"fg":"gold"},"erc-nick-default-face":{"fg":"blue"},"erc-nick-prefix-face":{"fg":"sage"},"erc-button-nick-default-face":{"fg":"blue"}, - "erc-nick-msg-face":{"fg":"regal"},"erc-direct-msg-face":{"fg":"regal"},"erc-action-face":{"fg":"sage","italic":True},"erc-keyword-face":{"fg":"gold","bold":True},"erc-pal-face":{"fg":"emerald"},"erc-fool-face":{"fg":"pewter"},"erc-dangerous-host-face":{"fg":"terracotta","bold":True},"erc-error-face":{"fg":"terracotta","bold":True},"erc-input-face":{"fg":"silver"},"erc-prompt-face":{"fg":"blue","bold":True},"erc-command-indicator-face":{"fg":"steel","bold":True},"erc-information":{"fg":"steel"}, - "erc-button":{"fg":"blue"},"erc-bold-face":{"bold":True},"erc-italic-face":{"italic":True},"erc-underline-face":{"fg":"silver","underline":True},"erc-inverse-face":{"fg":"#000000","bg":"silver"},"erc-spoiler-face":{"fg":"#000000","bg":"gunmetal"},"erc-fill-wrap-merge-indicator-face":{"fg":"pewter"},"erc-keep-place-indicator-arrow":{"fg":"gold"},"erc-keep-place-indicator-line":{"bg":"bg-dim"}} -SLACK_FACES=("slack-room-info-title-face slack-room-info-title-room-name-face slack-room-info-section-title-face slack-room-info-section-label-face slack-room-unread-face " - "slack-message-output-header slack-message-output-text slack-message-output-reaction slack-message-output-reaction-pressed slack-message-deleted-face slack-new-message-marker-face slack-all-thread-buffer-thread-header-face " - "slack-message-mention-face slack-message-mention-me-face slack-message-mention-keyword-face slack-channel-button-face " - "slack-mrkdwn-bold-face slack-mrkdwn-italic-face slack-mrkdwn-code-face slack-mrkdwn-code-block-face slack-mrkdwn-strike-face slack-mrkdwn-blockquote-face slack-mrkdwn-list-face " - "slack-attachment-header slack-attachment-footer slack-attachment-pad slack-attachment-field-title slack-message-attachment-preview-header-face slack-preview-face slack-block-highlight-source-overlay-face " - "slack-message-action-face slack-message-action-primary-face slack-message-action-danger-face " - "slack-button-block-element-face slack-button-primary-block-element-face slack-button-danger-block-element-face slack-select-block-element-face slack-overflow-block-element-face slack-date-picker-block-element-face " - "slack-dialog-title-face slack-dialog-element-label-face slack-dialog-element-hint-face slack-dialog-element-placeholder-face slack-dialog-element-error-face slack-dialog-submit-button-face slack-dialog-cancel-button-face slack-dialog-select-element-input-face " - "slack-user-active-face slack-user-dnd-face slack-user-profile-header-face slack-user-profile-property-name-face slack-profile-image-face " - "slack-search-result-message-header-face slack-search-result-message-username-face " - "slack-modeline-has-unreads-face slack-modeline-channel-has-unreads-face slack-modeline-thread-has-unreads-face").split() -SLACK_SEED={ - "slack-room-info-title-face":{"fg":"blue","bold":True},"slack-room-info-title-room-name-face":{"fg":"gold","bold":True},"slack-room-info-section-title-face":{"fg":"blue","bold":True},"slack-room-info-section-label-face":{"fg":"steel"},"slack-room-unread-face":{"fg":"white","bold":True}, - "slack-message-output-header":{"fg":"blue","bold":True},"slack-message-output-text":{"fg":"#cdced1"},"slack-message-output-reaction":{"fg":"steel"},"slack-message-output-reaction-pressed":{"fg":"gold","bold":True},"slack-message-deleted-face":{"fg":"pewter","italic":True},"slack-new-message-marker-face":{"fg":"terracotta","bold":True},"slack-all-thread-buffer-thread-header-face":{"fg":"blue","bold":True}, - "slack-message-mention-face":{"fg":"blue"},"slack-message-mention-me-face":{"fg":"gold","bg":"navy","bold":True},"slack-message-mention-keyword-face":{"fg":"gold","bold":True},"slack-channel-button-face":{"fg":"blue"}, - "slack-mrkdwn-bold-face":{"bold":True},"slack-mrkdwn-italic-face":{"italic":True},"slack-mrkdwn-code-face":{"fg":"terracotta"},"slack-mrkdwn-code-block-face":{"fg":"terracotta","bg":"bg-dim"},"slack-mrkdwn-strike-face":{"fg":"pewter","strike":True},"slack-mrkdwn-blockquote-face":{"fg":"silver","italic":True},"slack-mrkdwn-list-face":{"fg":"silver"}, - "slack-attachment-header":{"fg":"blue","bold":True},"slack-attachment-footer":{"fg":"pewter"},"slack-attachment-pad":{"fg":"pewter"},"slack-attachment-field-title":{"fg":"steel","bold":True},"slack-message-attachment-preview-header-face":{"fg":"blue"},"slack-preview-face":{"fg":"silver"},"slack-block-highlight-source-overlay-face":{"bg":"bg-dim"}, - "slack-message-action-face":{"fg":"blue"},"slack-message-action-primary-face":{"fg":"sage"},"slack-message-action-danger-face":{"fg":"terracotta"}, - "slack-button-block-element-face":{"fg":"silver"},"slack-button-primary-block-element-face":{"fg":"sage","bold":True},"slack-button-danger-block-element-face":{"fg":"terracotta","bold":True},"slack-select-block-element-face":{"fg":"blue"},"slack-overflow-block-element-face":{"fg":"steel"},"slack-date-picker-block-element-face":{"fg":"blue"}, - "slack-dialog-title-face":{"fg":"blue","bold":True},"slack-dialog-element-label-face":{"fg":"steel"},"slack-dialog-element-hint-face":{"fg":"pewter","italic":True},"slack-dialog-element-placeholder-face":{"fg":"pewter"},"slack-dialog-element-error-face":{"fg":"terracotta"},"slack-dialog-submit-button-face":{"fg":"sage","bold":True},"slack-dialog-cancel-button-face":{"fg":"silver"},"slack-dialog-select-element-input-face":{"fg":"silver"}, - "slack-user-active-face":{"fg":"sage"},"slack-user-dnd-face":{"fg":"terracotta"},"slack-user-profile-header-face":{"fg":"blue","bold":True},"slack-user-profile-property-name-face":{"fg":"steel"},"slack-profile-image-face":{"fg":"pewter"}, - "slack-search-result-message-header-face":{"fg":"blue"},"slack-search-result-message-username-face":{"fg":"gold","bold":True}, - "slack-modeline-has-unreads-face":{"fg":"gold"},"slack-modeline-channel-has-unreads-face":{"fg":"gold","bold":True},"slack-modeline-thread-has-unreads-face":{"fg":"gold"}} -TELEGA_FACES=("telega-root-heading telega-tracking telega-unread-unmuted-modeline telega-username telega-user-online-status telega-user-non-online-status telega-secret-title telega-contact-birthdays-today " - "telega-muted-count telega-unmuted-count telega-mention-count telega-has-chatbuf-brackets telega-delim-face telega-shadow telega-link telega-blue telega-red " - "telega-msg-heading telega-msg-user-title telega-msg-self-title telega-msg-deleted telega-msg-sponsored telega-msg-inline-reply telega-msg-inline-forward telega-msg-inline-other " - "telega-entity-type-bold telega-entity-type-italic telega-entity-type-underline telega-entity-type-strikethrough telega-entity-type-code telega-entity-type-pre telega-entity-type-blockquote telega-entity-type-mention telega-entity-type-hashtag telega-entity-type-cashtag telega-entity-type-botcommand telega-entity-type-texturl telega-entity-type-spoiler " - "telega-reaction telega-reaction-chosen telega-reaction-paid telega-reaction-paid-chosen telega-highlight-text-face telega-button-highlight " - "telega-chat-prompt telega-chat-prompt-aux telega-chat-input-attachment telega-topic-button telega-filter-active telega-filter-button-active telega-filter-button-inactive telega-checklist-stats-done telega-checklist-stats-todo " - "telega-box-button telega-box-button-active telega-box-button-default-active telega-box-button-default-passive telega-box-button-primary-active telega-box-button-primary-passive telega-box-button-success-active telega-box-button-success-passive telega-box-button-danger-active telega-box-button-danger-passive telega-box-button-ui-active telega-box-button-ui-passive telega-box-button2-active telega-box-button2-passive telega-box-button2-white-foreground " - "telega-describe-item-title telega-describe-section-title telega-describe-subsection-title telega-enckey-00 telega-enckey-01 telega-enckey-10 telega-enckey-11 " - "telega-palette-builtin-blue telega-palette-builtin-green telega-palette-builtin-orange telega-palette-builtin-purple " - "telega-webpage-title telega-webpage-subtitle telega-webpage-header telega-webpage-subheader telega-webpage-outline telega-webpage-fixed telega-webpage-preformatted telega-webpage-marked telega-webpage-strike-through telega-webpage-chat-link telega-link-preview-sitename telega-link-preview-title").split() -TELEGA_SEED={ - "telega-root-heading":{"fg":"blue","bold":True},"telega-tracking":{"fg":"gold"},"telega-unread-unmuted-modeline":{"fg":"gold","bold":True},"telega-username":{"fg":"blue"},"telega-user-online-status":{"fg":"sage"},"telega-user-non-online-status":{"fg":"pewter"},"telega-secret-title":{"fg":"sage"},"telega-contact-birthdays-today":{"fg":"gold"}, - "telega-muted-count":{"fg":"pewter"},"telega-unmuted-count":{"fg":"gold","bold":True},"telega-mention-count":{"fg":"gold","bold":True},"telega-has-chatbuf-brackets":{"fg":"steel"},"telega-delim-face":{"fg":"pewter"},"telega-shadow":{"fg":"pewter"},"telega-link":{"fg":"blue"},"telega-blue":{"fg":"blue"},"telega-red":{"fg":"terracotta"}, - "telega-msg-heading":{"fg":"steel"},"telega-msg-user-title":{"fg":"blue","bold":True},"telega-msg-self-title":{"fg":"gold","bold":True},"telega-msg-deleted":{"fg":"pewter","italic":True},"telega-msg-sponsored":{"fg":"pewter","italic":True},"telega-msg-inline-reply":{"fg":"steel"},"telega-msg-inline-forward":{"fg":"sage"},"telega-msg-inline-other":{"fg":"pewter"}, - "telega-entity-type-bold":{"bold":True},"telega-entity-type-italic":{"italic":True},"telega-entity-type-underline":{"fg":"silver","underline":True},"telega-entity-type-strikethrough":{"fg":"pewter","strike":True},"telega-entity-type-code":{"fg":"terracotta"},"telega-entity-type-pre":{"fg":"terracotta","bg":"bg-dim"},"telega-entity-type-blockquote":{"fg":"silver","italic":True},"telega-entity-type-mention":{"fg":"blue"},"telega-entity-type-hashtag":{"fg":"blue"},"telega-entity-type-cashtag":{"fg":"sage"},"telega-entity-type-botcommand":{"fg":"sage"},"telega-entity-type-texturl":{"fg":"blue"},"telega-entity-type-spoiler":{"fg":"gunmetal","bg":"gunmetal"}, - "telega-reaction":{"fg":"steel"},"telega-reaction-chosen":{"fg":"gold","bold":True},"telega-reaction-paid":{"fg":"gold"},"telega-reaction-paid-chosen":{"fg":"gold","bold":True},"telega-highlight-text-face":{"fg":"#000000","bg":"gold"},"telega-button-highlight":{"fg":"gold","bold":True}, - "telega-chat-prompt":{"fg":"blue","bold":True},"telega-chat-prompt-aux":{"fg":"steel"},"telega-chat-input-attachment":{"fg":"sage"},"telega-topic-button":{"fg":"blue"},"telega-filter-active":{"fg":"gold","bold":True},"telega-filter-button-active":{"fg":"#000000","bg":"gold"},"telega-filter-button-inactive":{"fg":"steel"},"telega-checklist-stats-done":{"fg":"sage"},"telega-checklist-stats-todo":{"fg":"steel"}, - "telega-box-button":{"fg":"blue"},"telega-box-button-active":{"fg":"#000000","bg":"blue"},"telega-box-button-default-active":{"fg":"#000000","bg":"silver"},"telega-box-button-default-passive":{"fg":"steel"},"telega-box-button-primary-active":{"fg":"#000000","bg":"blue"},"telega-box-button-primary-passive":{"fg":"blue"},"telega-box-button-success-active":{"fg":"#000000","bg":"emerald"},"telega-box-button-success-passive":{"fg":"sage"},"telega-box-button-danger-active":{"fg":"#000000","bg":"terracotta"},"telega-box-button-danger-passive":{"fg":"terracotta"},"telega-box-button-ui-active":{"fg":"#000000","bg":"gold"},"telega-box-button-ui-passive":{"fg":"gold"},"telega-box-button2-active":{"fg":"#000000","bg":"blue"},"telega-box-button2-passive":{"fg":"steel"},"telega-box-button2-white-foreground":{"fg":"white"}, - "telega-describe-item-title":{"fg":"steel","bold":True},"telega-describe-section-title":{"fg":"blue","bold":True},"telega-describe-subsection-title":{"fg":"blue"},"telega-enckey-00":{"fg":"pewter"},"telega-enckey-01":{"fg":"sage"},"telega-enckey-10":{"fg":"gold"},"telega-enckey-11":{"fg":"blue"}, - "telega-palette-builtin-blue":{"fg":"blue"},"telega-palette-builtin-green":{"fg":"emerald"},"telega-palette-builtin-orange":{"fg":"terracotta"},"telega-palette-builtin-purple":{"fg":"regal"}, - "telega-webpage-title":{"fg":"blue","bold":True},"telega-webpage-subtitle":{"fg":"steel"},"telega-webpage-header":{"fg":"gold","bold":True},"telega-webpage-subheader":{"fg":"gold"},"telega-webpage-outline":{"fg":"pewter"},"telega-webpage-fixed":{"fg":"terracotta"},"telega-webpage-preformatted":{"fg":"terracotta","bg":"bg-dim"},"telega-webpage-marked":{"fg":"#000000","bg":"gold"},"telega-webpage-strike-through":{"fg":"pewter","strike":True},"telega-webpage-chat-link":{"fg":"blue"},"telega-link-preview-sitename":{"fg":"steel"},"telega-link-preview-title":{"fg":"blue","bold":True}} -# shr is built-in (not in the inventory). It is the HTML renderer behind nov -# (EPUB), eww, elfeed article view, and HTML mail, so theming it themes them all. -SHR_FACES=("shr-h1 shr-h2 shr-h3 shr-h4 shr-h5 shr-h6 shr-text shr-link shr-selected-link " - "shr-code shr-mark shr-strike-through shr-sup shr-abbreviation shr-sliced-image").split() -SHR_SEED={ - "shr-h1":{"fg":"gold","bold":True,"height":1.4},"shr-h2":{"fg":"blue","bold":True,"height":1.2},"shr-h3":{"fg":"blue","bold":True},"shr-h4":{"fg":"silver","bold":True},"shr-h5":{"fg":"steel","bold":True},"shr-h6":{"fg":"pewter","bold":True}, - "shr-text":{"fg":"#cdced1"},"shr-link":{"fg":"blue","underline":True},"shr-selected-link":{"fg":"gold","bold":True,"underline":True},"shr-code":{"fg":"terracotta","bg":"bg-dim"},"shr-mark":{"fg":"#000000","bg":"gold"},"shr-strike-through":{"fg":"pewter","strike":True},"shr-sup":{"fg":"steel"},"shr-abbreviation":{"fg":"steel","italic":True},"shr-sliced-image":{"fg":"pewter"}} -def _faces(names,prefix,seed): - out=[] - for f in names: - lbl=(f[len(prefix):] if f.startswith(prefix) else f).replace("-face","").replace("-"," ") - out.append([f,lbl,seed.get(f,{})]) - return out -APPS={"org-mode":{"label":"org-mode","preview":"org","faces":_faces(ORG_FACES,"org-",ORG_SEED)}, - "magit":{"label":"magit","preview":"magit","faces":_faces(MAGIT_FACES,"magit-",MAGIT_SEED)}, - "elfeed":{"label":"elfeed","preview":"elfeed","faces":_faces(ELFEED_FACES,"elfeed-",ELFEED_SEED)}, - "mu4e":{"label":"mu4e","preview":"mu4e","faces":_faces(MU4E_FACES,"mu4e-",MU4E_SEED)}, - "ghostel":{"label":"ghostel","preview":"ghostel","faces":_faces(GHOSTEL_FACES,"ghostel-",GHOSTEL_SEED)}, - "dashboard":{"label":"dashboard","preview":"dashboard","faces":_faces(DASHBOARD_FACES,"dashboard-",DASHBOARD_SEED)}, - "lsp-mode":{"label":"lsp-mode","preview":"lsp","faces":_faces(LSP_FACES,"lsp-",LSP_SEED)}, - "git-gutter":{"label":"git-gutter","preview":"gitgutter","faces":_faces(GITGUTTER_FACES,"git-gutter:",GITGUTTER_SEED)}, - "flycheck":{"label":"flycheck","preview":"flycheck","faces":_faces(FLYCHECK_FACES,"flycheck-",FLYCHECK_SEED)}, - "dired":{"label":"dired","preview":"dired","faces":_faces(DIRED_FACES,"dired-",DIRED_SEED)}, - "dirvish":{"label":"dirvish","preview":"dirvish","faces":_faces(DIRVISH_FACES,"dirvish-",DIRVISH_SEED)}, - "calibredb":{"label":"calibredb","preview":"calibredb","faces":_faces(CALIBREDB_FACES,"calibredb-",CALIBREDB_SEED)}, - "erc":{"label":"erc","preview":"erc","faces":_faces(ERC_FACES,"erc-",ERC_SEED)}, - "org-drill":{"label":"org-drill","preview":"orgdrill","faces":_faces(ORGDRILL_FACES,"org-drill-",ORGDRILL_SEED)}, - "org-noter":{"label":"org-noter","preview":"orgnoter","faces":_faces(ORGNOTER_FACES,"org-noter-",ORGNOTER_SEED)}, - "signel":{"label":"signel","preview":"signel","faces":_faces(SIGNEL_FACES,"signel-",SIGNEL_SEED)}, - "pearl":{"label":"pearl","preview":"pearl","faces":_faces(PEARL_FACES,"pearl-",PEARL_SEED)}, - "slack":{"label":"slack","preview":"slack","faces":_faces(SLACK_FACES,"slack-",SLACK_SEED)}, - "telega":{"label":"telega","preview":"telega","faces":_faces(TELEGA_FACES,"telega-",TELEGA_SEED)}, - "shr":{"label":"shr (HTML: nov/eww/mail)","preview":"shr","faces":_faces(SHR_FACES,"shr-",SHR_SEED)}} -# Phase 6: merge the generated all-package inventory (refresh with build-inventory.el). -# Bespoke apps stay; every other installed package becomes an editable generic app. -_inv_path=os.path.join(HERE,"package-inventory.json") -if os.path.exists(_inv_path): - _INV=json.load(open(_inv_path)) - _BESPOKE={"magit","elfeed","org","org-mode","mu4e","ghostel","dashboard","lsp-mode","git-gutter","flycheck","dired","dirvish","calibredb","erc","org-drill","org-noter","signel","pearl","slack","telega","shr"} - for _pkg in sorted(_INV): - if _pkg in _BESPOKE or _pkg in APPS: continue - APPS[_pkg]={"label":_pkg,"preview":"generic","faces":[ - [f,(f[len(_pkg)+1:] if f.startswith(_pkg+"-") else f).replace("-face","").replace("-"," "),{}] - for f in _INV[_pkg]]} -HTML = """theme-selector - -

Untitled: theme

-
-
-

palette

-
-
-
- - - - - -
-
-
-
-
-
#888888
-
limit
-
-
-
-
-
-

export, import, and save

-
- -
-
- - - - -
- -
-
-

code/color assignments

-
-
-
elements △color △styleexamplecontrast
-
-
-
-

- 
-
-

ui faces

-
-
-
face △foreground △background △stylepreview
-
-
-
-
-
-
-

package faces

-
- - - -
-
-
-
face △fg △bg △styleinherit △size △contrast △
-
-
-
-
-
-
-""" -HTML=(HTML.replace("SAMPLES_J",json.dumps(SAMPLES)) - .replace("PALETTE_J",json.dumps(PALETTE)).replace("CATS_J",json.dumps(CATS)) - .replace("UIFACES_J",json.dumps(UI_FACES)).replace("UIMAP_J",json.dumps(UIMAP)).replace("APPS_J",json.dumps(APPS)) - .replace("BOLD_J",json.dumps(BOLD)).replace("MAP_J",json.dumps(MAP))) -OUT=os.path.join(HERE,'theme-selector.html') -open(OUT,"w").write(HTML) -print("wrote",OUT) diff --git a/scripts/theme-selector/package-inventory.json b/scripts/theme-selector/package-inventory.json deleted file mode 100644 index 18fd7aa25..000000000 --- a/scripts/theme-selector/package-inventory.json +++ /dev/null @@ -1,723 +0,0 @@ -{ - "2048-game": [ - "twentyfortyeight-face-1024", - "twentyfortyeight-face-128", - "twentyfortyeight-face-16", - "twentyfortyeight-face-2", - "twentyfortyeight-face-2048", - "twentyfortyeight-face-256", - "twentyfortyeight-face-32", - "twentyfortyeight-face-4", - "twentyfortyeight-face-512", - "twentyfortyeight-face-64", - "twentyfortyeight-face-8" - ], - "alert": [ - "alert-high-face", - "alert-low-face", - "alert-moderate-face", - "alert-normal-face", - "alert-trivial-face", - "alert-urgent-face" - ], - "all-the-icons": [ - "all-the-icons-blue", - "all-the-icons-blue-alt", - "all-the-icons-cyan", - "all-the-icons-cyan-alt", - "all-the-icons-dblue", - "all-the-icons-dcyan", - "all-the-icons-dgreen", - "all-the-icons-dmaroon", - "all-the-icons-dorange", - "all-the-icons-dpink", - "all-the-icons-dpurple", - "all-the-icons-dred", - "all-the-icons-dsilver", - "all-the-icons-dyellow", - "all-the-icons-green", - "all-the-icons-lblue", - "all-the-icons-lcyan", - "all-the-icons-lgreen", - "all-the-icons-lmaroon", - "all-the-icons-lorange", - "all-the-icons-lpink", - "all-the-icons-lpurple", - "all-the-icons-lred", - "all-the-icons-lsilver", - "all-the-icons-lyellow", - "all-the-icons-maroon", - "all-the-icons-orange", - "all-the-icons-pink", - "all-the-icons-purple", - "all-the-icons-purple-alt", - "all-the-icons-red", - "all-the-icons-red-alt", - "all-the-icons-silver", - "all-the-icons-yellow" - ], - "company": [ - "company-echo", - "company-echo-common", - "company-preview", - "company-preview-common", - "company-preview-search", - "company-tooltip", - "company-tooltip-annotation", - "company-tooltip-annotation-selection", - "company-tooltip-common", - "company-tooltip-common-selection", - "company-tooltip-deprecated", - "company-tooltip-mouse", - "company-tooltip-quick-access", - "company-tooltip-quick-access-selection", - "company-tooltip-scrollbar-thumb", - "company-tooltip-scrollbar-track", - "company-tooltip-search", - "company-tooltip-search-selection", - "company-tooltip-selection" - ], - "company-box": [ - "company-box-annotation", - "company-box-background", - "company-box-candidate", - "company-box-numbers", - "company-box-scrollbar", - "company-box-selection" - ], - "consult": [ - "consult-async-failed", - "consult-async-finished", - "consult-async-running", - "consult-async-split", - "consult-bookmark", - "consult-buffer", - "consult-file", - "consult-grep-context", - "consult-help", - "consult-highlight-mark", - "consult-highlight-match", - "consult-key", - "consult-line-number", - "consult-line-number-prefix", - "consult-line-number-wrapped", - "consult-narrow-indicator", - "consult-preview-insertion", - "consult-preview-line", - "consult-preview-match", - "consult-separator" - ], - "dashboard": [ - "dashboard-banner-logo-title", - "dashboard-footer-face", - "dashboard-footer-icon-face", - "dashboard-heading", - "dashboard-items-face", - "dashboard-navigator", - "dashboard-no-items-face", - "dashboard-text-banner" - ], - "dirvish": [ - "dirvish-collapse-dir-face", - "dirvish-collapse-empty-dir-face", - "dirvish-collapse-file-face", - "dirvish-emerge-group-title", - "dirvish-file-device-number", - "dirvish-file-group-id", - "dirvish-file-inode-number", - "dirvish-file-link-number", - "dirvish-file-modes", - "dirvish-file-size", - "dirvish-file-time", - "dirvish-file-user-id", - "dirvish-free-space", - "dirvish-git-commit-message-face", - "dirvish-hl-line", - "dirvish-hl-line-inactive", - "dirvish-inactive", - "dirvish-media-info-heading", - "dirvish-media-info-property-key", - "dirvish-narrow-match-face-0", - "dirvish-narrow-match-face-1", - "dirvish-narrow-match-face-2", - "dirvish-narrow-match-face-3", - "dirvish-narrow-split", - "dirvish-proc-failed", - "dirvish-proc-finished", - "dirvish-proc-running", - "dirvish-subtree-guide", - "dirvish-subtree-state", - "dirvish-vc-added-state", - "dirvish-vc-conflict-state", - "dirvish-vc-edited-state", - "dirvish-vc-locked-state", - "dirvish-vc-missing-state", - "dirvish-vc-needs-merge-face", - "dirvish-vc-needs-update-state", - "dirvish-vc-removed-state", - "dirvish-vc-unregistered-face" - ], - "elfeed": [ - "elfeed-log-date-face", - "elfeed-log-debug-level-face", - "elfeed-log-error-level-face", - "elfeed-log-info-level-face", - "elfeed-log-warn-level-face", - "elfeed-search-date-face", - "elfeed-search-feed-face", - "elfeed-search-filter-face", - "elfeed-search-last-update-face", - "elfeed-search-tag-face", - "elfeed-search-title-face", - "elfeed-search-unread-count-face", - "elfeed-search-unread-title-face" - ], - "embark": [ - "embark-collect-annotation", - "embark-collect-candidate", - "embark-collect-group-separator", - "embark-collect-group-title", - "embark-keybinding", - "embark-keybinding-repeat", - "embark-keymap", - "embark-selected", - "embark-target", - "embark-verbose-indicator-documentation", - "embark-verbose-indicator-shadowed", - "embark-verbose-indicator-title" - ], - "emms": [ - "emms-browser-album-face", - "emms-browser-albumartist-face", - "emms-browser-artist-face", - "emms-browser-composer-face", - "emms-browser-performer-face", - "emms-browser-track-face", - "emms-browser-year/genre-face", - "emms-metaplaylist-mode-current-face", - "emms-metaplaylist-mode-face", - "emms-playlist-selected-face", - "emms-playlist-track-face" - ], - "flycheck": [ - "flycheck-delimited-error", - "flycheck-error", - "flycheck-error-delimiter", - "flycheck-error-list-checker-name", - "flycheck-error-list-column-number", - "flycheck-error-list-error", - "flycheck-error-list-error-message", - "flycheck-error-list-filename", - "flycheck-error-list-highlight", - "flycheck-error-list-id", - "flycheck-error-list-id-with-explainer", - "flycheck-error-list-info", - "flycheck-error-list-line-number", - "flycheck-error-list-warning", - "flycheck-fringe-error", - "flycheck-fringe-info", - "flycheck-fringe-warning", - "flycheck-info", - "flycheck-verify-select-checker", - "flycheck-warning" - ], - "flyspell-correct": [ - "flyspell-correct-highlight-face" - ], - "ghostel": [ - "ghostel-color-black", - "ghostel-color-blue", - "ghostel-color-bright-black", - "ghostel-color-bright-blue", - "ghostel-color-bright-cyan", - "ghostel-color-bright-green", - "ghostel-color-bright-magenta", - "ghostel-color-bright-red", - "ghostel-color-bright-white", - "ghostel-color-bright-yellow", - "ghostel-color-cyan", - "ghostel-color-green", - "ghostel-color-magenta", - "ghostel-color-red", - "ghostel-color-white", - "ghostel-color-yellow", - "ghostel-default", - "ghostel-fake-cursor", - "ghostel-fake-cursor-box" - ], - "git-gutter": [ - "git-gutter:added", - "git-gutter:deleted", - "git-gutter:modified", - "git-gutter:separator", - "git-gutter:unchanged" - ], - "highlight-indent-guides": [ - "highlight-indent-guides-character-face", - "highlight-indent-guides-even-face", - "highlight-indent-guides-odd-face", - "highlight-indent-guides-stack-character-face", - "highlight-indent-guides-stack-even-face", - "highlight-indent-guides-stack-odd-face", - "highlight-indent-guides-top-character-face", - "highlight-indent-guides-top-even-face", - "highlight-indent-guides-top-odd-face" - ], - "hl-todo": [ - "hl-todo", - "hl-todo-flymake-type" - ], - "json-mode": [ - "json-mode-object-name-face" - ], - "llama": [ - "llama-##-macro", - "llama-deleted-argument", - "llama-llama-macro", - "llama-mandatory-argument", - "llama-optional-argument" - ], - "lsp-mode": [ - "lsp-details-face", - "lsp-face-highlight-read", - "lsp-face-highlight-textual", - "lsp-face-highlight-write", - "lsp-face-rename", - "lsp-inlay-hint-face", - "lsp-inlay-hint-parameter-face", - "lsp-inlay-hint-type-face", - "lsp-installation-buffer-face", - "lsp-installation-finished-buffer-face", - "lsp-rename-placeholder-face", - "lsp-signature-face", - "lsp-signature-highlight-function-argument", - "lsp-signature-posframe" - ], - "lv": [ - "lv-separator" - ], - "magit": [ - "git-commit-comment-action", - "git-commit-comment-branch-local", - "git-commit-comment-branch-remote", - "git-commit-comment-detached", - "git-commit-comment-file", - "git-commit-comment-heading", - "git-commit-keyword", - "git-commit-nonempty-second-line", - "git-commit-overlong-summary", - "git-commit-summary", - "git-commit-trailer-token", - "git-commit-trailer-value", - "magit-bisect-bad", - "magit-bisect-good", - "magit-bisect-skip", - "magit-blame-date", - "magit-blame-dimmed", - "magit-blame-hash", - "magit-blame-heading", - "magit-blame-highlight", - "magit-blame-margin", - "magit-blame-name", - "magit-blame-summary", - "magit-branch-current", - "magit-branch-local", - "magit-branch-remote", - "magit-branch-remote-head", - "magit-branch-upstream", - "magit-branch-warning", - "magit-cherry-equivalent", - "magit-cherry-unmatched", - "magit-diff-added", - "magit-diff-added-highlight", - "magit-diff-base", - "magit-diff-base-highlight", - "magit-diff-conflict-heading", - "magit-diff-conflict-heading-highlight", - "magit-diff-context", - "magit-diff-context-highlight", - "magit-diff-file-heading", - "magit-diff-file-heading-highlight", - "magit-diff-file-heading-selection", - "magit-diff-hunk-heading", - "magit-diff-hunk-heading-highlight", - "magit-diff-hunk-heading-selection", - "magit-diff-hunk-region", - "magit-diff-lines-boundary", - "magit-diff-lines-heading", - "magit-diff-our", - "magit-diff-our-highlight", - "magit-diff-removed", - "magit-diff-removed-highlight", - "magit-diff-revision-summary", - "magit-diff-revision-summary-highlight", - "magit-diff-their", - "magit-diff-their-highlight", - "magit-diff-whitespace-warning", - "magit-diffstat-added", - "magit-diffstat-removed", - "magit-dimmed", - "magit-filename", - "magit-hash", - "magit-head", - "magit-header-line", - "magit-header-line-key", - "magit-header-line-log-select", - "magit-keyword", - "magit-keyword-squash", - "magit-log-author", - "magit-log-date", - "magit-log-graph", - "magit-mode-line-process", - "magit-mode-line-process-error", - "magit-process-ng", - "magit-process-ok", - "magit-reflog-amend", - "magit-reflog-checkout", - "magit-reflog-cherry-pick", - "magit-reflog-commit", - "magit-reflog-merge", - "magit-reflog-other", - "magit-reflog-rebase", - "magit-reflog-remote", - "magit-reflog-reset", - "magit-refname", - "magit-refname-pullreq", - "magit-refname-stash", - "magit-refname-wip", - "magit-sequence-done", - "magit-sequence-drop", - "magit-sequence-exec", - "magit-sequence-head", - "magit-sequence-onto", - "magit-sequence-part", - "magit-sequence-pick", - "magit-sequence-stop", - "magit-signature-bad", - "magit-signature-error", - "magit-signature-expired", - "magit-signature-expired-key", - "magit-signature-good", - "magit-signature-revoked", - "magit-signature-untrusted", - "magit-tag" - ], - "magit-section": [ - "magit-left-margin", - "magit-section-child-count", - "magit-section-heading", - "magit-section-heading-selection", - "magit-section-highlight", - "magit-section-secondary-heading" - ], - "malyon": [ - "malyon-face-bold", - "malyon-face-error", - "malyon-face-italic", - "malyon-face-plain", - "malyon-face-reverse" - ], - "marginalia": [ - "marginalia-archive", - "marginalia-char", - "marginalia-date", - "marginalia-documentation", - "marginalia-file-name", - "marginalia-file-owner", - "marginalia-file-priv-dir", - "marginalia-file-priv-exec", - "marginalia-file-priv-link", - "marginalia-file-priv-no", - "marginalia-file-priv-other", - "marginalia-file-priv-rare", - "marginalia-file-priv-read", - "marginalia-file-priv-write", - "marginalia-function", - "marginalia-installed", - "marginalia-key", - "marginalia-lighter", - "marginalia-list", - "marginalia-mode", - "marginalia-modified", - "marginalia-null", - "marginalia-number", - "marginalia-off", - "marginalia-on", - "marginalia-size", - "marginalia-string", - "marginalia-symbol", - "marginalia-true", - "marginalia-type", - "marginalia-value", - "marginalia-version" - ], - "markdown-mode": [ - "markdown-blockquote-face", - "markdown-bold-face", - "markdown-code-face", - "markdown-comment-face", - "markdown-footnote-marker-face", - "markdown-footnote-text-face", - "markdown-gfm-checkbox-face", - "markdown-header-delimiter-face", - "markdown-header-face", - "markdown-header-face-1", - "markdown-header-face-2", - "markdown-header-face-3", - "markdown-header-face-4", - "markdown-header-face-5", - "markdown-header-face-6", - "markdown-header-rule-face", - "markdown-highlight-face", - "markdown-highlighting-face", - "markdown-hr-face", - "markdown-html-attr-name-face", - "markdown-html-attr-value-face", - "markdown-html-entity-face", - "markdown-html-tag-delimiter-face", - "markdown-html-tag-name-face", - "markdown-inline-code-face", - "markdown-italic-face", - "markdown-language-info-face", - "markdown-language-keyword-face", - "markdown-line-break-face", - "markdown-link-face", - "markdown-link-title-face", - "markdown-list-face", - "markdown-markup-face", - "markdown-math-face", - "markdown-metadata-key-face", - "markdown-metadata-value-face", - "markdown-missing-link-face", - "markdown-plain-url-face", - "markdown-pre-face", - "markdown-reference-face", - "markdown-strike-through-face", - "markdown-table-face", - "markdown-url-face" - ], - "nerd-icons": [ - "nerd-icons-blue", - "nerd-icons-blue-alt", - "nerd-icons-cyan", - "nerd-icons-cyan-alt", - "nerd-icons-dblue", - "nerd-icons-dcyan", - "nerd-icons-dgreen", - "nerd-icons-dmaroon", - "nerd-icons-dorange", - "nerd-icons-dpink", - "nerd-icons-dpurple", - "nerd-icons-dred", - "nerd-icons-dsilver", - "nerd-icons-dyellow", - "nerd-icons-green", - "nerd-icons-lblue", - "nerd-icons-lcyan", - "nerd-icons-lgreen", - "nerd-icons-lmaroon", - "nerd-icons-lorange", - "nerd-icons-lpink", - "nerd-icons-lpurple", - "nerd-icons-lred", - "nerd-icons-lsilver", - "nerd-icons-lyellow", - "nerd-icons-maroon", - "nerd-icons-orange", - "nerd-icons-pink", - "nerd-icons-purple", - "nerd-icons-purple-alt", - "nerd-icons-red", - "nerd-icons-red-alt", - "nerd-icons-silver", - "nerd-icons-yellow" - ], - "nerd-icons-completion": [ - "nerd-icons-completion-dir-face" - ], - "orderless": [ - "orderless-match-face-0", - "orderless-match-face-1", - "orderless-match-face-2", - "orderless-match-face-3" - ], - "org-roam": [ - "org-roam-dailies-calendar-note", - "org-roam-dim", - "org-roam-header-line", - "org-roam-olp", - "org-roam-preview-heading", - "org-roam-preview-heading-highlight", - "org-roam-preview-heading-selection", - "org-roam-preview-region", - "org-roam-title" - ], - "org-superstar": [ - "org-superstar-first", - "org-superstar-header-bullet", - "org-superstar-item", - "org-superstar-leading" - ], - "prescient": [ - "prescient-primary-highlight", - "prescient-secondary-highlight" - ], - "rainbow-delimiters": [ - "rainbow-delimiters-base-error-face", - "rainbow-delimiters-base-face", - "rainbow-delimiters-depth-1-face", - "rainbow-delimiters-depth-2-face", - "rainbow-delimiters-depth-3-face", - "rainbow-delimiters-depth-4-face", - "rainbow-delimiters-depth-5-face", - "rainbow-delimiters-depth-6-face", - "rainbow-delimiters-depth-7-face", - "rainbow-delimiters-depth-8-face", - "rainbow-delimiters-depth-9-face", - "rainbow-delimiters-mismatched-face", - "rainbow-delimiters-unmatched-face" - ], - "symbol-overlay": [ - "symbol-overlay-default-face", - "symbol-overlay-face-1", - "symbol-overlay-face-2", - "symbol-overlay-face-3", - "symbol-overlay-face-4", - "symbol-overlay-face-5", - "symbol-overlay-face-6", - "symbol-overlay-face-7", - "symbol-overlay-face-8" - ], - "tmr": [ - "tmr-description", - "tmr-duration", - "tmr-end-time", - "tmr-finished", - "tmr-is-acknowledged", - "tmr-must-be-acknowledged", - "tmr-start-time", - "tmr-tabulated-acknowledgement", - "tmr-tabulated-description", - "tmr-tabulated-end-time", - "tmr-tabulated-remaining-time", - "tmr-tabulated-start-time" - ], - "transient": [ - "transient-active-infix", - "transient-argument", - "transient-delimiter", - "transient-disabled-suffix", - "transient-enabled-suffix", - "transient-heading", - "transient-higher-level", - "transient-inactive-argument", - "transient-inactive-value", - "transient-inapt-argument", - "transient-inapt-suffix", - "transient-key", - "transient-key-exit", - "transient-key-noop", - "transient-key-recurse", - "transient-key-return", - "transient-key-stack", - "transient-key-stay", - "transient-mismatched-key", - "transient-nonstandard-key", - "transient-unreachable", - "transient-unreachable-key", - "transient-value" - ], - "vertico": [ - "vertico-current", - "vertico-group-separator", - "vertico-group-title", - "vertico-multiline" - ], - "web-mode": [ - "web-mode-annotation-face", - "web-mode-annotation-html-face", - "web-mode-annotation-tag-face", - "web-mode-annotation-type-face", - "web-mode-annotation-value-face", - "web-mode-block-attr-name-face", - "web-mode-block-attr-value-face", - "web-mode-block-comment-face", - "web-mode-block-control-face", - "web-mode-block-delimiter-face", - "web-mode-block-face", - "web-mode-block-string-face", - "web-mode-bold-face", - "web-mode-builtin-face", - "web-mode-comment-face", - "web-mode-comment-keyword-face", - "web-mode-constant-face", - "web-mode-css-at-rule-face", - "web-mode-css-color-face", - "web-mode-css-comment-face", - "web-mode-css-function-face", - "web-mode-css-priority-face", - "web-mode-css-property-name-face", - "web-mode-css-pseudo-class-face", - "web-mode-css-selector-class-face", - "web-mode-css-selector-face", - "web-mode-css-selector-tag-face", - "web-mode-css-string-face", - "web-mode-css-variable-face", - "web-mode-current-column-highlight-face", - "web-mode-current-element-highlight-face", - "web-mode-doctype-face", - "web-mode-error-face", - "web-mode-filter-face", - "web-mode-folded-face", - "web-mode-function-call-face", - "web-mode-function-name-face", - "web-mode-html-attr-custom-face", - "web-mode-html-attr-engine-face", - "web-mode-html-attr-equal-face", - "web-mode-html-attr-name-face", - "web-mode-html-attr-value-face", - "web-mode-html-entity-face", - "web-mode-html-tag-bracket-face", - "web-mode-html-tag-custom-face", - "web-mode-html-tag-face", - "web-mode-html-tag-namespaced-face", - "web-mode-html-tag-unclosed-face", - "web-mode-inlay-face", - "web-mode-interpolate-color1-face", - "web-mode-interpolate-color2-face", - "web-mode-interpolate-color3-face", - "web-mode-interpolate-color4-face", - "web-mode-italic-face", - "web-mode-javascript-comment-face", - "web-mode-javascript-string-face", - "web-mode-json-comment-face", - "web-mode-json-context-face", - "web-mode-json-key-face", - "web-mode-json-string-face", - "web-mode-jsx-depth-1-face", - "web-mode-jsx-depth-2-face", - "web-mode-jsx-depth-3-face", - "web-mode-jsx-depth-4-face", - "web-mode-jsx-depth-5-face", - "web-mode-keyword-face", - "web-mode-param-name-face", - "web-mode-part-comment-face", - "web-mode-part-face", - "web-mode-part-string-face", - "web-mode-preprocessor-face", - "web-mode-script-face", - "web-mode-sql-keyword-face", - "web-mode-string-face", - "web-mode-style-face", - "web-mode-symbol-face", - "web-mode-type-face", - "web-mode-underline-face", - "web-mode-variable-name-face", - "web-mode-warning-face", - "web-mode-whitespace-face" - ], - "yasnippet": [ - "yas--field-debug-face", - "yas-field-highlight-face" - ] -} diff --git a/scripts/theme-selector/samples.py b/scripts/theme-selector/samples.py deleted file mode 100644 index e487cc5e9..000000000 --- a/scripts/theme-selector/samples.py +++ /dev/null @@ -1,269 +0,0 @@ -GROUND="#0d0b0a" -COLS={ - 'kw':("#67809c",True),'bi':("#67809c",False),'pp':("#67809c",False), - 'fnd':("#a9b2bb",True),'fnc':("#a9b2bb",False),'dec':("#e8bd30",False), - 'ty':("#9b5fd0",False),'prop':("#838d97",False), - 'con':("#cb6b4d",False),'num':("#cb6b4d",False),'esc':("#cb6b4d",False), - 'str':("#2ba178",False),'re':("#5d9b86",False),'doc':("#5d9b86",False), - 'cm':("#be9e74",False),'cmd':("#a9b2bb",False), - 'var':("#e8bd30",False),'op':("#a9b2bb",False),'punc':("#a9b2bb",False),'p':("#ffffff",False), -} -NAMES={"#67809c":"blue","#e8bd30":"gold","#9b5fd0":"regal","#2ba178":"emerald","#cb6b4d":"terracotta","#be9e74":"tan","#5d9b86":"sage","#cdced1":"white","#a9b2bb":"silver","#838d97":"steel","#5e6770":"pewter","#2f343a":"gunmetal","#264364":"navy"} -def esc(t): return t.replace("&","&").replace("<","<").replace(">",">") -def span(k,t): - c,b=COLS[k]; w=";font-weight:bold" if b else "" - return f'{esc(t)}' -def render(lines): return "\n".join("".join(span(k,t) for k,t in ln) or " " for ln in lines) - -PYS=[ - [('cmd','#'),('cm',' theme.py')], - [('kw','from'),('p',' '),('var','dataclasses'),('p',' '),('kw','import'),('p',' '),('var','dataclass'),('punc',','),('p',' '),('var','field')], - [], - [('con','DEFAULT_PORT'),('op',':'),('p',' '),('ty','int'),('p',' '),('op','='),('p',' '),('num','8080')], - [('con','HEX'),('p',' '),('op','='),('p',' '),('var','re'),('op','.'),('fnc','compile'),('punc','('),('re','r"#[0-9a-f]{6}"'),('punc',')')], - [], - [('dec','@dataclass')], - [('kw','class'),('p',' '),('ty','Theme'),('op',':')], - [('p',' '),('doc','"""A color theme."""')], - [('p',' '),('prop','name'),('op',':'),('p',' '),('ty','str'),('p',' '),('op','='),('p',' '),('str','"dupre"')], - [('p',' '),('prop','colors'),('op',':'),('p',' '),('ty','dict'),('p',' '),('op','='),('p',' '),('fnc','field'),('punc','('),('prop','default_factory'),('op','='),('ty','dict'),('punc',')')], - [], - [('p',' '),('kw','def'),('p',' '),('fnd','resolve'),('punc','('),('var','self'),('punc',','),('p',' '),('var','key'),('op',':'),('p',' '),('ty','str'),('punc',')'),('p',' '),('op','->'),('p',' '),('ty','str'),('p',' '),('op','|'),('p',' '),('con','None'),('op',':')], - [('p',' '),('cmd','#'),('cm',' fallback to none')], - [('p',' '),('var','v'),('p',' '),('op','='),('p',' '),('var','self'),('op','.'),('prop','colors'),('op','.'),('fnc','get'),('punc','('),('var','key'),('punc',','),('p',' '),('str','"'),('esc','\\t'),('str','none"'),('punc',')')], - [('p',' '),('kw','if'),('p',' '),('bi','len'),('punc','('),('var','v'),('punc',')'),('p',' '),('op','=='),('p',' '),('num','0'),('op',':'),('p',' '),('kw','return'),('p',' '),('con','None')], - [('p',' '),('kw','return'),('p',' '),('var','v')], - [], - [('p',' '),('dec','@property')], - [('p',' '),('kw','def'),('p',' '),('fnd','size'),('punc','('),('var','self'),('punc',')'),('p',' '),('op','->'),('p',' '),('ty','int'),('op',':')], - [('p',' '),('kw','return'),('p',' '),('bi','len'),('punc','('),('var','self'),('op','.'),('prop','colors'),('punc',')')], - [], - [('var','theme'),('p',' '),('op','='),('p',' '),('ty','Theme'),('punc','('),('str','"dupre"'),('punc',')')], - [('fnc','print'),('punc','('),('var','theme'),('op','.'),('fnc','resolve'),('punc','('),('str','"bg"'),('punc','))')], -] -ELS=[ - [('cmd',';;'),('cm',' cache.el')], - [('punc','('),('kw','require'),('p',' '),('con',"'cl-lib"),('punc',')')], - [], - [('punc','('),('kw','defvar'),('p',' '),('var','cache--tbl'),('p',' '),('punc','('),('fnc','make-hash-table'),('p',' '),('con',':test'),('p',' '),('con',"'equal"),('punc','))')], - [('p',' '),('doc','"Memo table.")')], - [], - [('punc','('),('kw','defun'),('p',' '),('fnd','cache-get'),('p',' '),('punc','('),('var','key'),('punc',')')], - [('p',' '),('doc','"Return cached value for KEY."')], - [('p',' '),('punc','('),('kw','or'),('p',' '),('punc','('),('bi','gethash'),('p',' '),('var','key'),('p',' '),('var','cache--tbl'),('punc',')')], - [('p',' '),('punc','('),('kw','let'),('p',' '),('punc','(('),('var','v'),('p',' '),('punc','('),('fnc','compute'),('p',' '),('var','key'),('p',' '),('num','42'),('punc','))) ')], - [('p',' '),('punc','('),('fnc','puthash'),('p',' '),('var','key'),('p',' '),('var','v'),('p',' '),('var','cache--tbl'),('punc',') '),('var','v'),('punc','))))')], - [], - [('punc','('),('kw','defun'),('p',' '),('fnd','cache-clear'),('p',' '),('punc','()')], - [('p',' '),('doc','"Empty the memo table."')], - [('p',' '),('punc','('),('kw','interactive'),('punc',')')], - [('p',' '),('punc','('),('fnc','clrhash'),('p',' '),('var','cache--tbl'),('punc',')')], - [('p',' '),('punc','('),('fnc','message'),('p',' '),('str','"cleared'),('esc','\\n'),('str','"'),('punc','))')], - [], - [('punc','('),('kw','defun'),('p',' '),('fnd','cache-keys'),('p',' '),('punc','()')], - [('p',' '),('doc','"Return all keys."')], - [('p',' '),('punc','('),('kw','let'),('p',' '),('punc','(('),('var','acc'),('p',' '),('con','nil'),('punc','))')], - [('p',' '),('punc','('),('fnc','maphash'),('p',' '),('punc','('),('kw','lambda'),('p',' '),('punc','('),('var','k'),('p',' '),('var','_v'),('punc',')'),('p',' '),('punc','('),('fnc','push'),('p',' '),('var','k'),('p',' '),('var','acc'),('punc','))')], - [('p',' '),('var','cache--tbl'),('punc',')'),('p',' '),('var','acc'),('punc','))')], - [], - [('punc','('),('kw','provide'),('p',' '),('con',"'cache"),('punc',')')], -] -GOS=[ - [('cmd','//'),('cm',' queue.go')], - [('kw','package'),('p',' '),('var','main')], - [], - [('kw','import'),('p',' '),('str','"fmt"')], - [], - [('kw','const'),('p',' '),('con','MaxItems'),('p',' '),('op','='),('p',' '),('num','100')], - [], - [('kw','type'),('p',' '),('ty','Order'),('p',' '),('kw','struct'),('p',' '),('punc','{')], - [('p',' '),('prop','ID'),('p',' '),('ty','int')], - [('p',' '),('prop','Name'),('p',' '),('ty','string')], - [('punc','}')], - [], - [('kw','func'),('p',' '),('punc','('),('var','q'),('p',' '),('op','*'),('ty','Queue'),('punc',')'),('p',' '),('fnd','Push'),('punc','('),('var','o'),('p',' '),('op','*'),('ty','Order'),('punc',')'),('p',' '),('ty','error'),('p',' '),('punc','{')], - [('p',' '),('cmd','//'),('cm',' reject nil')], - [('p',' '),('kw','if'),('p',' '),('var','o'),('p',' '),('op','=='),('p',' '),('con','nil'),('p',' '),('punc','{')], - [('p',' '),('kw','return'),('p',' '),('fnc','fmt.Errorf'),('punc','('),('str','"nil'),('esc','\\n'),('str','"'),('punc',')')], - [('p',' '),('punc','}')], - [('p',' '),('var','q'),('op','.'),('prop','items'),('p',' '),('op','='),('p',' '),('bi','append'),('punc','('),('var','q'),('op','.'),('prop','items'),('punc',','),('p',' '),('var','o'),('punc',')')], - [('p',' '),('kw','return'),('p',' '),('con','nil')], - [('punc','}')], - [], - [('kw','func'),('p',' '),('fnd','main'),('punc','()'),('p',' '),('punc','{')], - [('p',' '),('fnc','fmt.Println'),('punc','('),('op','&'),('ty','Queue'),('punc','{}'),('punc',')')], - [('punc','}')], -] -TSS=[ - [('cmd','//'),('cm',' orders.ts')], - [('kw','import'),('p',' '),('punc','{'),('p',' '),('ty','Order'),('p',' '),('punc','}'),('p',' '),('kw','from'),('p',' '),('str','"./types"')], - [], - [('kw','export'),('p',' '),('kw','interface'),('p',' '),('ty','Queue'),('p',' '),('punc','{')], - [('p',' '),('prop','max'),('op',':'),('p',' '),('ty','number'),('punc',';')], - [('p',' '),('prop','items'),('op',':'),('p',' '),('ty','Order'),('punc','[];')], - [('punc','}')], - [], - [('dec','@Injectable'),('punc','()')], - [('kw','export'),('p',' '),('kw','class'),('p',' '),('ty','OrderQueue'),('p',' '),('kw','implements'),('p',' '),('ty','Queue'),('p',' '),('punc','{')], - [('p',' '),('kw','private'),('p',' '),('prop','re'),('p',' '),('op','='),('p',' '),('re','/^#[0-9a-f]{6}$/i'),('punc',';')], - [], - [('p',' '),('fnd','push'),('punc','('),('var','o'),('op',':'),('p',' '),('ty','Order'),('punc',')'),('op',':'),('p',' '),('ty','boolean'),('p',' '),('punc','{')], - [('p',' '),('kw','if'),('p',' '),('punc','('),('var','o'),('p',' '),('op','==='),('p',' '),('con','null'),('punc',')'),('p',' '),('kw','return'),('p',' '),('con','false'),('punc',';')], - [('p',' '),('var','console'),('op','.'),('fnc','log'),('punc','('),('str','`id '),('punc','${'),('var','o'),('op','.'),('prop','id'),('punc','}'),('esc','\\n'),('str','`'),('punc',');')], - [('p',' '),('kw','return'),('p',' '),('con','true'),('punc',';')], - [('p',' '),('punc','}')], - [('punc','}')], - [], - [('kw','const'),('p',' '),('con','LIMIT'),('op',':'),('p',' '),('ty','number'),('p',' '),('op','='),('p',' '),('num','50'),('punc',';')], - [('kw','const'),('p',' '),('var','q'),('p',' '),('op','='),('p',' '),('kw','new'),('p',' '),('ty','OrderQueue'),('punc','()'),('punc',';')], - [('var','q'),('op','.'),('fnd','push'),('punc','('),('punc','{'),('p',' '),('prop','id'),('op',':'),('p',' '),('num','1'),('p',' '),('punc','}'),('p',' '),('kw','as'),('p',' '),('ty','Order'),('punc',')'),('punc',';')], - [('var','console'),('op','.'),('fnc','log'),('punc','('),('var','q'),('op','.'),('prop','max'),('punc',')'),('punc',';')], - [('kw','const'),('p',' '),('var','cap'),('p',' '),('op','='),('p',' '),('var','Math'),('op','.'),('bi','max'),('punc','('),('con','LIMIT'),('punc',','),('p',' '),('num','0'),('punc',')'),('punc',';')], -] - -CS=[ - [('cmd','/**'),('doc',' Order queue. */')], - [('pp','#include'),('p',' '),('str','')], - [('pp','#include'),('p',' '),('str','')], - [('pp','#define'),('p',' '),('con','MAX_PORT'),('p',' '),('num','8080')], - [], - [('kw','typedef'),('p',' '),('kw','struct'),('p',' '),('punc','{')], - [('p',' '),('ty','int'),('p',' '),('prop','id'),('punc',';')], - [('p',' '),('kw','const'),('p',' '),('ty','char'),('p',' '),('op','*'),('prop','name'),('punc',';')], - [('punc','}'),('p',' '),('ty','Order'),('punc',';')], - [], - [('cmd','//'),('cm',' returns -1 on null input')], - [('ty','int'),('p',' '),('fnd','push'),('punc','('),('ty','Order'),('p',' '),('op','*'),('var','o'),('punc',')'),('p',' '),('dec','__attribute__'),('punc','(('),('dec','nonnull'),('punc','))'),('p',' '),('punc','{')], - [('p',' '),('kw','if'),('p',' '),('punc','('),('var','o'),('p',' '),('op','=='),('p',' '),('con','NULL'),('punc',')'),('p',' '),('kw','return'),('p',' '),('num','-1'),('punc',';')], - [('p',' '),('fnc','printf'),('punc','('),('str','"id=%d'),('esc','\\n'),('str','"'),('punc',','),('p',' '),('var','o'),('op','->'),('prop','id'),('punc',');')], - [('p',' '),('kw','return'),('p',' '),('num','0'),('punc',';')], - [('punc','}')], - [], - [('ty','int'),('p',' '),('fnd','main'),('punc','('),('ty','void'),('punc',')'),('p',' '),('punc','{')], - [('p',' '),('ty','Order'),('p',' '),('var','o'),('p',' '),('op','='),('p',' '),('punc','{'),('p',' '),('op','.'),('prop','id'),('p',' '),('op','='),('p',' '),('num','1'),('punc',','),('p',' '),('op','.'),('prop','name'),('p',' '),('op','='),('p',' '),('str','"dupre"'),('p',' '),('punc','}'),('punc',';')], - [('p',' '),('ty','Order'),('p',' '),('op','*'),('var','p2'),('p',' '),('op','='),('p',' '),('bi','malloc'),('punc','('),('bi','sizeof'),('punc','('),('ty','Order'),('punc','))'),('punc',';')], - [('p',' '),('fnc','push'),('punc','('),('op','&'),('var','o'),('punc',')'),('punc',';')], - [('p',' '),('bi','free'),('punc','('),('var','p2'),('punc',')'),('punc',';')], - [('p',' '),('kw','return'),('p',' '),('num','0'),('punc',';')], - [('punc','}')], -] -CPS=[ - [('cmd','/**'),('doc',' A color theme. */')], - [('pp','#include'),('p',' '),('str','')], - [('pp','#include'),('p',' '),('str','')], - [('pp','#pragma'),('p',' '),('pp','once')], - [], - [('kw','namespace'),('p',' '),('var','dupre'),('p',' '),('punc','{')], - [], - [('kw','template'),('op','<'),('kw','typename'),('p',' '),('ty','T'),('op','>'),('p',' '),('kw','class'),('p',' '),('ty','Theme'),('p',' '),('punc','{')], - [('kw','public'),('op',':')], - [('p',' '),('kw','static'),('p',' '),('kw','constexpr'),('p',' '),('ty','int'),('p',' '),('con','MAX'),('p',' '),('op','='),('p',' '),('num','0x20'),('punc',';')], - [('p',' '),('ty','std'),('op','::'),('ty','string'),('p',' '),('prop','name_'),('p',' '),('op','='),('p',' '),('str','"dupre"'),('punc',';')], - [], - [('p',' '),('dec','[[nodiscard]]'),('p',' '),('ty','T'),('p',' '),('fnd','resolve'),('punc','('),('kw','const'),('p',' '),('ty','std'),('op','::'),('ty','string'),('op','&'),('p',' '),('var','key'),('punc',')'),('p',' '),('kw','const'),('p',' '),('punc','{')], - [('p',' '),('cmd','//'),('cm',' validate against a hex pattern')], - [('p',' '),('kw','static'),('p',' '),('ty','std'),('op','::'),('ty','regex'),('p',' '),('var','re'),('punc','('),('re','R"(#[0-9a-f]{6})"'),('punc',')'),('punc',';')], - [('p',' '),('kw','if'),('p',' '),('punc','('),('var','key'),('op','.'),('fnc','empty'),('punc','()'),('punc',')'),('p',' '),('kw','return'),('p',' '),('con','nullptr'),('punc',';')], - [('p',' '),('kw','return'),('p',' '),('ty','T'),('punc','{'),('var','key'),('punc','}'),('punc',';')], - [('p',' '),('punc','}')], - [('punc','}'),('punc',';')], - [], - [('ty','int'),('p',' '),('fnd','main'),('punc','()'),('p',' '),('punc','{')], - [('p',' '),('kw','auto'),('p',' '),('var','t'),('p',' '),('op','='),('p',' '),('ty','Theme'),('op','<'),('ty','int'),('op','>'),('punc','{}'),('punc',';')], - [('p',' '),('bi','static_cast'),('op','<'),('ty','int'),('op','>'),('punc','('),('var','t'),('op','.'),('prop','name_'),('op','.'),('fnc','size'),('punc','())'),('punc',';')], - [('p',' '),('ty','std'),('op','::'),('fnc','printf'),('punc','('),('str','"%s'),('esc','\\n'),('str','"'),('punc',','),('p',' '),('var','t'),('op','.'),('prop','name_'),('op','.'),('fnc','c_str'),('punc','())'),('punc',';')], - [('p',' '),('kw','return'),('p',' '),('num','0'),('punc',';')], - [('punc','}')], -] -JAS=[ - [('cmd','/**'),('doc',' A color theme. */')], - [('kw','package'),('p',' '),('var','com'),('op','.'),('var','dupre'),('punc',';')], - [('kw','import'),('p',' '),('var','java'),('op','.'),('var','util'),('op','.'),('var','regex'),('op','.'),('ty','Pattern'),('punc',';')], - [], - [('dec','@Deprecated')], - [('kw','public'),('p',' '),('kw','final'),('p',' '),('kw','class'),('p',' '),('ty','Theme'),('p',' '),('punc','{')], - [('p',' '),('kw','private'),('p',' '),('kw','static'),('p',' '),('kw','final'),('p',' '),('ty','int'),('p',' '),('con','MAX_PORT'),('p',' '),('op','='),('p',' '),('num','8080'),('punc',';')], - [('p',' '),('kw','private'),('p',' '),('kw','final'),('p',' '),('ty','String'),('p',' '),('prop','name'),('p',' '),('op','='),('p',' '),('str','"dupre"'),('punc',';')], - [('p',' '),('kw','private'),('p',' '),('kw','static'),('p',' '),('kw','final'),('p',' '),('ty','Pattern'),('p',' '),('con','HEX'),('p',' '),('op','='),('p',' '),('ty','Pattern'),('op','.'),('fnc','compile'),('punc','('),('re','"#[0-9a-f]{6}"'),('punc',')'),('punc',';')], - [], - [('p',' '),('dec','@Override')], - [('p',' '),('kw','public'),('p',' '),('ty','String'),('p',' '),('fnd','resolve'),('punc','('),('ty','String'),('p',' '),('var','key'),('punc',')'),('p',' '),('punc','{')], - [('p',' '),('cmd','//'),('cm',' fall back to null')], - [('p',' '),('kw','if'),('p',' '),('punc','('),('var','key'),('op','.'),('fnc','isEmpty'),('punc','()'),('punc',')'),('p',' '),('kw','return'),('p',' '),('con','null'),('punc',';')], - [('p',' '),('kw','return'),('p',' '),('var','key'),('op','.'),('fnc','strip'),('punc','('),('punc',')'),('op','+'),('str','"'),('esc','\\t'),('str','"'),('punc',';')], - [('p',' '),('punc','}')], - [], - [('p',' '),('kw','public'),('p',' '),('kw','static'),('p',' '),('ty','void'),('p',' '),('fnd','main'),('punc','('),('ty','String'),('punc','[]'),('p',' '),('var','args'),('punc',')'),('p',' '),('punc','{')], - [('p',' '),('ty','var'),('p',' '),('var','t'),('p',' '),('op','='),('p',' '),('kw','new'),('p',' '),('ty','Theme'),('punc','()'),('punc',';')], - [('p',' '),('ty','System'),('op','.'),('prop','out'),('op','.'),('fnc','println'),('punc','('),('var','t'),('op','.'),('fnc','resolve'),('punc','('),('str','"bg"'),('punc','))'),('punc',';')], - [('p',' '),('punc','}')], - [('punc','}')], -] -SHS=[ - [('cmd','#!'),('cm','/bin/bash')], - [('cmd','#'),('cm',' deploy.sh')], - [('bi','set'),('p',' '),('op','-'),('var','euo'),('p',' '),('var','pipefail')], - [], - [('var','PORT'),('op','='),('num','8080')], - [('var','NAME'),('op','='),('str','"dupre"')], - [], - [('fnd','deploy'),('punc','()'),('p',' '),('punc','{')], - [('p',' '),('kw','local'),('p',' '),('var','target'),('op','='),('str','"$1"')], - [('p',' '),('kw','if'),('p',' '),('punc','[['),('p',' '),('op','-z'),('p',' '),('str','"$target"'),('p',' '),('punc',']]'),('punc',';'),('p',' '),('kw','then')], - [('p',' '),('bi','echo'),('p',' '),('str','"no target"')], - [('p',' '),('kw','return'),('p',' '),('num','1')], - [('p',' '),('kw','fi')], - [('p',' '),('fnc','rsync'),('p',' '),('op','-az'),('p',' '),('str','"$NAME"'),('p',' '),('str','"$target"')], - [('punc','}')], - [], - [('fnd','main'),('punc','()'),('p',' '),('punc','{')], - [('p',' '),('kw','for'),('p',' '),('var','host'),('p',' '),('kw','in'),('p',' '),('str','"$@"'),('punc',';'),('p',' '),('kw','do')], - [('p',' '),('fnc','deploy'),('p',' '),('str','"$host"'),('p',' '),('op','||'),('p',' '),('bi','exit'),('p',' '),('num','1')], - [('p',' '),('kw','done')], - [('p',' '),('bi','echo'),('p',' '),('op','-e'),('p',' '),('str','"all done'),('esc','\\n'),('str','"')], - [('punc','}')], - [], - [('fnc','main'),('p',' '),('str','"$@"')], -] - -cols="".join(f'

{n}

{render(s)}
' for n,s in [("Elisp",ELS),("Go",GOS),("Python",PYS),("TypeScript",TSS),("Java",JAS),("C",CS),("C++",CPS),("Shell",SHS)]) -legend_rows=[ - ("keyword (bold)","kw","class def if return import"),("builtin","bi","len range print"), - ("function — definition (bold)","fnd","resolve cache-get push"),("function — call","fnc","get append fmt.Errorf"), - ("decorator / attribute","dec","@dataclass @Injectable"),("type / class","ty","str dict Order Queue boolean"), - ("property / field / key","prop","name colors items id re"),("constant","con","None nil true MaxItems :test"), - ("number","num","8080 100 42 0"),("string","str",'"dupre" "fmt" `id`'),("escape","esc",r'\t \n'), - ("regexp","re",'/^#[0-9a-f]{6}$/i'),("docstring","doc",'"""..." "Memo table."'), - ("comment","cm","# reject nil // fallback"),("comment delimiter","cmd","# // ;; /*"), - ("variable / use","var","v key self q console"),("operator","op",": = -> | == === . *"), - ("punctuation / bracket","punc","{ } ( ) [ ] , ;"), -] -def lrow(label,k,ex): - c,b=COLS[k] - return f'{NAMES.get(c,"")}{c}{label}{esc(ex)}' -legend="".join(lrow(l,k,e) for l,k,e in legend_rows) -def grp(title,items): - sw="".join(f'
{n}
{h}
' for n,h in items) - return f'
{title}
{sw}
' -palette=(grp("ground / foreground",[("ground","#0d0b0a"),("bg-dim","#1a1714"),("fg","#cdced1")]) - + grp("syntax hues",[("blue · keyword","#67809c"),("gold · variable","#e8bd30"),("regal · type","#9b5fd0"),("emerald · string","#2ba178"),("terracotta · const/num","#cb6b4d"),("tan · comment","#be9e74")]) - + grp("metallic greyscale (structural)",[("gunmetal","#2f343a"),("metal","#474e56"),("pewter","#5e6770"),("steel · property","#838d97"),("silver · fn/op/punct","#a9b2bb"),("bright · fg","#cdced1")]) - + grp("special green + fills",[("muted emerald · doc/regexp","#5d9b86"),("navy fill","#264364"),("gunmetal fill","#2f343a")])) -html=f'''dupre revision — canonical - -

code samples

-
{cols}
-

color → tree-sitter category assignment — click a header to sort

-{legend}
color △hex △category △example
-

palette

-{palette}\n''' -open("/tmp/dupre-canon.html","w").write(html) -print("wrote /tmp/dupre-canon.html") diff --git a/scripts/theme-selector/theme-selector.html b/scripts/theme-selector/theme-selector.html deleted file mode 100644 index 30279c745..000000000 --- a/scripts/theme-selector/theme-selector.html +++ /dev/null @@ -1,738 +0,0 @@ -theme-selector - -

Untitled: theme

-
-
-

palette

-
-
-
- - - - - -
-
-
-
-
-
#888888
-
limit
-
-
-
-
-
-

export, import, and save

-
- -
-
- - - - -
- -
-
-

code/color assignments

-
-
-
elements △color △styleexamplecontrast
-
-
-
-

- 
-
-

ui faces

-
-
-
face △foreground △background △stylepreview
-
-
-
-
-
-
-

package faces

-
- - - -
-
-
-
face △fg △bg △styleinherit △size △contrast △
-
-
-
-
-
-
- \ No newline at end of file diff --git a/scripts/theme-studio/README.md b/scripts/theme-studio/README.md new file mode 100644 index 000000000..620340396 --- /dev/null +++ b/scripts/theme-studio/README.md @@ -0,0 +1,166 @@ +# theme-studio + +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/-*.el`. + +## Run + +```bash +python3 generate.py # writes theme-studio.html beside this script +``` + +Then open it in Chrome (Firefox had color-rendering flakiness during design): + +```bash +WAYLAND_DISPLAY=wayland-1 google-chrome-stable theme-studio.html +``` + +During color work, disable Hyprland inactive-window dimming so colors read true: + +```bash +hyprctl keyword decoration:dim_inactive false +``` + +## Files + +- `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-studio.html` — generated output. Regenerate; don't hand-edit. + +## What it captures + +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. + +Twenty applications have bespoke previews that exercise nearly all of their +faces: org-mode (a document plus an agenda view), magit (a status buffer plus +blame, reflog, sequence, bisect, and signature rows), elfeed (a search list and +log), ghostel (a mock terminal with the 16 ANSI colors), mu4e (a headers list, +message view, and compose stub), dashboard, lsp-mode (signatures, inlay hints, +symbol highlights, rename), git-gutter, flycheck (a diagnostic line plus an +error-list buffer), dired, dirvish (attribute columns, vc states, media, proc, +narrow), calibredb (a library listing and detail view), erc (an IRC channel), +org-drill (a cloze flashcard), org-noter, signel (a Signal chat), pearl (a +ticket), slack (a channel with mrkdwn, attachments, blocks, and dialogs), and +telega (chat entities, reactions, buttons, and webpage rendering), and shr (the +built-in HTML renderer behind nov, eww, elfeed's article view, and HTML mail, so +theming it themes all of them). 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. Clicking a face row flashes that face in the preview, and clicking +a preview element flashes its row. + +**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-studio/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 + +The export (and what a build step consumes): + +```json +{ + "name": "dupre", + "palette": [["#67809c", "blue"], ["#e8bd30", "gold"]], + "assignments": {"kw": "#67809c", "str": "#5d9b86", "bg": "#000000", "p": "#ffffff"}, + "bold": ["kw", "fnd"], + "italic": [], + "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, + "underline": false, "strike": false, + "inherit": null, "height": 1.3, "source": "default"} + } + } +} +``` + +- `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`, + `underline`, `strike`, 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 converter writes `underline` as + `:underline t` and `strike` as `:strike-through t`. +- 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. + +## Build step — `build-theme.el` + +`build-theme.el` converts a `theme.json` into a single self-contained +`themes/-theme.el` deftheme. JSON in, valid Emacs faces out, across all +four tiers: `default` from `assignments.bg`/`.p`, the syntax categories mapped +to their font-lock / tree-sitter faces (with the `bold`/`italic` sets applied), +the UI faces passed through, and the package faces with `:inherit`/`:height` +and weight/slant written. + +```bash +emacs --batch -l scripts/theme-studio/build-theme.el \ + --eval '(build-theme/convert-file "scripts/theme-studio/dupre-revised.json" "themes")' +``` + +Output is a flat generated deftheme, not the palette/faces/theme trio the +original dupre ships — a `theme.json` carries resolved per-face hex, not dupre's +semantic-mapping layer, so a flat deftheme is the faithful output and never +clobbers the curated dupre files. + +One mapping limitation: the `dec` (decorator) syntax key has no independent +Emacs face. Emacs renders decorators with `font-lock-type-face`, which the `ty` +key already owns, so `dec` is omitted from the output and decorators follow the +type color (as they do in stock Emacs). Tests live in +`tests/test-build-theme.el` (Normal / Boundary / Error, plus a WCAG-contrast +assertion on the round-tripped result). diff --git a/scripts/theme-studio/build-inventory.el b/scripts/theme-studio/build-inventory.el new file mode 100644 index 000000000..04d821453 --- /dev/null +++ b/scripts/theme-studio/build-inventory.el @@ -0,0 +1,31 @@ +;;; build-inventory.el --- emit package->faces inventory for theme-studio -*- lexical-binding: t -*- +;;; Commentary: +;; Loaded into a running Emacs (emacsclient -e '(load ".../build-inventory.el")') +;; to write package-inventory.json next to itself: a JSON object mapping each +;; installed (elpa/straight) package to the faces it defines, grouped by the +;; package that owns the face's definition file. Built-in faces are skipped. +;; generate.py embeds the JSON so the theme-studio dropdown can reach every +;; installed package (tier-3 phase 6, the "theme every package" path). +;;; Code: + +(require 'json) + +(let ((h (make-hash-table :test 'equal))) + (dolist (f (face-list)) + (let* ((file (ignore-errors (symbol-file f 'defface))) + (pkg (and (stringp file) + (string-match "/\\(?:elpa\\|straight/build\\|site-lisp\\)/\\([a-zA-Z0-9._-]+?\\)-[0-9][^/]*/" file) + (match-string 1 file)))) + (when pkg (push (symbol-name f) (gethash pkg h))))) + (let (al) + (maphash (lambda (k v) (push (cons (intern k) (sort v #'string<)) al)) h) + (setq al (sort al (lambda (a b) (string< (symbol-name (car a)) (symbol-name (car b)))))) + (with-temp-file (expand-file-name + "package-inventory.json" + (file-name-directory (or load-file-name buffer-file-name + "~/.emacs.d/scripts/theme-studio/"))) + (let ((json-encoding-pretty-print t)) + (insert (json-encode al) "\n"))))) + +(provide 'build-inventory) +;;; build-inventory.el ends here diff --git a/scripts/theme-studio/build-theme.el b/scripts/theme-studio/build-theme.el new file mode 100644 index 000000000..c869dea18 --- /dev/null +++ b/scripts/theme-studio/build-theme.el @@ -0,0 +1,244 @@ +;;; build-theme.el --- Convert a theme-studio theme.json into a deftheme -*- lexical-binding: t -*- + +;; Author: Craig Jennings + +;;; Commentary: + +;; The last link in the theme-studio pipeline: turn a theme.json exported by +;; the tool (see scripts/theme-studio/README.md and +;; docs/design/theme-studio-package-faces-spec.org) into a single, +;; self-contained, loadable Emacs deftheme written to themes/-theme.el. +;; +;; Four tiers come out of the JSON: +;; - default -- background from assignments.bg, foreground from .p +;; - syntax -- assignments. -> font-lock / tree-sitter faces, with +;; the bold / italic category sets applied +;; - ui -- the ui keys are already real face names; fg/bg passthrough +;; - packages -- per-package face specs with :inherit / :height / weight / +;; slant +;; +;; Usage (from a shell or a running Emacs): +;; +;; emacsclient -e '(progn (load ".../build-theme.el") +;; (build-theme/convert-file ".../dupre-revised.json"))' +;; +;; or in batch: +;; +;; emacs --batch -l build-theme.el \ +;; --eval '(build-theme/convert-file "dupre-revised.json" "themes")' +;; +;; The output is a flat generated deftheme, not the hand-authored +;; palette/faces/theme trio that the original dupre theme ships -- a theme.json +;; carries resolved per-face hex, not dupre's semantic-mapping layer, so a flat +;; deftheme is the faithful output and never clobbers the curated dupre files. + +;;; Code: + +(require 'json) +(require 'subr-x) + +(defconst build-theme/--syntax-face-map + '((kw . (font-lock-keyword-face)) + (bi . (font-lock-builtin-face)) + (pp . (font-lock-preprocessor-face)) + (fnd . (font-lock-function-name-face)) + (fnc . (font-lock-function-call-face)) + (ty . (font-lock-type-face)) + (prop . (font-lock-property-name-face font-lock-property-use-face)) + (con . (font-lock-constant-face)) + (num . (font-lock-number-face)) + (str . (font-lock-string-face)) + (esc . (font-lock-escape-face)) + (re . (font-lock-regexp-face)) + (doc . (font-lock-doc-face)) + (cm . (font-lock-comment-face)) + (cmd . (font-lock-comment-delimiter-face)) + (var . (font-lock-variable-name-face font-lock-variable-use-face)) + (op . (font-lock-operator-face)) + (punc . (font-lock-punctuation-face font-lock-bracket-face + font-lock-delimiter-face font-lock-misc-punctuation-face))) + "Map each theme.json syntax-category key to the font-lock faces it colors. +A category may fan out to several faces (e.g. punc covers bracket and +delimiter too). The dec (decorator) key is deliberately absent: Emacs has +no dedicated decorator face -- it renders decorators with +`font-lock-type-face', which the ty key already owns -- so coloring dec +independently is not possible without clobbering types.") + +;;; --------------------------------------------------------------------------- +;;; Pure helpers + +(defun build-theme/--hex-p (s) + "Non-nil when S is a \"#rrggbb\" hex color string." + (and (stringp s) (string-match-p "\\`#[0-9a-fA-F]\\{6\\}\\'" s))) + +(defun build-theme/--attrs (inherit fg bg bold italic underline strike height) + "Build a face-attribute plist from the given fields, in canonical order. +INHERIT is a face symbol or nil. FG and BG are hex strings or nil. BOLD, +ITALIC, UNDERLINE, and STRIKE are booleans. HEIGHT is a float multiplier; 1.0 +(or nil) is omitted as the default. Only set attributes are written, so a +fully-nil face yields an empty plist." + (let (plist) + (when (and height (numberp height) (/= height 1.0)) + (setq plist (list :height height))) + (when strike (setq plist (append (list :strike-through t) plist))) + (when underline (setq plist (append (list :underline t) plist))) + (when italic (setq plist (append (list :slant 'italic) plist))) + (when bold (setq plist (append (list :weight 'bold) plist))) + (when bg (setq plist (append (list :background bg) plist))) + (when fg (setq plist (append (list :foreground fg) plist))) + (when inherit (setq plist (append (list :inherit inherit) plist))) + plist)) + +(defun build-theme/--face-spec (face attrs) + "Wrap FACE and its ATTRS plist as a `custom-theme-set-faces' spec. +Return nil when ATTRS is empty, so cleared faces emit nothing." + (when attrs + (list face (list (list t attrs))))) + +(defun build-theme/--obj-get (obj key) + "Value of KEY in alist OBJ, or nil." + (cdr (assq key obj))) + +(defun build-theme/--inherit-symbol (value) + "Coerce an inherit VALUE (a face-name string, symbol, or nil) to a symbol." + (cond ((null value) nil) + ((symbolp value) value) + ((stringp value) (intern value)) + (t nil))) + +;;; --------------------------------------------------------------------------- +;;; Tiers + +(defun build-theme/--default-spec (assignments) + "Build the `default' face spec from ASSIGNMENTS bg / p." + (let ((bg (build-theme/--obj-get assignments 'bg)) + (fg (build-theme/--obj-get assignments 'p))) + (build-theme/--face-spec 'default (build-theme/--attrs nil fg bg nil nil nil nil nil)))) + +(defun build-theme/--syntax-face-specs (assignments bold italic) + "Build syntax-tier face specs from ASSIGNMENTS plus the BOLD and ITALIC sets. +BOLD and ITALIC are lists of category-key symbols. Each category fans out to +the font-lock faces in `build-theme/--syntax-face-map'." + (let (specs) + (dolist (pair build-theme/--syntax-face-map) + (let* ((cat (car pair)) + (faces (cdr pair)) + (hex (build-theme/--obj-get assignments cat))) + (when hex + (let ((attrs (build-theme/--attrs nil hex nil + (memq cat bold) (memq cat italic) nil nil nil))) + (dolist (face faces) + (when-let ((spec (build-theme/--face-spec face attrs))) + (push spec specs))))))) + (nreverse specs))) + +(defun build-theme/--ui-face-specs (ui) + "Build UI-tier face specs from the UI alist (face -> {fg,bg,bold,italic})." + (let (specs) + (dolist (entry ui) + (let* ((face (car entry)) + (obj (cdr entry)) + (attrs (build-theme/--attrs nil + (build-theme/--obj-get obj 'fg) + (build-theme/--obj-get obj 'bg) + (build-theme/--obj-get obj 'bold) + (build-theme/--obj-get obj 'italic) + (build-theme/--obj-get obj 'underline) + (build-theme/--obj-get obj 'strike) + nil))) + (when-let ((spec (build-theme/--face-spec face attrs))) + (push spec specs)))) + (nreverse specs))) + +(defun build-theme/--package-face-specs (packages) + "Build package-tier face specs from the PACKAGES alist (app -> face -> spec)." + (let (specs) + (dolist (app packages) + (dolist (entry (cdr app)) + (let* ((face (car entry)) + (obj (cdr entry)) + (attrs (build-theme/--attrs + (build-theme/--inherit-symbol (build-theme/--obj-get obj 'inherit)) + (build-theme/--obj-get obj 'fg) + (build-theme/--obj-get obj 'bg) + (build-theme/--obj-get obj 'bold) + (build-theme/--obj-get obj 'italic) + (build-theme/--obj-get obj 'underline) + (build-theme/--obj-get obj 'strike) + (build-theme/--obj-get obj 'height)))) + (when-let ((spec (build-theme/--face-spec face attrs))) + (push spec specs))))) + (nreverse specs))) + +(defun build-theme/--all-specs (data) + "Build the full ordered face-spec list from parsed theme.json DATA." + (let ((assignments (build-theme/--obj-get data 'assignments)) + (bold (mapcar #'intern (build-theme/--obj-get data 'bold))) + (italic (mapcar #'intern (build-theme/--obj-get data 'italic))) + (ui (build-theme/--obj-get data 'ui)) + (packages (build-theme/--obj-get data 'packages))) + (delq nil + (append + (list (build-theme/--default-spec assignments)) + (build-theme/--syntax-face-specs assignments bold italic) + (build-theme/--ui-face-specs ui) + (build-theme/--package-face-specs packages))))) + +;;; --------------------------------------------------------------------------- +;;; Rendering + +(defun build-theme/--render (name specs) + "Render a deftheme file body for theme NAME from face SPECS, as a string." + (concat + (format ";;; %s-theme.el --- Generated by theme-studio -*- lexical-binding: t -*-\n" name) + "\n;;; Commentary:\n" + (format ";; Generated from %s.json by scripts/theme-studio/build-theme.el.\n" name) + ";; Do not hand-edit; re-run the converter.\n" + "\n;;; Code:\n\n" + (format "(deftheme %s\n \"Generated by theme-studio.\")\n\n" name) + (format "(custom-theme-set-faces\n '%s\n" name) + ;; Each spec is quoted: custom-theme-set-faces is a function, so an + ;; unquoted (face ((t ...))) would be evaluated as a call. Specs hold + ;; only literal strings, symbols, and numbers, so a plain quote suffices. + (mapconcat (lambda (spec) (concat " '" (prin1-to-string spec))) specs "\n") + ")\n\n" + (format "(provide-theme '%s)\n" name) + (format ";;; %s-theme.el ends here\n" name))) + +(defun build-theme/--parse (json-file) + "Parse JSON-FILE into an alist, with null/false as nil and arrays as lists. +Signal a `file-missing' error when JSON-FILE does not exist." + (unless (file-readable-p json-file) + (signal 'file-missing (list "Cannot read theme.json" json-file))) + (with-temp-buffer + (insert-file-contents json-file) + (goto-char (point-min)) + (json-parse-buffer :object-type 'alist :array-type 'list + :null-object nil :false-object nil))) + +;;; --------------------------------------------------------------------------- +;;; Entry point + +(defun build-theme/convert-file (json-file &optional out-dir) + "Convert JSON-FILE (a theme.json export) into a deftheme file. +Write themes/-theme.el, where is the JSON name field, into +OUT-DIR (default: the themes/ directory of this repo). Return the written +path." + (let* ((data (build-theme/--parse json-file)) + (name (build-theme/--obj-get data 'name)) + (specs (build-theme/--all-specs data)) + (dir (or out-dir + (expand-file-name + "../../themes" + (file-name-directory (or load-file-name buffer-file-name + default-directory))))) + (out (expand-file-name (format "%s-theme.el" name) dir))) + (unless (and (stringp name) (string-match-p "\\`[a-zA-Z][a-zA-Z0-9-]*\\'" name)) + (error "Invalid theme name in %s: %S" json-file name)) + (make-directory dir t) + (with-temp-file out + (insert (build-theme/--render name specs))) + out)) + +(provide 'build-theme) +;;; build-theme.el ends here diff --git a/scripts/theme-studio/generate.py b/scripts/theme-studio/generate.py new file mode 100644 index 000000000..405ce150a --- /dev/null +++ b/scripts/theme-studio/generate.py @@ -0,0 +1,1116 @@ +import json, os +HERE=os.path.dirname(os.path.abspath(__file__)) +ns={} +src=open(os.path.join(HERE,'samples.py')).read() +exec(src[:src.index('cols=')], ns) +SAMPLES={"Elisp":ns['ELS'],"Go":ns['GOS'],"Python":ns['PYS'],"TypeScript":ns['TSS'],"Java":ns['JAS'],"C":ns['CS'],"C++":ns['CPS'],"Shell":ns['SHS']} +COLS=ns['COLS'] +MAP={k:v[0] for k,v in COLS.items()}; BOLD={k:v[1] for k,v in COLS.items()}; MAP['str']='#5d9b86'; MAP['bg']='#000000' +PALETTE=[["#67809c","blue"],["#e8bd30","gold"],["#9b5fd0","regal"],["#2ba178","emerald"],["#5d9b86","sage"], + ["#cb6b4d","terracotta"],["#be9e74","tan"],["#ffffff","white"],["#a9b2bb","silver"],["#838d97","steel"], + ["#5e6770","pewter"],["#2f343a","gunmetal"],["#264364","navy"],["#000000","ground"],["#1a1714","bg-dim"]] +CATS=[["bg","background (ground)","Aa Bb 123"],["p","fg · default text","other / whitespace"],["kw","keyword","class def if return"],["bi","builtin","len echo printf"], + ["pp","preprocessor","#include #define"],["fnd","function · def","resolve push"], + ["fnc","function · call","printf rsync get"],["dec","decorator","@dataclass"], + ["ty","type / class","int str Order Queue"],["prop","property / field","id name items"], + ["con","constant","None nil NULL true"],["num","number","8080 100 -1"], + ["str","string",'"dupre" "fmt"'],["esc","escape","\\n \\t"],["re","regexp","/^#[0-9a-f]+/"], + ["doc","docstring",'"""..."""'],["cm","comment","# reject nil"],["cmd","comment delim","# // ;;"], + ["var","variable / use","value key self"],["op","operator",": = -> =="], + ["punc","punctuation","{ } ( ) ;"]] +UI_FACES=[["cursor","cursor","Aa|"],["region","region (selection)","selected text"], + ["hl-line","hl-line (current line)","current line"],["highlight","highlight","hover"], + ["mode-line","mode-line","status active"],["mode-line-inactive","mode-line-inactive","status idle"], + ["fringe","fringe","| |"],["line-number","line-number"," 42"], + ["line-number-current-line","line-number-current-line","> 42"],["minibuffer-prompt","minibuffer-prompt","M-x "], + ["isearch","isearch (match)","match"],["lazy-highlight","lazy-highlight","other match"], + ["isearch-fail","isearch-fail","no match"],["show-paren-match","show-paren-match","( )"], + ["show-paren-mismatch","show-paren-mismatch",") ("],["link","link","https://"], + ["error","error","error!"],["warning","warning","warning"], + ["success","success","ok"],["vertical-border","vertical-border","|"]] +UIMAP={"cursor":{"fg":None,"bg":"#a9b2bb"},"region":{"fg":None,"bg":"#264364"}, + "hl-line":{"fg":None,"bg":"#1a1714"},"highlight":{"fg":None,"bg":"#2f343a"}, + "mode-line":{"fg":"#cdced1","bg":"#2f343a"},"mode-line-inactive":{"fg":"#838d97","bg":"#1a1714"}, + "fringe":{"fg":None,"bg":"#0d0b0a"},"line-number":{"fg":"#5e6770","bg":None}, + "line-number-current-line":{"fg":"#e8bd30","bg":"#1a1714"},"minibuffer-prompt":{"fg":"#67809c","bg":None}, + "isearch":{"fg":"#0d0b0a","bg":"#e8bd30"},"lazy-highlight":{"fg":"#0d0b0a","bg":"#838d97"}, + "isearch-fail":{"fg":"#cb6b4d","bg":None},"show-paren-match":{"fg":None,"bg":"#264364"}, + "show-paren-mismatch":{"fg":"#0d0b0a","bg":"#cb6b4d"},"link":{"fg":"#67809c","bg":None}, + "error":{"fg":"#cb6b4d","bg":None},"warning":{"fg":"#e8bd30","bg":None}, + "success":{"fg":"#5d9b86","bg":None},"vertical-border":{"fg":"#2f343a","bg":None}} +# link is underlined by default (matches the built-in link face). +UIMAP["link"]["underline"]=True +# Tier-3 package faces (Phase 2): complete own-defface sets for org/magit/elfeed, +# built from face-name lists + a curated seed-color map. Prominent faces are +# seeded; the long tail seeds to the default foreground for the user to tune. +ORG_FACES=("org-document-title org-document-info org-document-info-keyword " + "org-level-1 org-level-2 org-level-3 org-level-4 org-level-5 org-level-6 org-level-7 org-level-8 " + "org-headline-todo org-headline-done 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 org-link org-footnote org-date org-sexp-date " + "org-date-selected org-target org-macro org-cite org-cite-key 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 org-table org-table-header org-table-row org-formula org-column " + "org-column-title org-list-dt org-meta-line org-ellipsis org-hide org-indent org-archived " + "org-default org-dispatcher-highlight 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 " + "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 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").split() +MAGIT_FACES=("magit-section-heading magit-section-secondary-heading magit-section-heading-selection " + "magit-section-highlight magit-section-child-count magit-diff-added magit-diff-added-highlight " + "magit-diff-removed magit-diff-removed-highlight magit-diff-context magit-diff-context-highlight " + "magit-diff-file-heading magit-diff-file-heading-highlight magit-diff-file-heading-selection " + "magit-diff-hunk-heading magit-diff-hunk-heading-highlight magit-diff-hunk-heading-selection " + "magit-diff-hunk-region magit-diff-lines-heading magit-diff-lines-boundary magit-diff-base " + "magit-diff-base-highlight magit-diff-our magit-diff-our-highlight magit-diff-their " + "magit-diff-their-highlight magit-diff-conflict-heading magit-diff-conflict-heading-highlight " + "magit-diff-revision-summary magit-diff-revision-summary-highlight magit-diff-whitespace-warning " + "magit-diffstat-added magit-diffstat-removed magit-branch-current magit-branch-local " + "magit-branch-remote magit-branch-remote-head magit-branch-upstream magit-branch-warning " + "magit-head magit-tag magit-hash magit-filename magit-dimmed magit-keyword magit-keyword-squash " + "magit-refname magit-refname-stash magit-refname-wip magit-refname-pullreq magit-log-author " + "magit-log-date magit-log-graph magit-header-line magit-header-line-key magit-header-line-log-select " + "magit-process-ok magit-process-ng magit-mode-line-process magit-mode-line-process-error " + "magit-bisect-good magit-bisect-bad magit-bisect-skip magit-blame-heading magit-blame-highlight " + "magit-blame-hash magit-blame-name magit-blame-date magit-blame-summary magit-blame-dimmed " + "magit-blame-margin magit-cherry-equivalent magit-cherry-unmatched magit-signature-good " + "magit-signature-bad magit-signature-untrusted magit-signature-expired magit-signature-expired-key " + "magit-signature-revoked magit-signature-error magit-reflog-commit magit-reflog-amend " + "magit-reflog-merge magit-reflog-checkout magit-reflog-reset magit-reflog-rebase " + "magit-reflog-cherry-pick magit-reflog-remote magit-reflog-other magit-sequence-pick " + "magit-sequence-stop magit-sequence-part magit-sequence-head magit-sequence-drop magit-sequence-done " + "magit-sequence-onto magit-sequence-exec magit-left-margin").split() +ELFEED_FACES=("elfeed-search-date-face elfeed-search-title-face elfeed-search-unread-title-face " + "elfeed-search-feed-face elfeed-search-tag-face elfeed-search-unread-count-face " + "elfeed-search-filter-face elfeed-search-last-update-face elfeed-log-date-face " + "elfeed-log-error-level-face elfeed-log-warn-level-face elfeed-log-info-level-face " + "elfeed-log-debug-level-face").split() +ORG_SEED={ + "org-document-title":{"fg":"gold","bold":True,"height":1.5},"org-document-info":{"fg":"steel"}, + "org-document-info-keyword":{"fg":"pewter","inherit":"fixed-pitch"}, + "org-level-1":{"fg":"blue","bold":True,"height":1.3},"org-level-2":{"fg":"gold","height":1.2}, + "org-level-3":{"fg":"regal","height":1.15},"org-level-4":{"fg":"emerald","height":1.1}, + "org-level-5":{"fg":"terracotta"},"org-level-6":{"fg":"tan"},"org-level-7":{"fg":"sage"},"org-level-8":{"fg":"steel"}, + "org-headline-done":{"fg":"pewter"},"org-todo":{"fg":"terracotta","bold":True},"org-done":{"fg":"sage","bold":True}, + "org-priority":{"fg":"gold","bold":True},"org-tag":{"fg":"tan"},"org-tag-group":{"fg":"tan"}, + "org-special-keyword":{"fg":"pewter"},"org-drawer":{"fg":"pewter"},"org-property-value":{"fg":"steel"}, + "org-checkbox":{"fg":"gold","inherit":"fixed-pitch"},"org-checkbox-statistics-todo":{"fg":"terracotta"}, + "org-checkbox-statistics-done":{"fg":"sage"},"org-warning":{"fg":"terracotta","bold":True}, + "org-link":{"fg":"blue"},"org-footnote":{"fg":"blue"},"org-date":{"fg":"steel","inherit":"fixed-pitch"}, + "org-sexp-date":{"fg":"steel"},"org-date-selected":{"fg":"ground","bg":"gold"},"org-target":{"fg":"regal"}, + "org-macro":{"fg":"regal"},"org-cite":{"fg":"blue"},"org-cite-key":{"fg":"blue"}, + "org-block":{"fg":"white","bg":"bg-dim","inherit":"fixed-pitch"}, + "org-block-begin-line":{"fg":"pewter","bg":"bg-dim","inherit":"fixed-pitch"}, + "org-block-end-line":{"fg":"pewter","bg":"bg-dim","inherit":"fixed-pitch"}, + "org-code":{"fg":"terracotta","inherit":"fixed-pitch"},"org-verbatim":{"fg":"steel","inherit":"fixed-pitch"}, + "org-inline-src-block":{"fg":"terracotta","inherit":"fixed-pitch"},"org-quote":{"fg":"silver","italic":True}, + "org-verse":{"fg":"silver","italic":True},"org-latex-and-related":{"fg":"gold"}, + "org-table":{"fg":"steel","inherit":"fixed-pitch"},"org-table-header":{"fg":"white","bold":True,"bg":"gunmetal"}, + "org-formula":{"fg":"terracotta"},"org-column":{"bg":"gunmetal"},"org-column-title":{"fg":"white","bold":True,"bg":"gunmetal"}, + "org-list-dt":{"fg":"gold","bold":True},"org-meta-line":{"fg":"pewter","inherit":"fixed-pitch"}, + "org-ellipsis":{"fg":"pewter"},"org-hide":{"fg":"ground"},"org-indent":{"fg":"ground"}, + "org-archived":{"fg":"pewter"},"org-dispatcher-highlight":{"fg":"gold","bold":True,"bg":"navy"}, + "org-agenda-structure":{"fg":"blue","bold":True,"height":1.1},"org-agenda-structure-secondary":{"fg":"blue"}, + "org-agenda-structure-filter":{"fg":"terracotta","bold":True},"org-agenda-date":{"fg":"steel","height":1.05}, + "org-agenda-date-today":{"fg":"gold","bold":True,"height":1.05},"org-agenda-date-weekend":{"fg":"steel","bold":True}, + "org-agenda-date-weekend-today":{"fg":"gold","bold":True},"org-agenda-current-time":{"fg":"gold"}, + "org-agenda-done":{"fg":"sage"},"org-agenda-dimmed-todo-face":{"fg":"pewter"}, + "org-agenda-calendar-event":{"fg":"white"},"org-agenda-calendar-sexp":{"fg":"steel"}, + "org-agenda-calendar-daterange":{"fg":"steel"},"org-agenda-diary":{"fg":"sage"}, + "org-agenda-clocking":{"bg":"navy"},"org-agenda-column-dateline":{"bg":"gunmetal"}, + "org-agenda-restriction-lock":{"bg":"terracotta"},"org-agenda-filter-category":{"fg":"gold","bold":True}, + "org-agenda-filter-effort":{"fg":"gold","bold":True},"org-agenda-filter-regexp":{"fg":"gold","bold":True}, + "org-agenda-filter-tags":{"fg":"gold","bold":True},"org-scheduled":{"fg":"sage"}, + "org-scheduled-today":{"fg":"sage","bold":True},"org-scheduled-previously":{"fg":"terracotta"}, + "org-upcoming-deadline":{"fg":"gold"},"org-upcoming-distant-deadline":{"fg":"tan"}, + "org-imminent-deadline":{"fg":"terracotta","bold":True},"org-time-grid":{"fg":"tan"}, + "org-clock-overlay":{"bg":"navy"},"org-mode-line-clock":{"fg":"steel"},"org-mode-line-clock-overrun":{"fg":"terracotta","bold":True}} +MAGIT_SEED={ + "magit-section-heading":{"fg":"gold","bold":True},"magit-section-secondary-heading":{"fg":"tan","bold":True}, + "magit-section-heading-selection":{"fg":"gold","bg":"navy"},"magit-section-highlight":{"bg":"bg-dim"}, + "magit-section-child-count":{"fg":"pewter"},"magit-diff-added":{"fg":"sage"}, + "magit-diff-added-highlight":{"fg":"sage","bg":"bg-dim"},"magit-diff-removed":{"fg":"terracotta"}, + "magit-diff-removed-highlight":{"fg":"terracotta","bg":"bg-dim"},"magit-diff-context":{"fg":"pewter"}, + "magit-diff-context-highlight":{"fg":"silver","bg":"bg-dim"},"magit-diff-file-heading":{"fg":"white","bold":True}, + "magit-diff-file-heading-highlight":{"fg":"white","bold":True,"bg":"bg-dim"}, + "magit-diff-hunk-heading":{"fg":"steel","bg":"gunmetal"},"magit-diff-hunk-heading-highlight":{"fg":"white","bg":"gunmetal"}, + "magit-diffstat-added":{"fg":"sage"},"magit-diffstat-removed":{"fg":"terracotta"}, + "magit-branch-current":{"fg":"blue","bold":True},"magit-branch-local":{"fg":"blue"}, + "magit-branch-remote":{"fg":"sage"},"magit-branch-remote-head":{"fg":"sage","bold":True}, + "magit-head":{"fg":"blue","bold":True},"magit-tag":{"fg":"gold"},"magit-hash":{"fg":"pewter"}, + "magit-filename":{"fg":"steel"},"magit-dimmed":{"fg":"pewter"},"magit-keyword":{"fg":"regal"}, + "magit-keyword-squash":{"fg":"terracotta"},"magit-refname":{"fg":"pewter"},"magit-log-author":{"fg":"tan"}, + "magit-log-date":{"fg":"steel"},"magit-log-graph":{"fg":"pewter"}, + "magit-header-line":{"fg":"white","bold":True,"bg":"gunmetal"},"magit-process-ok":{"fg":"sage","bold":True}, + "magit-process-ng":{"fg":"terracotta","bold":True},"magit-mode-line-process":{"fg":"sage"}, + "magit-mode-line-process-error":{"fg":"terracotta"},"magit-bisect-good":{"fg":"sage"}, + "magit-bisect-bad":{"fg":"terracotta"},"magit-bisect-skip":{"fg":"gold"}, + "magit-blame-heading":{"fg":"steel","bg":"gunmetal"},"magit-blame-hash":{"fg":"pewter"}, + "magit-blame-name":{"fg":"tan"},"magit-blame-date":{"fg":"steel"},"magit-cherry-equivalent":{"fg":"regal"}, + "magit-cherry-unmatched":{"fg":"sage"},"magit-signature-good":{"fg":"sage"}, + "magit-signature-bad":{"fg":"terracotta","bold":True},"magit-signature-untrusted":{"fg":"gold"}, + "magit-signature-expired":{"fg":"tan"},"magit-diff-whitespace-warning":{"bg":"terracotta"}, + "magit-reflog-commit":{"fg":"sage"},"magit-reflog-amend":{"fg":"regal"},"magit-reflog-merge":{"fg":"sage"}, + "magit-reflog-checkout":{"fg":"blue"},"magit-reflog-reset":{"fg":"terracotta"},"magit-reflog-rebase":{"fg":"regal"}, + "magit-reflog-cherry-pick":{"fg":"sage"},"magit-reflog-remote":{"fg":"steel"},"magit-reflog-other":{"fg":"steel"}, + "magit-sequence-pick":{"fg":"white"},"magit-sequence-stop":{"fg":"terracotta"},"magit-sequence-done":{"fg":"pewter"}, + "magit-sequence-head":{"fg":"blue"}} +ELFEED_SEED={ + "elfeed-search-date-face":{"fg":"steel"},"elfeed-search-title-face":{"fg":"silver"}, + "elfeed-search-unread-title-face":{"fg":"white","bold":True},"elfeed-search-feed-face":{"fg":"sage"}, + "elfeed-search-tag-face":{"fg":"tan"},"elfeed-search-unread-count-face":{"fg":"gold"}, + "elfeed-search-filter-face":{"fg":"blue","bold":True},"elfeed-search-last-update-face":{"fg":"pewter"}, + "elfeed-log-date-face":{"fg":"steel"},"elfeed-log-error-level-face":{"fg":"terracotta","bold":True}, + "elfeed-log-warn-level-face":{"fg":"gold"},"elfeed-log-info-level-face":{"fg":"sage"}, + "elfeed-log-debug-level-face":{"fg":"pewter"}} +# ghostel (terminal): the 16 ANSI colors plus default and the fake cursor. +GHOSTEL_FACES=("ghostel-default ghostel-fake-cursor ghostel-fake-cursor-box " + "ghostel-color-black ghostel-color-red ghostel-color-green ghostel-color-yellow " + "ghostel-color-blue ghostel-color-magenta ghostel-color-cyan ghostel-color-white " + "ghostel-color-bright-black ghostel-color-bright-red ghostel-color-bright-green ghostel-color-bright-yellow " + "ghostel-color-bright-blue ghostel-color-bright-magenta ghostel-color-bright-cyan ghostel-color-bright-white").split() +GHOSTEL_SEED={ + "ghostel-default":{"fg":"#cdced1"},"ghostel-fake-cursor":{"fg":"#000000","bg":"silver"},"ghostel-fake-cursor-box":{"fg":"silver"}, + "ghostel-color-black":{"fg":"pewter"},"ghostel-color-red":{"fg":"terracotta"},"ghostel-color-green":{"fg":"emerald"},"ghostel-color-yellow":{"fg":"gold"}, + "ghostel-color-blue":{"fg":"blue"},"ghostel-color-magenta":{"fg":"regal"},"ghostel-color-cyan":{"fg":"sage"},"ghostel-color-white":{"fg":"silver"}, + "ghostel-color-bright-black":{"fg":"steel"},"ghostel-color-bright-red":{"fg":"#de4949"},"ghostel-color-bright-green":{"fg":"#84b068"},"ghostel-color-bright-yellow":{"fg":"#eed376"}, + "ghostel-color-bright-blue":{"fg":"#7a9abe"},"ghostel-color-bright-magenta":{"fg":"#b07fd0"},"ghostel-color-bright-cyan":{"fg":"#7fc0a8"},"ghostel-color-bright-white":{"fg":"white"}} +DASHBOARD_FACES=("dashboard-banner-logo-title dashboard-text-banner dashboard-heading " + "dashboard-items-face dashboard-navigator dashboard-no-items-face dashboard-footer-face dashboard-footer-icon-face").split() +DASHBOARD_SEED={ + "dashboard-banner-logo-title":{"fg":"gold","bold":True},"dashboard-text-banner":{"fg":"steel"},"dashboard-heading":{"fg":"blue","bold":True}, + "dashboard-items-face":{"fg":"#cdced1"},"dashboard-navigator":{"fg":"blue"},"dashboard-no-items-face":{"fg":"pewter"}, + "dashboard-footer-face":{"fg":"tan"},"dashboard-footer-icon-face":{"fg":"gold"}} +# mu4e is not in the generated inventory (not loaded when it was built), so its +# face list is curated from the set the dupre theme already themes. +MU4E_FACES=("mu4e-title-face mu4e-context-face mu4e-modeline-face mu4e-ok-face mu4e-warning-face " + "mu4e-header-title-face mu4e-header-key-face mu4e-header-value-face mu4e-header-face mu4e-header-highlight-face mu4e-header-marks-face " + "mu4e-unread-face mu4e-flagged-face mu4e-replied-face mu4e-forwarded-face mu4e-draft-face mu4e-trashed-face mu4e-moved-face mu4e-related-face " + "mu4e-contact-face mu4e-special-header-value-face mu4e-attach-number-face mu4e-url-number-face mu4e-link-face " + "mu4e-cited-1-face mu4e-cited-2-face mu4e-cited-3-face mu4e-cited-4-face mu4e-cited-5-face mu4e-cited-6-face mu4e-cited-7-face " + "mu4e-footer-face mu4e-region-code mu4e-system-face mu4e-highlight-face mu4e-compose-header-face mu4e-compose-separator-face").split() +MU4E_SEED={ + "mu4e-title-face":{"fg":"blue","bold":True},"mu4e-context-face":{"fg":"blue","bold":True},"mu4e-modeline-face":{"fg":"silver"},"mu4e-ok-face":{"fg":"sage","bold":True},"mu4e-warning-face":{"fg":"gold","bold":True}, + "mu4e-header-title-face":{"fg":"blue","bold":True},"mu4e-header-key-face":{"fg":"blue","bold":True},"mu4e-header-value-face":{"fg":"silver"},"mu4e-header-face":{"fg":"#cdced1"},"mu4e-header-highlight-face":{"bg":"gunmetal"},"mu4e-header-marks-face":{"fg":"gold"}, + "mu4e-unread-face":{"fg":"white","bold":True},"mu4e-flagged-face":{"fg":"gold"},"mu4e-replied-face":{"fg":"silver"},"mu4e-forwarded-face":{"fg":"silver"},"mu4e-draft-face":{"fg":"steel","italic":True},"mu4e-trashed-face":{"fg":"pewter","strike":True},"mu4e-moved-face":{"fg":"steel","italic":True},"mu4e-related-face":{"fg":"steel","italic":True}, + "mu4e-contact-face":{"fg":"#cdced1"},"mu4e-special-header-value-face":{"fg":"silver"},"mu4e-attach-number-face":{"fg":"blue","bold":True},"mu4e-url-number-face":{"fg":"blue","bold":True},"mu4e-link-face":{"fg":"blue","underline":True}, + "mu4e-cited-1-face":{"fg":"silver"},"mu4e-cited-2-face":{"fg":"steel"},"mu4e-cited-3-face":{"fg":"sage"},"mu4e-cited-4-face":{"fg":"pewter"},"mu4e-cited-5-face":{"fg":"tan"},"mu4e-cited-6-face":{"fg":"terracotta"},"mu4e-cited-7-face":{"fg":"regal"}, + "mu4e-footer-face":{"fg":"pewter"},"mu4e-region-code":{"bg":"bg-dim"},"mu4e-system-face":{"fg":"pewter","italic":True},"mu4e-highlight-face":{"fg":"gold","bold":True},"mu4e-compose-header-face":{"fg":"blue","bold":True},"mu4e-compose-separator-face":{"fg":"pewter"}} +LSP_FACES=("lsp-signature-face lsp-signature-highlight-function-argument lsp-signature-posframe " + "lsp-face-highlight-read lsp-face-highlight-write lsp-face-highlight-textual lsp-face-rename lsp-rename-placeholder-face " + "lsp-inlay-hint-face lsp-inlay-hint-parameter-face lsp-inlay-hint-type-face lsp-details-face " + "lsp-installation-buffer-face lsp-installation-finished-buffer-face").split() +LSP_SEED={ + "lsp-signature-face":{"fg":"silver"},"lsp-signature-highlight-function-argument":{"fg":"gold","bold":True},"lsp-signature-posframe":{"bg":"bg-dim"}, + "lsp-face-highlight-read":{"bg":"navy"},"lsp-face-highlight-write":{"bg":"#3d2f4a"},"lsp-face-highlight-textual":{"bg":"gunmetal"}, + "lsp-face-rename":{"bg":"gunmetal","bold":True},"lsp-rename-placeholder-face":{"fg":"gold","bold":True}, + "lsp-inlay-hint-face":{"fg":"pewter","italic":True},"lsp-inlay-hint-parameter-face":{"fg":"steel","italic":True},"lsp-inlay-hint-type-face":{"fg":"sage","italic":True}, + "lsp-details-face":{"fg":"pewter","italic":True},"lsp-installation-buffer-face":{"fg":"blue"},"lsp-installation-finished-buffer-face":{"fg":"sage"}} +GITGUTTER_FACES=("git-gutter:added git-gutter:modified git-gutter:deleted git-gutter:unchanged git-gutter:separator").split() +GITGUTTER_SEED={ + "git-gutter:added":{"fg":"emerald"},"git-gutter:modified":{"fg":"gold"},"git-gutter:deleted":{"fg":"terracotta"}, + "git-gutter:unchanged":{"fg":"pewter"},"git-gutter:separator":{"fg":"steel"}} +FLYCHECK_FACES=("flycheck-error flycheck-warning flycheck-info flycheck-fringe-error flycheck-fringe-warning flycheck-fringe-info " + "flycheck-delimited-error flycheck-error-delimiter flycheck-error-list-error flycheck-error-list-warning flycheck-error-list-info " + "flycheck-error-list-error-message flycheck-error-list-checker-name flycheck-error-list-column-number flycheck-error-list-line-number " + "flycheck-error-list-filename flycheck-error-list-id flycheck-error-list-id-with-explainer flycheck-error-list-highlight flycheck-verify-select-checker").split() +FLYCHECK_SEED={ + "flycheck-error":{"fg":"terracotta"},"flycheck-warning":{"fg":"gold"},"flycheck-info":{"fg":"blue"}, + "flycheck-fringe-error":{"fg":"terracotta"},"flycheck-fringe-warning":{"fg":"gold"},"flycheck-fringe-info":{"fg":"blue"}, + "flycheck-delimited-error":{"fg":"terracotta"},"flycheck-error-delimiter":{"fg":"terracotta"}, + "flycheck-error-list-error":{"fg":"terracotta"},"flycheck-error-list-warning":{"fg":"gold"},"flycheck-error-list-info":{"fg":"blue"}, + "flycheck-error-list-error-message":{"fg":"#cdced1"},"flycheck-error-list-checker-name":{"fg":"steel"}, + "flycheck-error-list-column-number":{"fg":"pewter"},"flycheck-error-list-line-number":{"fg":"pewter"},"flycheck-error-list-filename":{"fg":"blue"}, + "flycheck-error-list-id":{"fg":"steel"},"flycheck-error-list-id-with-explainer":{"fg":"steel","bold":True}, + "flycheck-error-list-highlight":{"bg":"gunmetal"},"flycheck-verify-select-checker":{"fg":"gold"}} +DIRED_FACES=("dired-header dired-directory dired-symlink dired-broken-symlink dired-special dired-set-id " + "dired-perm-write dired-mark dired-marked dired-flagged dired-ignored dired-warning").split() +DIRED_SEED={ + "dired-header":{"fg":"blue","bold":True},"dired-directory":{"fg":"blue","bold":True},"dired-symlink":{"fg":"sage"}, + "dired-broken-symlink":{"fg":"#de4949","bold":True},"dired-special":{"fg":"regal"},"dired-set-id":{"fg":"terracotta"}, + "dired-perm-write":{"fg":"silver"},"dired-mark":{"fg":"gold"},"dired-marked":{"fg":"gold","bold":True}, + "dired-flagged":{"fg":"terracotta","bold":True},"dired-ignored":{"fg":"pewter"},"dired-warning":{"fg":"gold","bold":True}} +DIRVISH_FACES=("dirvish-inactive dirvish-free-space dirvish-hl-line dirvish-hl-line-inactive " + "dirvish-file-modes dirvish-file-link-number dirvish-file-user-id dirvish-file-group-id dirvish-file-size dirvish-file-time " + "dirvish-file-inode-number dirvish-file-device-number dirvish-subtree-guide dirvish-subtree-state " + "dirvish-collapse-dir-face dirvish-collapse-empty-dir-face dirvish-collapse-file-face dirvish-emerge-group-title " + "dirvish-media-info-heading dirvish-media-info-property-key dirvish-narrow-match-face-0 dirvish-narrow-match-face-1 " + "dirvish-narrow-match-face-2 dirvish-narrow-match-face-3 dirvish-narrow-split dirvish-proc-running dirvish-proc-finished " + "dirvish-proc-failed dirvish-git-commit-message-face dirvish-vc-added-state dirvish-vc-edited-state dirvish-vc-removed-state " + "dirvish-vc-conflict-state dirvish-vc-locked-state dirvish-vc-missing-state dirvish-vc-needs-merge-face " + "dirvish-vc-needs-update-state dirvish-vc-unregistered-face").split() +DIRVISH_SEED={ + "dirvish-inactive":{"fg":"pewter"},"dirvish-free-space":{"fg":"sage"},"dirvish-hl-line":{"bg":"gunmetal"},"dirvish-hl-line-inactive":{"bg":"bg-dim"}, + "dirvish-file-modes":{"fg":"steel"},"dirvish-file-link-number":{"fg":"pewter"},"dirvish-file-user-id":{"fg":"blue"},"dirvish-file-group-id":{"fg":"steel"}, + "dirvish-file-size":{"fg":"sage"},"dirvish-file-time":{"fg":"pewter"},"dirvish-file-inode-number":{"fg":"pewter"},"dirvish-file-device-number":{"fg":"pewter"}, + "dirvish-subtree-guide":{"fg":"pewter"},"dirvish-subtree-state":{"fg":"steel"},"dirvish-collapse-dir-face":{"fg":"blue"}, + "dirvish-collapse-empty-dir-face":{"fg":"pewter"},"dirvish-collapse-file-face":{"fg":"silver"},"dirvish-emerge-group-title":{"fg":"gold","bold":True}, + "dirvish-media-info-heading":{"fg":"blue","bold":True},"dirvish-media-info-property-key":{"fg":"steel"}, + "dirvish-narrow-match-face-0":{"fg":"gold","bold":True},"dirvish-narrow-match-face-1":{"fg":"blue","bold":True}, + "dirvish-narrow-match-face-2":{"fg":"emerald","bold":True},"dirvish-narrow-match-face-3":{"fg":"regal","bold":True},"dirvish-narrow-split":{"fg":"pewter"}, + "dirvish-proc-running":{"fg":"gold"},"dirvish-proc-finished":{"fg":"sage"},"dirvish-proc-failed":{"fg":"terracotta"}, + "dirvish-git-commit-message-face":{"fg":"tan","italic":True},"dirvish-vc-added-state":{"fg":"sage"},"dirvish-vc-edited-state":{"fg":"gold"}, + "dirvish-vc-removed-state":{"fg":"terracotta"},"dirvish-vc-conflict-state":{"fg":"terracotta","bold":True},"dirvish-vc-locked-state":{"fg":"blue"}, + "dirvish-vc-missing-state":{"fg":"terracotta"},"dirvish-vc-needs-merge-face":{"fg":"gold"},"dirvish-vc-needs-update-state":{"fg":"gold"}, + "dirvish-vc-unregistered-face":{"fg":"pewter"}} +ORGDRILL_FACES=("org-drill-hidden-cloze-face org-drill-visible-cloze-face org-drill-visible-cloze-hint-face").split() +ORGDRILL_SEED={"org-drill-hidden-cloze-face":{"fg":"#000000","bg":"steel"},"org-drill-visible-cloze-face":{"fg":"gold","bold":True},"org-drill-visible-cloze-hint-face":{"fg":"pewter","italic":True}} +ORGNOTER_FACES=("org-noter-notes-exist-face org-noter-no-notes-exist-face").split() +ORGNOTER_SEED={"org-noter-notes-exist-face":{"fg":"sage"},"org-noter-no-notes-exist-face":{"fg":"pewter"}} +SIGNEL_FACES=("signel-timestamp-face signel-my-msg-face signel-other-msg-face signel-error-face").split() +SIGNEL_SEED={"signel-timestamp-face":{"fg":"pewter"},"signel-my-msg-face":{"fg":"blue"},"signel-other-msg-face":{"fg":"silver"},"signel-error-face":{"fg":"terracotta","bold":True}} +PEARL_FACES=("pearl-preamble-summary pearl-editable-comment pearl-readonly-comment pearl-modified-highlight pearl-modified-local pearl-modified-unknown").split() +PEARL_SEED={"pearl-preamble-summary":{"fg":"blue","bold":True},"pearl-editable-comment":{"fg":"silver"},"pearl-readonly-comment":{"fg":"pewter","italic":True},"pearl-modified-highlight":{"bg":"navy"},"pearl-modified-local":{"fg":"gold"},"pearl-modified-unknown":{"fg":"pewter"}} +CALIBREDB_FACES=("calibredb-search-header-library-name-face calibredb-search-header-library-path-face calibredb-search-header-total-face calibredb-search-header-filter-face calibredb-search-header-sort-face calibredb-search-header-highlight-face " + "calibredb-id-face calibredb-title-face calibredb-author-face calibredb-format-face calibredb-size-face calibredb-tag-face calibredb-date-face calibredb-mark-face calibredb-series-face calibredb-publisher-face calibredb-pubdate-face " + "calibredb-language-face calibredb-comment-face calibredb-archive-face calibredb-favorite-face calibredb-file-face calibredb-ids-face calibredb-highlight-face calibredb-current-page-button-face calibredb-mouse-face " + "calibredb-title-detailed-view-face calibredb-edit-annotation-header-title-face").split() +CALIBREDB_SEED={ + "calibredb-search-header-library-name-face":{"fg":"blue","bold":True},"calibredb-search-header-library-path-face":{"fg":"pewter"},"calibredb-search-header-total-face":{"fg":"sage"},"calibredb-search-header-filter-face":{"fg":"gold"},"calibredb-search-header-sort-face":{"fg":"steel"},"calibredb-search-header-highlight-face":{"fg":"gold","bold":True}, + "calibredb-id-face":{"fg":"pewter"},"calibredb-title-face":{"fg":"blue","bold":True},"calibredb-author-face":{"fg":"sage"},"calibredb-format-face":{"fg":"steel"},"calibredb-size-face":{"fg":"pewter"},"calibredb-tag-face":{"fg":"tan"},"calibredb-date-face":{"fg":"pewter"},"calibredb-mark-face":{"fg":"gold","bold":True},"calibredb-series-face":{"fg":"regal"},"calibredb-publisher-face":{"fg":"steel"},"calibredb-pubdate-face":{"fg":"pewter"}, + "calibredb-language-face":{"fg":"steel"},"calibredb-comment-face":{"fg":"silver","italic":True},"calibredb-archive-face":{"fg":"pewter"},"calibredb-favorite-face":{"fg":"gold"},"calibredb-file-face":{"fg":"blue"},"calibredb-ids-face":{"fg":"pewter"},"calibredb-highlight-face":{"fg":"gold","bold":True},"calibredb-current-page-button-face":{"fg":"blue","bold":True},"calibredb-mouse-face":{"bg":"gunmetal"}, + "calibredb-title-detailed-view-face":{"fg":"gold","bold":True},"calibredb-edit-annotation-header-title-face":{"fg":"blue","bold":True}} +ERC_FACES=("erc-header-line erc-timestamp-face erc-notice-face erc-default-face erc-current-nick-face erc-my-nick-face erc-my-nick-prefix-face erc-nick-default-face erc-nick-prefix-face erc-button-nick-default-face " + "erc-nick-msg-face erc-direct-msg-face erc-action-face erc-keyword-face erc-pal-face erc-fool-face erc-dangerous-host-face erc-error-face erc-input-face erc-prompt-face erc-command-indicator-face erc-information " + "erc-button erc-bold-face erc-italic-face erc-underline-face erc-inverse-face erc-spoiler-face erc-fill-wrap-merge-indicator-face erc-keep-place-indicator-arrow erc-keep-place-indicator-line").split() +ERC_SEED={ + "erc-header-line":{"fg":"white","bg":"gunmetal","bold":True},"erc-timestamp-face":{"fg":"pewter"},"erc-notice-face":{"fg":"steel"},"erc-default-face":{"fg":"#cdced1"},"erc-current-nick-face":{"fg":"gold","bold":True},"erc-my-nick-face":{"fg":"gold","bold":True},"erc-my-nick-prefix-face":{"fg":"gold"},"erc-nick-default-face":{"fg":"blue"},"erc-nick-prefix-face":{"fg":"sage"},"erc-button-nick-default-face":{"fg":"blue"}, + "erc-nick-msg-face":{"fg":"regal"},"erc-direct-msg-face":{"fg":"regal"},"erc-action-face":{"fg":"sage","italic":True},"erc-keyword-face":{"fg":"gold","bold":True},"erc-pal-face":{"fg":"emerald"},"erc-fool-face":{"fg":"pewter"},"erc-dangerous-host-face":{"fg":"terracotta","bold":True},"erc-error-face":{"fg":"terracotta","bold":True},"erc-input-face":{"fg":"silver"},"erc-prompt-face":{"fg":"blue","bold":True},"erc-command-indicator-face":{"fg":"steel","bold":True},"erc-information":{"fg":"steel"}, + "erc-button":{"fg":"blue"},"erc-bold-face":{"bold":True},"erc-italic-face":{"italic":True},"erc-underline-face":{"fg":"silver","underline":True},"erc-inverse-face":{"fg":"#000000","bg":"silver"},"erc-spoiler-face":{"fg":"#000000","bg":"gunmetal"},"erc-fill-wrap-merge-indicator-face":{"fg":"pewter"},"erc-keep-place-indicator-arrow":{"fg":"gold"},"erc-keep-place-indicator-line":{"bg":"bg-dim"}} +SLACK_FACES=("slack-room-info-title-face slack-room-info-title-room-name-face slack-room-info-section-title-face slack-room-info-section-label-face slack-room-unread-face " + "slack-message-output-header slack-message-output-text slack-message-output-reaction slack-message-output-reaction-pressed slack-message-deleted-face slack-new-message-marker-face slack-all-thread-buffer-thread-header-face " + "slack-message-mention-face slack-message-mention-me-face slack-message-mention-keyword-face slack-channel-button-face " + "slack-mrkdwn-bold-face slack-mrkdwn-italic-face slack-mrkdwn-code-face slack-mrkdwn-code-block-face slack-mrkdwn-strike-face slack-mrkdwn-blockquote-face slack-mrkdwn-list-face " + "slack-attachment-header slack-attachment-footer slack-attachment-pad slack-attachment-field-title slack-message-attachment-preview-header-face slack-preview-face slack-block-highlight-source-overlay-face " + "slack-message-action-face slack-message-action-primary-face slack-message-action-danger-face " + "slack-button-block-element-face slack-button-primary-block-element-face slack-button-danger-block-element-face slack-select-block-element-face slack-overflow-block-element-face slack-date-picker-block-element-face " + "slack-dialog-title-face slack-dialog-element-label-face slack-dialog-element-hint-face slack-dialog-element-placeholder-face slack-dialog-element-error-face slack-dialog-submit-button-face slack-dialog-cancel-button-face slack-dialog-select-element-input-face " + "slack-user-active-face slack-user-dnd-face slack-user-profile-header-face slack-user-profile-property-name-face slack-profile-image-face " + "slack-search-result-message-header-face slack-search-result-message-username-face " + "slack-modeline-has-unreads-face slack-modeline-channel-has-unreads-face slack-modeline-thread-has-unreads-face").split() +SLACK_SEED={ + "slack-room-info-title-face":{"fg":"blue","bold":True},"slack-room-info-title-room-name-face":{"fg":"gold","bold":True},"slack-room-info-section-title-face":{"fg":"blue","bold":True},"slack-room-info-section-label-face":{"fg":"steel"},"slack-room-unread-face":{"fg":"white","bold":True}, + "slack-message-output-header":{"fg":"blue","bold":True},"slack-message-output-text":{"fg":"#cdced1"},"slack-message-output-reaction":{"fg":"steel"},"slack-message-output-reaction-pressed":{"fg":"gold","bold":True},"slack-message-deleted-face":{"fg":"pewter","italic":True},"slack-new-message-marker-face":{"fg":"terracotta","bold":True},"slack-all-thread-buffer-thread-header-face":{"fg":"blue","bold":True}, + "slack-message-mention-face":{"fg":"blue"},"slack-message-mention-me-face":{"fg":"gold","bg":"navy","bold":True},"slack-message-mention-keyword-face":{"fg":"gold","bold":True},"slack-channel-button-face":{"fg":"blue"}, + "slack-mrkdwn-bold-face":{"bold":True},"slack-mrkdwn-italic-face":{"italic":True},"slack-mrkdwn-code-face":{"fg":"terracotta"},"slack-mrkdwn-code-block-face":{"fg":"terracotta","bg":"bg-dim"},"slack-mrkdwn-strike-face":{"fg":"pewter","strike":True},"slack-mrkdwn-blockquote-face":{"fg":"silver","italic":True},"slack-mrkdwn-list-face":{"fg":"silver"}, + "slack-attachment-header":{"fg":"blue","bold":True},"slack-attachment-footer":{"fg":"pewter"},"slack-attachment-pad":{"fg":"pewter"},"slack-attachment-field-title":{"fg":"steel","bold":True},"slack-message-attachment-preview-header-face":{"fg":"blue"},"slack-preview-face":{"fg":"silver"},"slack-block-highlight-source-overlay-face":{"bg":"bg-dim"}, + "slack-message-action-face":{"fg":"blue"},"slack-message-action-primary-face":{"fg":"sage"},"slack-message-action-danger-face":{"fg":"terracotta"}, + "slack-button-block-element-face":{"fg":"silver"},"slack-button-primary-block-element-face":{"fg":"sage","bold":True},"slack-button-danger-block-element-face":{"fg":"terracotta","bold":True},"slack-select-block-element-face":{"fg":"blue"},"slack-overflow-block-element-face":{"fg":"steel"},"slack-date-picker-block-element-face":{"fg":"blue"}, + "slack-dialog-title-face":{"fg":"blue","bold":True},"slack-dialog-element-label-face":{"fg":"steel"},"slack-dialog-element-hint-face":{"fg":"pewter","italic":True},"slack-dialog-element-placeholder-face":{"fg":"pewter"},"slack-dialog-element-error-face":{"fg":"terracotta"},"slack-dialog-submit-button-face":{"fg":"sage","bold":True},"slack-dialog-cancel-button-face":{"fg":"silver"},"slack-dialog-select-element-input-face":{"fg":"silver"}, + "slack-user-active-face":{"fg":"sage"},"slack-user-dnd-face":{"fg":"terracotta"},"slack-user-profile-header-face":{"fg":"blue","bold":True},"slack-user-profile-property-name-face":{"fg":"steel"},"slack-profile-image-face":{"fg":"pewter"}, + "slack-search-result-message-header-face":{"fg":"blue"},"slack-search-result-message-username-face":{"fg":"gold","bold":True}, + "slack-modeline-has-unreads-face":{"fg":"gold"},"slack-modeline-channel-has-unreads-face":{"fg":"gold","bold":True},"slack-modeline-thread-has-unreads-face":{"fg":"gold"}} +TELEGA_FACES=("telega-root-heading telega-tracking telega-unread-unmuted-modeline telega-username telega-user-online-status telega-user-non-online-status telega-secret-title telega-contact-birthdays-today " + "telega-muted-count telega-unmuted-count telega-mention-count telega-has-chatbuf-brackets telega-delim-face telega-shadow telega-link telega-blue telega-red " + "telega-msg-heading telega-msg-user-title telega-msg-self-title telega-msg-deleted telega-msg-sponsored telega-msg-inline-reply telega-msg-inline-forward telega-msg-inline-other " + "telega-entity-type-bold telega-entity-type-italic telega-entity-type-underline telega-entity-type-strikethrough telega-entity-type-code telega-entity-type-pre telega-entity-type-blockquote telega-entity-type-mention telega-entity-type-hashtag telega-entity-type-cashtag telega-entity-type-botcommand telega-entity-type-texturl telega-entity-type-spoiler " + "telega-reaction telega-reaction-chosen telega-reaction-paid telega-reaction-paid-chosen telega-highlight-text-face telega-button-highlight " + "telega-chat-prompt telega-chat-prompt-aux telega-chat-input-attachment telega-topic-button telega-filter-active telega-filter-button-active telega-filter-button-inactive telega-checklist-stats-done telega-checklist-stats-todo " + "telega-box-button telega-box-button-active telega-box-button-default-active telega-box-button-default-passive telega-box-button-primary-active telega-box-button-primary-passive telega-box-button-success-active telega-box-button-success-passive telega-box-button-danger-active telega-box-button-danger-passive telega-box-button-ui-active telega-box-button-ui-passive telega-box-button2-active telega-box-button2-passive telega-box-button2-white-foreground " + "telega-describe-item-title telega-describe-section-title telega-describe-subsection-title telega-enckey-00 telega-enckey-01 telega-enckey-10 telega-enckey-11 " + "telega-palette-builtin-blue telega-palette-builtin-green telega-palette-builtin-orange telega-palette-builtin-purple " + "telega-webpage-title telega-webpage-subtitle telega-webpage-header telega-webpage-subheader telega-webpage-outline telega-webpage-fixed telega-webpage-preformatted telega-webpage-marked telega-webpage-strike-through telega-webpage-chat-link telega-link-preview-sitename telega-link-preview-title").split() +TELEGA_SEED={ + "telega-root-heading":{"fg":"blue","bold":True},"telega-tracking":{"fg":"gold"},"telega-unread-unmuted-modeline":{"fg":"gold","bold":True},"telega-username":{"fg":"blue"},"telega-user-online-status":{"fg":"sage"},"telega-user-non-online-status":{"fg":"pewter"},"telega-secret-title":{"fg":"sage"},"telega-contact-birthdays-today":{"fg":"gold"}, + "telega-muted-count":{"fg":"pewter"},"telega-unmuted-count":{"fg":"gold","bold":True},"telega-mention-count":{"fg":"gold","bold":True},"telega-has-chatbuf-brackets":{"fg":"steel"},"telega-delim-face":{"fg":"pewter"},"telega-shadow":{"fg":"pewter"},"telega-link":{"fg":"blue"},"telega-blue":{"fg":"blue"},"telega-red":{"fg":"terracotta"}, + "telega-msg-heading":{"fg":"steel"},"telega-msg-user-title":{"fg":"blue","bold":True},"telega-msg-self-title":{"fg":"gold","bold":True},"telega-msg-deleted":{"fg":"pewter","italic":True},"telega-msg-sponsored":{"fg":"pewter","italic":True},"telega-msg-inline-reply":{"fg":"steel"},"telega-msg-inline-forward":{"fg":"sage"},"telega-msg-inline-other":{"fg":"pewter"}, + "telega-entity-type-bold":{"bold":True},"telega-entity-type-italic":{"italic":True},"telega-entity-type-underline":{"fg":"silver","underline":True},"telega-entity-type-strikethrough":{"fg":"pewter","strike":True},"telega-entity-type-code":{"fg":"terracotta"},"telega-entity-type-pre":{"fg":"terracotta","bg":"bg-dim"},"telega-entity-type-blockquote":{"fg":"silver","italic":True},"telega-entity-type-mention":{"fg":"blue"},"telega-entity-type-hashtag":{"fg":"blue"},"telega-entity-type-cashtag":{"fg":"sage"},"telega-entity-type-botcommand":{"fg":"sage"},"telega-entity-type-texturl":{"fg":"blue"},"telega-entity-type-spoiler":{"fg":"gunmetal","bg":"gunmetal"}, + "telega-reaction":{"fg":"steel"},"telega-reaction-chosen":{"fg":"gold","bold":True},"telega-reaction-paid":{"fg":"gold"},"telega-reaction-paid-chosen":{"fg":"gold","bold":True},"telega-highlight-text-face":{"fg":"#000000","bg":"gold"},"telega-button-highlight":{"fg":"gold","bold":True}, + "telega-chat-prompt":{"fg":"blue","bold":True},"telega-chat-prompt-aux":{"fg":"steel"},"telega-chat-input-attachment":{"fg":"sage"},"telega-topic-button":{"fg":"blue"},"telega-filter-active":{"fg":"gold","bold":True},"telega-filter-button-active":{"fg":"#000000","bg":"gold"},"telega-filter-button-inactive":{"fg":"steel"},"telega-checklist-stats-done":{"fg":"sage"},"telega-checklist-stats-todo":{"fg":"steel"}, + "telega-box-button":{"fg":"blue"},"telega-box-button-active":{"fg":"#000000","bg":"blue"},"telega-box-button-default-active":{"fg":"#000000","bg":"silver"},"telega-box-button-default-passive":{"fg":"steel"},"telega-box-button-primary-active":{"fg":"#000000","bg":"blue"},"telega-box-button-primary-passive":{"fg":"blue"},"telega-box-button-success-active":{"fg":"#000000","bg":"emerald"},"telega-box-button-success-passive":{"fg":"sage"},"telega-box-button-danger-active":{"fg":"#000000","bg":"terracotta"},"telega-box-button-danger-passive":{"fg":"terracotta"},"telega-box-button-ui-active":{"fg":"#000000","bg":"gold"},"telega-box-button-ui-passive":{"fg":"gold"},"telega-box-button2-active":{"fg":"#000000","bg":"blue"},"telega-box-button2-passive":{"fg":"steel"},"telega-box-button2-white-foreground":{"fg":"white"}, + "telega-describe-item-title":{"fg":"steel","bold":True},"telega-describe-section-title":{"fg":"blue","bold":True},"telega-describe-subsection-title":{"fg":"blue"},"telega-enckey-00":{"fg":"pewter"},"telega-enckey-01":{"fg":"sage"},"telega-enckey-10":{"fg":"gold"},"telega-enckey-11":{"fg":"blue"}, + "telega-palette-builtin-blue":{"fg":"blue"},"telega-palette-builtin-green":{"fg":"emerald"},"telega-palette-builtin-orange":{"fg":"terracotta"},"telega-palette-builtin-purple":{"fg":"regal"}, + "telega-webpage-title":{"fg":"blue","bold":True},"telega-webpage-subtitle":{"fg":"steel"},"telega-webpage-header":{"fg":"gold","bold":True},"telega-webpage-subheader":{"fg":"gold"},"telega-webpage-outline":{"fg":"pewter"},"telega-webpage-fixed":{"fg":"terracotta"},"telega-webpage-preformatted":{"fg":"terracotta","bg":"bg-dim"},"telega-webpage-marked":{"fg":"#000000","bg":"gold"},"telega-webpage-strike-through":{"fg":"pewter","strike":True},"telega-webpage-chat-link":{"fg":"blue"},"telega-link-preview-sitename":{"fg":"steel"},"telega-link-preview-title":{"fg":"blue","bold":True}} +# shr is built-in (not in the inventory). It is the HTML renderer behind nov +# (EPUB), eww, elfeed article view, and HTML mail, so theming it themes them all. +SHR_FACES=("shr-h1 shr-h2 shr-h3 shr-h4 shr-h5 shr-h6 shr-text shr-link shr-selected-link " + "shr-code shr-mark shr-strike-through shr-sup shr-abbreviation shr-sliced-image").split() +SHR_SEED={ + "shr-h1":{"fg":"gold","bold":True,"height":1.4},"shr-h2":{"fg":"blue","bold":True,"height":1.2},"shr-h3":{"fg":"blue","bold":True},"shr-h4":{"fg":"silver","bold":True},"shr-h5":{"fg":"steel","bold":True},"shr-h6":{"fg":"pewter","bold":True}, + "shr-text":{"fg":"#cdced1"},"shr-link":{"fg":"blue","underline":True},"shr-selected-link":{"fg":"gold","bold":True,"underline":True},"shr-code":{"fg":"terracotta","bg":"bg-dim"},"shr-mark":{"fg":"#000000","bg":"gold"},"shr-strike-through":{"fg":"pewter","strike":True},"shr-sup":{"fg":"steel"},"shr-abbreviation":{"fg":"steel","italic":True},"shr-sliced-image":{"fg":"pewter"}} +def _faces(names,prefix,seed): + out=[] + for f in names: + lbl=(f[len(prefix):] if f.startswith(prefix) else f).replace("-face","").replace("-"," ") + out.append([f,lbl,seed.get(f,{})]) + return out +APPS={"org-mode":{"label":"org-mode","preview":"org","faces":_faces(ORG_FACES,"org-",ORG_SEED)}, + "magit":{"label":"magit","preview":"magit","faces":_faces(MAGIT_FACES,"magit-",MAGIT_SEED)}, + "elfeed":{"label":"elfeed","preview":"elfeed","faces":_faces(ELFEED_FACES,"elfeed-",ELFEED_SEED)}, + "mu4e":{"label":"mu4e","preview":"mu4e","faces":_faces(MU4E_FACES,"mu4e-",MU4E_SEED)}, + "ghostel":{"label":"ghostel","preview":"ghostel","faces":_faces(GHOSTEL_FACES,"ghostel-",GHOSTEL_SEED)}, + "dashboard":{"label":"dashboard","preview":"dashboard","faces":_faces(DASHBOARD_FACES,"dashboard-",DASHBOARD_SEED)}, + "lsp-mode":{"label":"lsp-mode","preview":"lsp","faces":_faces(LSP_FACES,"lsp-",LSP_SEED)}, + "git-gutter":{"label":"git-gutter","preview":"gitgutter","faces":_faces(GITGUTTER_FACES,"git-gutter:",GITGUTTER_SEED)}, + "flycheck":{"label":"flycheck","preview":"flycheck","faces":_faces(FLYCHECK_FACES,"flycheck-",FLYCHECK_SEED)}, + "dired":{"label":"dired","preview":"dired","faces":_faces(DIRED_FACES,"dired-",DIRED_SEED)}, + "dirvish":{"label":"dirvish","preview":"dirvish","faces":_faces(DIRVISH_FACES,"dirvish-",DIRVISH_SEED)}, + "calibredb":{"label":"calibredb","preview":"calibredb","faces":_faces(CALIBREDB_FACES,"calibredb-",CALIBREDB_SEED)}, + "erc":{"label":"erc","preview":"erc","faces":_faces(ERC_FACES,"erc-",ERC_SEED)}, + "org-drill":{"label":"org-drill","preview":"orgdrill","faces":_faces(ORGDRILL_FACES,"org-drill-",ORGDRILL_SEED)}, + "org-noter":{"label":"org-noter","preview":"orgnoter","faces":_faces(ORGNOTER_FACES,"org-noter-",ORGNOTER_SEED)}, + "signel":{"label":"signel","preview":"signel","faces":_faces(SIGNEL_FACES,"signel-",SIGNEL_SEED)}, + "pearl":{"label":"pearl","preview":"pearl","faces":_faces(PEARL_FACES,"pearl-",PEARL_SEED)}, + "slack":{"label":"slack","preview":"slack","faces":_faces(SLACK_FACES,"slack-",SLACK_SEED)}, + "telega":{"label":"telega","preview":"telega","faces":_faces(TELEGA_FACES,"telega-",TELEGA_SEED)}, + "shr":{"label":"shr (HTML: nov/eww/mail)","preview":"shr","faces":_faces(SHR_FACES,"shr-",SHR_SEED)}} +# Phase 6: merge the generated all-package inventory (refresh with build-inventory.el). +# Bespoke apps stay; every other installed package becomes an editable generic app. +_inv_path=os.path.join(HERE,"package-inventory.json") +if os.path.exists(_inv_path): + _INV=json.load(open(_inv_path)) + _BESPOKE={"magit","elfeed","org","org-mode","mu4e","ghostel","dashboard","lsp-mode","git-gutter","flycheck","dired","dirvish","calibredb","erc","org-drill","org-noter","signel","pearl","slack","telega","shr"} + for _pkg in sorted(_INV): + if _pkg in _BESPOKE or _pkg in APPS: continue + APPS[_pkg]={"label":_pkg,"preview":"generic","faces":[ + [f,(f[len(_pkg)+1:] if f.startswith(_pkg+"-") else f).replace("-face","").replace("-"," "),{}] + for f in _INV[_pkg]]} +HTML = """theme-studio + +

Untitled: theme

+
+
+

palette

+
+
+
+ + + + + +
+
+
+
+
+
#888888
+
limit
+
+
+
+
+
+

export, import, and save

+
+ +
+
+ + + + +
+ +
+
+

code/color assignments

+
+
+
elements △color △styleexamplecontrast
+
+
+
+

+ 
+
+

ui faces

+
+
+
face △foreground △background △stylepreview
+
+
+
+
+
+
+

package faces

+
+ + + +
+
+
+
face △fg △bg △styleinherit △size △contrast △
+
+
+
+
+
+
+""" +HTML=(HTML.replace("SAMPLES_J",json.dumps(SAMPLES)) + .replace("PALETTE_J",json.dumps(PALETTE)).replace("CATS_J",json.dumps(CATS)) + .replace("UIFACES_J",json.dumps(UI_FACES)).replace("UIMAP_J",json.dumps(UIMAP)).replace("APPS_J",json.dumps(APPS)) + .replace("BOLD_J",json.dumps(BOLD)).replace("MAP_J",json.dumps(MAP))) +OUT=os.path.join(HERE,'theme-studio.html') +open(OUT,"w").write(HTML) +print("wrote",OUT) diff --git a/scripts/theme-studio/package-inventory.json b/scripts/theme-studio/package-inventory.json new file mode 100644 index 000000000..18fd7aa25 --- /dev/null +++ b/scripts/theme-studio/package-inventory.json @@ -0,0 +1,723 @@ +{ + "2048-game": [ + "twentyfortyeight-face-1024", + "twentyfortyeight-face-128", + "twentyfortyeight-face-16", + "twentyfortyeight-face-2", + "twentyfortyeight-face-2048", + "twentyfortyeight-face-256", + "twentyfortyeight-face-32", + "twentyfortyeight-face-4", + "twentyfortyeight-face-512", + "twentyfortyeight-face-64", + "twentyfortyeight-face-8" + ], + "alert": [ + "alert-high-face", + "alert-low-face", + "alert-moderate-face", + "alert-normal-face", + "alert-trivial-face", + "alert-urgent-face" + ], + "all-the-icons": [ + "all-the-icons-blue", + "all-the-icons-blue-alt", + "all-the-icons-cyan", + "all-the-icons-cyan-alt", + "all-the-icons-dblue", + "all-the-icons-dcyan", + "all-the-icons-dgreen", + "all-the-icons-dmaroon", + "all-the-icons-dorange", + "all-the-icons-dpink", + "all-the-icons-dpurple", + "all-the-icons-dred", + "all-the-icons-dsilver", + "all-the-icons-dyellow", + "all-the-icons-green", + "all-the-icons-lblue", + "all-the-icons-lcyan", + "all-the-icons-lgreen", + "all-the-icons-lmaroon", + "all-the-icons-lorange", + "all-the-icons-lpink", + "all-the-icons-lpurple", + "all-the-icons-lred", + "all-the-icons-lsilver", + "all-the-icons-lyellow", + "all-the-icons-maroon", + "all-the-icons-orange", + "all-the-icons-pink", + "all-the-icons-purple", + "all-the-icons-purple-alt", + "all-the-icons-red", + "all-the-icons-red-alt", + "all-the-icons-silver", + "all-the-icons-yellow" + ], + "company": [ + "company-echo", + "company-echo-common", + "company-preview", + "company-preview-common", + "company-preview-search", + "company-tooltip", + "company-tooltip-annotation", + "company-tooltip-annotation-selection", + "company-tooltip-common", + "company-tooltip-common-selection", + "company-tooltip-deprecated", + "company-tooltip-mouse", + "company-tooltip-quick-access", + "company-tooltip-quick-access-selection", + "company-tooltip-scrollbar-thumb", + "company-tooltip-scrollbar-track", + "company-tooltip-search", + "company-tooltip-search-selection", + "company-tooltip-selection" + ], + "company-box": [ + "company-box-annotation", + "company-box-background", + "company-box-candidate", + "company-box-numbers", + "company-box-scrollbar", + "company-box-selection" + ], + "consult": [ + "consult-async-failed", + "consult-async-finished", + "consult-async-running", + "consult-async-split", + "consult-bookmark", + "consult-buffer", + "consult-file", + "consult-grep-context", + "consult-help", + "consult-highlight-mark", + "consult-highlight-match", + "consult-key", + "consult-line-number", + "consult-line-number-prefix", + "consult-line-number-wrapped", + "consult-narrow-indicator", + "consult-preview-insertion", + "consult-preview-line", + "consult-preview-match", + "consult-separator" + ], + "dashboard": [ + "dashboard-banner-logo-title", + "dashboard-footer-face", + "dashboard-footer-icon-face", + "dashboard-heading", + "dashboard-items-face", + "dashboard-navigator", + "dashboard-no-items-face", + "dashboard-text-banner" + ], + "dirvish": [ + "dirvish-collapse-dir-face", + "dirvish-collapse-empty-dir-face", + "dirvish-collapse-file-face", + "dirvish-emerge-group-title", + "dirvish-file-device-number", + "dirvish-file-group-id", + "dirvish-file-inode-number", + "dirvish-file-link-number", + "dirvish-file-modes", + "dirvish-file-size", + "dirvish-file-time", + "dirvish-file-user-id", + "dirvish-free-space", + "dirvish-git-commit-message-face", + "dirvish-hl-line", + "dirvish-hl-line-inactive", + "dirvish-inactive", + "dirvish-media-info-heading", + "dirvish-media-info-property-key", + "dirvish-narrow-match-face-0", + "dirvish-narrow-match-face-1", + "dirvish-narrow-match-face-2", + "dirvish-narrow-match-face-3", + "dirvish-narrow-split", + "dirvish-proc-failed", + "dirvish-proc-finished", + "dirvish-proc-running", + "dirvish-subtree-guide", + "dirvish-subtree-state", + "dirvish-vc-added-state", + "dirvish-vc-conflict-state", + "dirvish-vc-edited-state", + "dirvish-vc-locked-state", + "dirvish-vc-missing-state", + "dirvish-vc-needs-merge-face", + "dirvish-vc-needs-update-state", + "dirvish-vc-removed-state", + "dirvish-vc-unregistered-face" + ], + "elfeed": [ + "elfeed-log-date-face", + "elfeed-log-debug-level-face", + "elfeed-log-error-level-face", + "elfeed-log-info-level-face", + "elfeed-log-warn-level-face", + "elfeed-search-date-face", + "elfeed-search-feed-face", + "elfeed-search-filter-face", + "elfeed-search-last-update-face", + "elfeed-search-tag-face", + "elfeed-search-title-face", + "elfeed-search-unread-count-face", + "elfeed-search-unread-title-face" + ], + "embark": [ + "embark-collect-annotation", + "embark-collect-candidate", + "embark-collect-group-separator", + "embark-collect-group-title", + "embark-keybinding", + "embark-keybinding-repeat", + "embark-keymap", + "embark-selected", + "embark-target", + "embark-verbose-indicator-documentation", + "embark-verbose-indicator-shadowed", + "embark-verbose-indicator-title" + ], + "emms": [ + "emms-browser-album-face", + "emms-browser-albumartist-face", + "emms-browser-artist-face", + "emms-browser-composer-face", + "emms-browser-performer-face", + "emms-browser-track-face", + "emms-browser-year/genre-face", + "emms-metaplaylist-mode-current-face", + "emms-metaplaylist-mode-face", + "emms-playlist-selected-face", + "emms-playlist-track-face" + ], + "flycheck": [ + "flycheck-delimited-error", + "flycheck-error", + "flycheck-error-delimiter", + "flycheck-error-list-checker-name", + "flycheck-error-list-column-number", + "flycheck-error-list-error", + "flycheck-error-list-error-message", + "flycheck-error-list-filename", + "flycheck-error-list-highlight", + "flycheck-error-list-id", + "flycheck-error-list-id-with-explainer", + "flycheck-error-list-info", + "flycheck-error-list-line-number", + "flycheck-error-list-warning", + "flycheck-fringe-error", + "flycheck-fringe-info", + "flycheck-fringe-warning", + "flycheck-info", + "flycheck-verify-select-checker", + "flycheck-warning" + ], + "flyspell-correct": [ + "flyspell-correct-highlight-face" + ], + "ghostel": [ + "ghostel-color-black", + "ghostel-color-blue", + "ghostel-color-bright-black", + "ghostel-color-bright-blue", + "ghostel-color-bright-cyan", + "ghostel-color-bright-green", + "ghostel-color-bright-magenta", + "ghostel-color-bright-red", + "ghostel-color-bright-white", + "ghostel-color-bright-yellow", + "ghostel-color-cyan", + "ghostel-color-green", + "ghostel-color-magenta", + "ghostel-color-red", + "ghostel-color-white", + "ghostel-color-yellow", + "ghostel-default", + "ghostel-fake-cursor", + "ghostel-fake-cursor-box" + ], + "git-gutter": [ + "git-gutter:added", + "git-gutter:deleted", + "git-gutter:modified", + "git-gutter:separator", + "git-gutter:unchanged" + ], + "highlight-indent-guides": [ + "highlight-indent-guides-character-face", + "highlight-indent-guides-even-face", + "highlight-indent-guides-odd-face", + "highlight-indent-guides-stack-character-face", + "highlight-indent-guides-stack-even-face", + "highlight-indent-guides-stack-odd-face", + "highlight-indent-guides-top-character-face", + "highlight-indent-guides-top-even-face", + "highlight-indent-guides-top-odd-face" + ], + "hl-todo": [ + "hl-todo", + "hl-todo-flymake-type" + ], + "json-mode": [ + "json-mode-object-name-face" + ], + "llama": [ + "llama-##-macro", + "llama-deleted-argument", + "llama-llama-macro", + "llama-mandatory-argument", + "llama-optional-argument" + ], + "lsp-mode": [ + "lsp-details-face", + "lsp-face-highlight-read", + "lsp-face-highlight-textual", + "lsp-face-highlight-write", + "lsp-face-rename", + "lsp-inlay-hint-face", + "lsp-inlay-hint-parameter-face", + "lsp-inlay-hint-type-face", + "lsp-installation-buffer-face", + "lsp-installation-finished-buffer-face", + "lsp-rename-placeholder-face", + "lsp-signature-face", + "lsp-signature-highlight-function-argument", + "lsp-signature-posframe" + ], + "lv": [ + "lv-separator" + ], + "magit": [ + "git-commit-comment-action", + "git-commit-comment-branch-local", + "git-commit-comment-branch-remote", + "git-commit-comment-detached", + "git-commit-comment-file", + "git-commit-comment-heading", + "git-commit-keyword", + "git-commit-nonempty-second-line", + "git-commit-overlong-summary", + "git-commit-summary", + "git-commit-trailer-token", + "git-commit-trailer-value", + "magit-bisect-bad", + "magit-bisect-good", + "magit-bisect-skip", + "magit-blame-date", + "magit-blame-dimmed", + "magit-blame-hash", + "magit-blame-heading", + "magit-blame-highlight", + "magit-blame-margin", + "magit-blame-name", + "magit-blame-summary", + "magit-branch-current", + "magit-branch-local", + "magit-branch-remote", + "magit-branch-remote-head", + "magit-branch-upstream", + "magit-branch-warning", + "magit-cherry-equivalent", + "magit-cherry-unmatched", + "magit-diff-added", + "magit-diff-added-highlight", + "magit-diff-base", + "magit-diff-base-highlight", + "magit-diff-conflict-heading", + "magit-diff-conflict-heading-highlight", + "magit-diff-context", + "magit-diff-context-highlight", + "magit-diff-file-heading", + "magit-diff-file-heading-highlight", + "magit-diff-file-heading-selection", + "magit-diff-hunk-heading", + "magit-diff-hunk-heading-highlight", + "magit-diff-hunk-heading-selection", + "magit-diff-hunk-region", + "magit-diff-lines-boundary", + "magit-diff-lines-heading", + "magit-diff-our", + "magit-diff-our-highlight", + "magit-diff-removed", + "magit-diff-removed-highlight", + "magit-diff-revision-summary", + "magit-diff-revision-summary-highlight", + "magit-diff-their", + "magit-diff-their-highlight", + "magit-diff-whitespace-warning", + "magit-diffstat-added", + "magit-diffstat-removed", + "magit-dimmed", + "magit-filename", + "magit-hash", + "magit-head", + "magit-header-line", + "magit-header-line-key", + "magit-header-line-log-select", + "magit-keyword", + "magit-keyword-squash", + "magit-log-author", + "magit-log-date", + "magit-log-graph", + "magit-mode-line-process", + "magit-mode-line-process-error", + "magit-process-ng", + "magit-process-ok", + "magit-reflog-amend", + "magit-reflog-checkout", + "magit-reflog-cherry-pick", + "magit-reflog-commit", + "magit-reflog-merge", + "magit-reflog-other", + "magit-reflog-rebase", + "magit-reflog-remote", + "magit-reflog-reset", + "magit-refname", + "magit-refname-pullreq", + "magit-refname-stash", + "magit-refname-wip", + "magit-sequence-done", + "magit-sequence-drop", + "magit-sequence-exec", + "magit-sequence-head", + "magit-sequence-onto", + "magit-sequence-part", + "magit-sequence-pick", + "magit-sequence-stop", + "magit-signature-bad", + "magit-signature-error", + "magit-signature-expired", + "magit-signature-expired-key", + "magit-signature-good", + "magit-signature-revoked", + "magit-signature-untrusted", + "magit-tag" + ], + "magit-section": [ + "magit-left-margin", + "magit-section-child-count", + "magit-section-heading", + "magit-section-heading-selection", + "magit-section-highlight", + "magit-section-secondary-heading" + ], + "malyon": [ + "malyon-face-bold", + "malyon-face-error", + "malyon-face-italic", + "malyon-face-plain", + "malyon-face-reverse" + ], + "marginalia": [ + "marginalia-archive", + "marginalia-char", + "marginalia-date", + "marginalia-documentation", + "marginalia-file-name", + "marginalia-file-owner", + "marginalia-file-priv-dir", + "marginalia-file-priv-exec", + "marginalia-file-priv-link", + "marginalia-file-priv-no", + "marginalia-file-priv-other", + "marginalia-file-priv-rare", + "marginalia-file-priv-read", + "marginalia-file-priv-write", + "marginalia-function", + "marginalia-installed", + "marginalia-key", + "marginalia-lighter", + "marginalia-list", + "marginalia-mode", + "marginalia-modified", + "marginalia-null", + "marginalia-number", + "marginalia-off", + "marginalia-on", + "marginalia-size", + "marginalia-string", + "marginalia-symbol", + "marginalia-true", + "marginalia-type", + "marginalia-value", + "marginalia-version" + ], + "markdown-mode": [ + "markdown-blockquote-face", + "markdown-bold-face", + "markdown-code-face", + "markdown-comment-face", + "markdown-footnote-marker-face", + "markdown-footnote-text-face", + "markdown-gfm-checkbox-face", + "markdown-header-delimiter-face", + "markdown-header-face", + "markdown-header-face-1", + "markdown-header-face-2", + "markdown-header-face-3", + "markdown-header-face-4", + "markdown-header-face-5", + "markdown-header-face-6", + "markdown-header-rule-face", + "markdown-highlight-face", + "markdown-highlighting-face", + "markdown-hr-face", + "markdown-html-attr-name-face", + "markdown-html-attr-value-face", + "markdown-html-entity-face", + "markdown-html-tag-delimiter-face", + "markdown-html-tag-name-face", + "markdown-inline-code-face", + "markdown-italic-face", + "markdown-language-info-face", + "markdown-language-keyword-face", + "markdown-line-break-face", + "markdown-link-face", + "markdown-link-title-face", + "markdown-list-face", + "markdown-markup-face", + "markdown-math-face", + "markdown-metadata-key-face", + "markdown-metadata-value-face", + "markdown-missing-link-face", + "markdown-plain-url-face", + "markdown-pre-face", + "markdown-reference-face", + "markdown-strike-through-face", + "markdown-table-face", + "markdown-url-face" + ], + "nerd-icons": [ + "nerd-icons-blue", + "nerd-icons-blue-alt", + "nerd-icons-cyan", + "nerd-icons-cyan-alt", + "nerd-icons-dblue", + "nerd-icons-dcyan", + "nerd-icons-dgreen", + "nerd-icons-dmaroon", + "nerd-icons-dorange", + "nerd-icons-dpink", + "nerd-icons-dpurple", + "nerd-icons-dred", + "nerd-icons-dsilver", + "nerd-icons-dyellow", + "nerd-icons-green", + "nerd-icons-lblue", + "nerd-icons-lcyan", + "nerd-icons-lgreen", + "nerd-icons-lmaroon", + "nerd-icons-lorange", + "nerd-icons-lpink", + "nerd-icons-lpurple", + "nerd-icons-lred", + "nerd-icons-lsilver", + "nerd-icons-lyellow", + "nerd-icons-maroon", + "nerd-icons-orange", + "nerd-icons-pink", + "nerd-icons-purple", + "nerd-icons-purple-alt", + "nerd-icons-red", + "nerd-icons-red-alt", + "nerd-icons-silver", + "nerd-icons-yellow" + ], + "nerd-icons-completion": [ + "nerd-icons-completion-dir-face" + ], + "orderless": [ + "orderless-match-face-0", + "orderless-match-face-1", + "orderless-match-face-2", + "orderless-match-face-3" + ], + "org-roam": [ + "org-roam-dailies-calendar-note", + "org-roam-dim", + "org-roam-header-line", + "org-roam-olp", + "org-roam-preview-heading", + "org-roam-preview-heading-highlight", + "org-roam-preview-heading-selection", + "org-roam-preview-region", + "org-roam-title" + ], + "org-superstar": [ + "org-superstar-first", + "org-superstar-header-bullet", + "org-superstar-item", + "org-superstar-leading" + ], + "prescient": [ + "prescient-primary-highlight", + "prescient-secondary-highlight" + ], + "rainbow-delimiters": [ + "rainbow-delimiters-base-error-face", + "rainbow-delimiters-base-face", + "rainbow-delimiters-depth-1-face", + "rainbow-delimiters-depth-2-face", + "rainbow-delimiters-depth-3-face", + "rainbow-delimiters-depth-4-face", + "rainbow-delimiters-depth-5-face", + "rainbow-delimiters-depth-6-face", + "rainbow-delimiters-depth-7-face", + "rainbow-delimiters-depth-8-face", + "rainbow-delimiters-depth-9-face", + "rainbow-delimiters-mismatched-face", + "rainbow-delimiters-unmatched-face" + ], + "symbol-overlay": [ + "symbol-overlay-default-face", + "symbol-overlay-face-1", + "symbol-overlay-face-2", + "symbol-overlay-face-3", + "symbol-overlay-face-4", + "symbol-overlay-face-5", + "symbol-overlay-face-6", + "symbol-overlay-face-7", + "symbol-overlay-face-8" + ], + "tmr": [ + "tmr-description", + "tmr-duration", + "tmr-end-time", + "tmr-finished", + "tmr-is-acknowledged", + "tmr-must-be-acknowledged", + "tmr-start-time", + "tmr-tabulated-acknowledgement", + "tmr-tabulated-description", + "tmr-tabulated-end-time", + "tmr-tabulated-remaining-time", + "tmr-tabulated-start-time" + ], + "transient": [ + "transient-active-infix", + "transient-argument", + "transient-delimiter", + "transient-disabled-suffix", + "transient-enabled-suffix", + "transient-heading", + "transient-higher-level", + "transient-inactive-argument", + "transient-inactive-value", + "transient-inapt-argument", + "transient-inapt-suffix", + "transient-key", + "transient-key-exit", + "transient-key-noop", + "transient-key-recurse", + "transient-key-return", + "transient-key-stack", + "transient-key-stay", + "transient-mismatched-key", + "transient-nonstandard-key", + "transient-unreachable", + "transient-unreachable-key", + "transient-value" + ], + "vertico": [ + "vertico-current", + "vertico-group-separator", + "vertico-group-title", + "vertico-multiline" + ], + "web-mode": [ + "web-mode-annotation-face", + "web-mode-annotation-html-face", + "web-mode-annotation-tag-face", + "web-mode-annotation-type-face", + "web-mode-annotation-value-face", + "web-mode-block-attr-name-face", + "web-mode-block-attr-value-face", + "web-mode-block-comment-face", + "web-mode-block-control-face", + "web-mode-block-delimiter-face", + "web-mode-block-face", + "web-mode-block-string-face", + "web-mode-bold-face", + "web-mode-builtin-face", + "web-mode-comment-face", + "web-mode-comment-keyword-face", + "web-mode-constant-face", + "web-mode-css-at-rule-face", + "web-mode-css-color-face", + "web-mode-css-comment-face", + "web-mode-css-function-face", + "web-mode-css-priority-face", + "web-mode-css-property-name-face", + "web-mode-css-pseudo-class-face", + "web-mode-css-selector-class-face", + "web-mode-css-selector-face", + "web-mode-css-selector-tag-face", + "web-mode-css-string-face", + "web-mode-css-variable-face", + "web-mode-current-column-highlight-face", + "web-mode-current-element-highlight-face", + "web-mode-doctype-face", + "web-mode-error-face", + "web-mode-filter-face", + "web-mode-folded-face", + "web-mode-function-call-face", + "web-mode-function-name-face", + "web-mode-html-attr-custom-face", + "web-mode-html-attr-engine-face", + "web-mode-html-attr-equal-face", + "web-mode-html-attr-name-face", + "web-mode-html-attr-value-face", + "web-mode-html-entity-face", + "web-mode-html-tag-bracket-face", + "web-mode-html-tag-custom-face", + "web-mode-html-tag-face", + "web-mode-html-tag-namespaced-face", + "web-mode-html-tag-unclosed-face", + "web-mode-inlay-face", + "web-mode-interpolate-color1-face", + "web-mode-interpolate-color2-face", + "web-mode-interpolate-color3-face", + "web-mode-interpolate-color4-face", + "web-mode-italic-face", + "web-mode-javascript-comment-face", + "web-mode-javascript-string-face", + "web-mode-json-comment-face", + "web-mode-json-context-face", + "web-mode-json-key-face", + "web-mode-json-string-face", + "web-mode-jsx-depth-1-face", + "web-mode-jsx-depth-2-face", + "web-mode-jsx-depth-3-face", + "web-mode-jsx-depth-4-face", + "web-mode-jsx-depth-5-face", + "web-mode-keyword-face", + "web-mode-param-name-face", + "web-mode-part-comment-face", + "web-mode-part-face", + "web-mode-part-string-face", + "web-mode-preprocessor-face", + "web-mode-script-face", + "web-mode-sql-keyword-face", + "web-mode-string-face", + "web-mode-style-face", + "web-mode-symbol-face", + "web-mode-type-face", + "web-mode-underline-face", + "web-mode-variable-name-face", + "web-mode-warning-face", + "web-mode-whitespace-face" + ], + "yasnippet": [ + "yas--field-debug-face", + "yas-field-highlight-face" + ] +} diff --git a/scripts/theme-studio/samples.py b/scripts/theme-studio/samples.py new file mode 100644 index 000000000..e487cc5e9 --- /dev/null +++ b/scripts/theme-studio/samples.py @@ -0,0 +1,269 @@ +GROUND="#0d0b0a" +COLS={ + 'kw':("#67809c",True),'bi':("#67809c",False),'pp':("#67809c",False), + 'fnd':("#a9b2bb",True),'fnc':("#a9b2bb",False),'dec':("#e8bd30",False), + 'ty':("#9b5fd0",False),'prop':("#838d97",False), + 'con':("#cb6b4d",False),'num':("#cb6b4d",False),'esc':("#cb6b4d",False), + 'str':("#2ba178",False),'re':("#5d9b86",False),'doc':("#5d9b86",False), + 'cm':("#be9e74",False),'cmd':("#a9b2bb",False), + 'var':("#e8bd30",False),'op':("#a9b2bb",False),'punc':("#a9b2bb",False),'p':("#ffffff",False), +} +NAMES={"#67809c":"blue","#e8bd30":"gold","#9b5fd0":"regal","#2ba178":"emerald","#cb6b4d":"terracotta","#be9e74":"tan","#5d9b86":"sage","#cdced1":"white","#a9b2bb":"silver","#838d97":"steel","#5e6770":"pewter","#2f343a":"gunmetal","#264364":"navy"} +def esc(t): return t.replace("&","&").replace("<","<").replace(">",">") +def span(k,t): + c,b=COLS[k]; w=";font-weight:bold" if b else "" + return f'{esc(t)}' +def render(lines): return "\n".join("".join(span(k,t) for k,t in ln) or " " for ln in lines) + +PYS=[ + [('cmd','#'),('cm',' theme.py')], + [('kw','from'),('p',' '),('var','dataclasses'),('p',' '),('kw','import'),('p',' '),('var','dataclass'),('punc',','),('p',' '),('var','field')], + [], + [('con','DEFAULT_PORT'),('op',':'),('p',' '),('ty','int'),('p',' '),('op','='),('p',' '),('num','8080')], + [('con','HEX'),('p',' '),('op','='),('p',' '),('var','re'),('op','.'),('fnc','compile'),('punc','('),('re','r"#[0-9a-f]{6}"'),('punc',')')], + [], + [('dec','@dataclass')], + [('kw','class'),('p',' '),('ty','Theme'),('op',':')], + [('p',' '),('doc','"""A color theme."""')], + [('p',' '),('prop','name'),('op',':'),('p',' '),('ty','str'),('p',' '),('op','='),('p',' '),('str','"dupre"')], + [('p',' '),('prop','colors'),('op',':'),('p',' '),('ty','dict'),('p',' '),('op','='),('p',' '),('fnc','field'),('punc','('),('prop','default_factory'),('op','='),('ty','dict'),('punc',')')], + [], + [('p',' '),('kw','def'),('p',' '),('fnd','resolve'),('punc','('),('var','self'),('punc',','),('p',' '),('var','key'),('op',':'),('p',' '),('ty','str'),('punc',')'),('p',' '),('op','->'),('p',' '),('ty','str'),('p',' '),('op','|'),('p',' '),('con','None'),('op',':')], + [('p',' '),('cmd','#'),('cm',' fallback to none')], + [('p',' '),('var','v'),('p',' '),('op','='),('p',' '),('var','self'),('op','.'),('prop','colors'),('op','.'),('fnc','get'),('punc','('),('var','key'),('punc',','),('p',' '),('str','"'),('esc','\\t'),('str','none"'),('punc',')')], + [('p',' '),('kw','if'),('p',' '),('bi','len'),('punc','('),('var','v'),('punc',')'),('p',' '),('op','=='),('p',' '),('num','0'),('op',':'),('p',' '),('kw','return'),('p',' '),('con','None')], + [('p',' '),('kw','return'),('p',' '),('var','v')], + [], + [('p',' '),('dec','@property')], + [('p',' '),('kw','def'),('p',' '),('fnd','size'),('punc','('),('var','self'),('punc',')'),('p',' '),('op','->'),('p',' '),('ty','int'),('op',':')], + [('p',' '),('kw','return'),('p',' '),('bi','len'),('punc','('),('var','self'),('op','.'),('prop','colors'),('punc',')')], + [], + [('var','theme'),('p',' '),('op','='),('p',' '),('ty','Theme'),('punc','('),('str','"dupre"'),('punc',')')], + [('fnc','print'),('punc','('),('var','theme'),('op','.'),('fnc','resolve'),('punc','('),('str','"bg"'),('punc','))')], +] +ELS=[ + [('cmd',';;'),('cm',' cache.el')], + [('punc','('),('kw','require'),('p',' '),('con',"'cl-lib"),('punc',')')], + [], + [('punc','('),('kw','defvar'),('p',' '),('var','cache--tbl'),('p',' '),('punc','('),('fnc','make-hash-table'),('p',' '),('con',':test'),('p',' '),('con',"'equal"),('punc','))')], + [('p',' '),('doc','"Memo table.")')], + [], + [('punc','('),('kw','defun'),('p',' '),('fnd','cache-get'),('p',' '),('punc','('),('var','key'),('punc',')')], + [('p',' '),('doc','"Return cached value for KEY."')], + [('p',' '),('punc','('),('kw','or'),('p',' '),('punc','('),('bi','gethash'),('p',' '),('var','key'),('p',' '),('var','cache--tbl'),('punc',')')], + [('p',' '),('punc','('),('kw','let'),('p',' '),('punc','(('),('var','v'),('p',' '),('punc','('),('fnc','compute'),('p',' '),('var','key'),('p',' '),('num','42'),('punc','))) ')], + [('p',' '),('punc','('),('fnc','puthash'),('p',' '),('var','key'),('p',' '),('var','v'),('p',' '),('var','cache--tbl'),('punc',') '),('var','v'),('punc','))))')], + [], + [('punc','('),('kw','defun'),('p',' '),('fnd','cache-clear'),('p',' '),('punc','()')], + [('p',' '),('doc','"Empty the memo table."')], + [('p',' '),('punc','('),('kw','interactive'),('punc',')')], + [('p',' '),('punc','('),('fnc','clrhash'),('p',' '),('var','cache--tbl'),('punc',')')], + [('p',' '),('punc','('),('fnc','message'),('p',' '),('str','"cleared'),('esc','\\n'),('str','"'),('punc','))')], + [], + [('punc','('),('kw','defun'),('p',' '),('fnd','cache-keys'),('p',' '),('punc','()')], + [('p',' '),('doc','"Return all keys."')], + [('p',' '),('punc','('),('kw','let'),('p',' '),('punc','(('),('var','acc'),('p',' '),('con','nil'),('punc','))')], + [('p',' '),('punc','('),('fnc','maphash'),('p',' '),('punc','('),('kw','lambda'),('p',' '),('punc','('),('var','k'),('p',' '),('var','_v'),('punc',')'),('p',' '),('punc','('),('fnc','push'),('p',' '),('var','k'),('p',' '),('var','acc'),('punc','))')], + [('p',' '),('var','cache--tbl'),('punc',')'),('p',' '),('var','acc'),('punc','))')], + [], + [('punc','('),('kw','provide'),('p',' '),('con',"'cache"),('punc',')')], +] +GOS=[ + [('cmd','//'),('cm',' queue.go')], + [('kw','package'),('p',' '),('var','main')], + [], + [('kw','import'),('p',' '),('str','"fmt"')], + [], + [('kw','const'),('p',' '),('con','MaxItems'),('p',' '),('op','='),('p',' '),('num','100')], + [], + [('kw','type'),('p',' '),('ty','Order'),('p',' '),('kw','struct'),('p',' '),('punc','{')], + [('p',' '),('prop','ID'),('p',' '),('ty','int')], + [('p',' '),('prop','Name'),('p',' '),('ty','string')], + [('punc','}')], + [], + [('kw','func'),('p',' '),('punc','('),('var','q'),('p',' '),('op','*'),('ty','Queue'),('punc',')'),('p',' '),('fnd','Push'),('punc','('),('var','o'),('p',' '),('op','*'),('ty','Order'),('punc',')'),('p',' '),('ty','error'),('p',' '),('punc','{')], + [('p',' '),('cmd','//'),('cm',' reject nil')], + [('p',' '),('kw','if'),('p',' '),('var','o'),('p',' '),('op','=='),('p',' '),('con','nil'),('p',' '),('punc','{')], + [('p',' '),('kw','return'),('p',' '),('fnc','fmt.Errorf'),('punc','('),('str','"nil'),('esc','\\n'),('str','"'),('punc',')')], + [('p',' '),('punc','}')], + [('p',' '),('var','q'),('op','.'),('prop','items'),('p',' '),('op','='),('p',' '),('bi','append'),('punc','('),('var','q'),('op','.'),('prop','items'),('punc',','),('p',' '),('var','o'),('punc',')')], + [('p',' '),('kw','return'),('p',' '),('con','nil')], + [('punc','}')], + [], + [('kw','func'),('p',' '),('fnd','main'),('punc','()'),('p',' '),('punc','{')], + [('p',' '),('fnc','fmt.Println'),('punc','('),('op','&'),('ty','Queue'),('punc','{}'),('punc',')')], + [('punc','}')], +] +TSS=[ + [('cmd','//'),('cm',' orders.ts')], + [('kw','import'),('p',' '),('punc','{'),('p',' '),('ty','Order'),('p',' '),('punc','}'),('p',' '),('kw','from'),('p',' '),('str','"./types"')], + [], + [('kw','export'),('p',' '),('kw','interface'),('p',' '),('ty','Queue'),('p',' '),('punc','{')], + [('p',' '),('prop','max'),('op',':'),('p',' '),('ty','number'),('punc',';')], + [('p',' '),('prop','items'),('op',':'),('p',' '),('ty','Order'),('punc','[];')], + [('punc','}')], + [], + [('dec','@Injectable'),('punc','()')], + [('kw','export'),('p',' '),('kw','class'),('p',' '),('ty','OrderQueue'),('p',' '),('kw','implements'),('p',' '),('ty','Queue'),('p',' '),('punc','{')], + [('p',' '),('kw','private'),('p',' '),('prop','re'),('p',' '),('op','='),('p',' '),('re','/^#[0-9a-f]{6}$/i'),('punc',';')], + [], + [('p',' '),('fnd','push'),('punc','('),('var','o'),('op',':'),('p',' '),('ty','Order'),('punc',')'),('op',':'),('p',' '),('ty','boolean'),('p',' '),('punc','{')], + [('p',' '),('kw','if'),('p',' '),('punc','('),('var','o'),('p',' '),('op','==='),('p',' '),('con','null'),('punc',')'),('p',' '),('kw','return'),('p',' '),('con','false'),('punc',';')], + [('p',' '),('var','console'),('op','.'),('fnc','log'),('punc','('),('str','`id '),('punc','${'),('var','o'),('op','.'),('prop','id'),('punc','}'),('esc','\\n'),('str','`'),('punc',');')], + [('p',' '),('kw','return'),('p',' '),('con','true'),('punc',';')], + [('p',' '),('punc','}')], + [('punc','}')], + [], + [('kw','const'),('p',' '),('con','LIMIT'),('op',':'),('p',' '),('ty','number'),('p',' '),('op','='),('p',' '),('num','50'),('punc',';')], + [('kw','const'),('p',' '),('var','q'),('p',' '),('op','='),('p',' '),('kw','new'),('p',' '),('ty','OrderQueue'),('punc','()'),('punc',';')], + [('var','q'),('op','.'),('fnd','push'),('punc','('),('punc','{'),('p',' '),('prop','id'),('op',':'),('p',' '),('num','1'),('p',' '),('punc','}'),('p',' '),('kw','as'),('p',' '),('ty','Order'),('punc',')'),('punc',';')], + [('var','console'),('op','.'),('fnc','log'),('punc','('),('var','q'),('op','.'),('prop','max'),('punc',')'),('punc',';')], + [('kw','const'),('p',' '),('var','cap'),('p',' '),('op','='),('p',' '),('var','Math'),('op','.'),('bi','max'),('punc','('),('con','LIMIT'),('punc',','),('p',' '),('num','0'),('punc',')'),('punc',';')], +] + +CS=[ + [('cmd','/**'),('doc',' Order queue. */')], + [('pp','#include'),('p',' '),('str','')], + [('pp','#include'),('p',' '),('str','')], + [('pp','#define'),('p',' '),('con','MAX_PORT'),('p',' '),('num','8080')], + [], + [('kw','typedef'),('p',' '),('kw','struct'),('p',' '),('punc','{')], + [('p',' '),('ty','int'),('p',' '),('prop','id'),('punc',';')], + [('p',' '),('kw','const'),('p',' '),('ty','char'),('p',' '),('op','*'),('prop','name'),('punc',';')], + [('punc','}'),('p',' '),('ty','Order'),('punc',';')], + [], + [('cmd','//'),('cm',' returns -1 on null input')], + [('ty','int'),('p',' '),('fnd','push'),('punc','('),('ty','Order'),('p',' '),('op','*'),('var','o'),('punc',')'),('p',' '),('dec','__attribute__'),('punc','(('),('dec','nonnull'),('punc','))'),('p',' '),('punc','{')], + [('p',' '),('kw','if'),('p',' '),('punc','('),('var','o'),('p',' '),('op','=='),('p',' '),('con','NULL'),('punc',')'),('p',' '),('kw','return'),('p',' '),('num','-1'),('punc',';')], + [('p',' '),('fnc','printf'),('punc','('),('str','"id=%d'),('esc','\\n'),('str','"'),('punc',','),('p',' '),('var','o'),('op','->'),('prop','id'),('punc',');')], + [('p',' '),('kw','return'),('p',' '),('num','0'),('punc',';')], + [('punc','}')], + [], + [('ty','int'),('p',' '),('fnd','main'),('punc','('),('ty','void'),('punc',')'),('p',' '),('punc','{')], + [('p',' '),('ty','Order'),('p',' '),('var','o'),('p',' '),('op','='),('p',' '),('punc','{'),('p',' '),('op','.'),('prop','id'),('p',' '),('op','='),('p',' '),('num','1'),('punc',','),('p',' '),('op','.'),('prop','name'),('p',' '),('op','='),('p',' '),('str','"dupre"'),('p',' '),('punc','}'),('punc',';')], + [('p',' '),('ty','Order'),('p',' '),('op','*'),('var','p2'),('p',' '),('op','='),('p',' '),('bi','malloc'),('punc','('),('bi','sizeof'),('punc','('),('ty','Order'),('punc','))'),('punc',';')], + [('p',' '),('fnc','push'),('punc','('),('op','&'),('var','o'),('punc',')'),('punc',';')], + [('p',' '),('bi','free'),('punc','('),('var','p2'),('punc',')'),('punc',';')], + [('p',' '),('kw','return'),('p',' '),('num','0'),('punc',';')], + [('punc','}')], +] +CPS=[ + [('cmd','/**'),('doc',' A color theme. */')], + [('pp','#include'),('p',' '),('str','')], + [('pp','#include'),('p',' '),('str','')], + [('pp','#pragma'),('p',' '),('pp','once')], + [], + [('kw','namespace'),('p',' '),('var','dupre'),('p',' '),('punc','{')], + [], + [('kw','template'),('op','<'),('kw','typename'),('p',' '),('ty','T'),('op','>'),('p',' '),('kw','class'),('p',' '),('ty','Theme'),('p',' '),('punc','{')], + [('kw','public'),('op',':')], + [('p',' '),('kw','static'),('p',' '),('kw','constexpr'),('p',' '),('ty','int'),('p',' '),('con','MAX'),('p',' '),('op','='),('p',' '),('num','0x20'),('punc',';')], + [('p',' '),('ty','std'),('op','::'),('ty','string'),('p',' '),('prop','name_'),('p',' '),('op','='),('p',' '),('str','"dupre"'),('punc',';')], + [], + [('p',' '),('dec','[[nodiscard]]'),('p',' '),('ty','T'),('p',' '),('fnd','resolve'),('punc','('),('kw','const'),('p',' '),('ty','std'),('op','::'),('ty','string'),('op','&'),('p',' '),('var','key'),('punc',')'),('p',' '),('kw','const'),('p',' '),('punc','{')], + [('p',' '),('cmd','//'),('cm',' validate against a hex pattern')], + [('p',' '),('kw','static'),('p',' '),('ty','std'),('op','::'),('ty','regex'),('p',' '),('var','re'),('punc','('),('re','R"(#[0-9a-f]{6})"'),('punc',')'),('punc',';')], + [('p',' '),('kw','if'),('p',' '),('punc','('),('var','key'),('op','.'),('fnc','empty'),('punc','()'),('punc',')'),('p',' '),('kw','return'),('p',' '),('con','nullptr'),('punc',';')], + [('p',' '),('kw','return'),('p',' '),('ty','T'),('punc','{'),('var','key'),('punc','}'),('punc',';')], + [('p',' '),('punc','}')], + [('punc','}'),('punc',';')], + [], + [('ty','int'),('p',' '),('fnd','main'),('punc','()'),('p',' '),('punc','{')], + [('p',' '),('kw','auto'),('p',' '),('var','t'),('p',' '),('op','='),('p',' '),('ty','Theme'),('op','<'),('ty','int'),('op','>'),('punc','{}'),('punc',';')], + [('p',' '),('bi','static_cast'),('op','<'),('ty','int'),('op','>'),('punc','('),('var','t'),('op','.'),('prop','name_'),('op','.'),('fnc','size'),('punc','())'),('punc',';')], + [('p',' '),('ty','std'),('op','::'),('fnc','printf'),('punc','('),('str','"%s'),('esc','\\n'),('str','"'),('punc',','),('p',' '),('var','t'),('op','.'),('prop','name_'),('op','.'),('fnc','c_str'),('punc','())'),('punc',';')], + [('p',' '),('kw','return'),('p',' '),('num','0'),('punc',';')], + [('punc','}')], +] +JAS=[ + [('cmd','/**'),('doc',' A color theme. */')], + [('kw','package'),('p',' '),('var','com'),('op','.'),('var','dupre'),('punc',';')], + [('kw','import'),('p',' '),('var','java'),('op','.'),('var','util'),('op','.'),('var','regex'),('op','.'),('ty','Pattern'),('punc',';')], + [], + [('dec','@Deprecated')], + [('kw','public'),('p',' '),('kw','final'),('p',' '),('kw','class'),('p',' '),('ty','Theme'),('p',' '),('punc','{')], + [('p',' '),('kw','private'),('p',' '),('kw','static'),('p',' '),('kw','final'),('p',' '),('ty','int'),('p',' '),('con','MAX_PORT'),('p',' '),('op','='),('p',' '),('num','8080'),('punc',';')], + [('p',' '),('kw','private'),('p',' '),('kw','final'),('p',' '),('ty','String'),('p',' '),('prop','name'),('p',' '),('op','='),('p',' '),('str','"dupre"'),('punc',';')], + [('p',' '),('kw','private'),('p',' '),('kw','static'),('p',' '),('kw','final'),('p',' '),('ty','Pattern'),('p',' '),('con','HEX'),('p',' '),('op','='),('p',' '),('ty','Pattern'),('op','.'),('fnc','compile'),('punc','('),('re','"#[0-9a-f]{6}"'),('punc',')'),('punc',';')], + [], + [('p',' '),('dec','@Override')], + [('p',' '),('kw','public'),('p',' '),('ty','String'),('p',' '),('fnd','resolve'),('punc','('),('ty','String'),('p',' '),('var','key'),('punc',')'),('p',' '),('punc','{')], + [('p',' '),('cmd','//'),('cm',' fall back to null')], + [('p',' '),('kw','if'),('p',' '),('punc','('),('var','key'),('op','.'),('fnc','isEmpty'),('punc','()'),('punc',')'),('p',' '),('kw','return'),('p',' '),('con','null'),('punc',';')], + [('p',' '),('kw','return'),('p',' '),('var','key'),('op','.'),('fnc','strip'),('punc','('),('punc',')'),('op','+'),('str','"'),('esc','\\t'),('str','"'),('punc',';')], + [('p',' '),('punc','}')], + [], + [('p',' '),('kw','public'),('p',' '),('kw','static'),('p',' '),('ty','void'),('p',' '),('fnd','main'),('punc','('),('ty','String'),('punc','[]'),('p',' '),('var','args'),('punc',')'),('p',' '),('punc','{')], + [('p',' '),('ty','var'),('p',' '),('var','t'),('p',' '),('op','='),('p',' '),('kw','new'),('p',' '),('ty','Theme'),('punc','()'),('punc',';')], + [('p',' '),('ty','System'),('op','.'),('prop','out'),('op','.'),('fnc','println'),('punc','('),('var','t'),('op','.'),('fnc','resolve'),('punc','('),('str','"bg"'),('punc','))'),('punc',';')], + [('p',' '),('punc','}')], + [('punc','}')], +] +SHS=[ + [('cmd','#!'),('cm','/bin/bash')], + [('cmd','#'),('cm',' deploy.sh')], + [('bi','set'),('p',' '),('op','-'),('var','euo'),('p',' '),('var','pipefail')], + [], + [('var','PORT'),('op','='),('num','8080')], + [('var','NAME'),('op','='),('str','"dupre"')], + [], + [('fnd','deploy'),('punc','()'),('p',' '),('punc','{')], + [('p',' '),('kw','local'),('p',' '),('var','target'),('op','='),('str','"$1"')], + [('p',' '),('kw','if'),('p',' '),('punc','[['),('p',' '),('op','-z'),('p',' '),('str','"$target"'),('p',' '),('punc',']]'),('punc',';'),('p',' '),('kw','then')], + [('p',' '),('bi','echo'),('p',' '),('str','"no target"')], + [('p',' '),('kw','return'),('p',' '),('num','1')], + [('p',' '),('kw','fi')], + [('p',' '),('fnc','rsync'),('p',' '),('op','-az'),('p',' '),('str','"$NAME"'),('p',' '),('str','"$target"')], + [('punc','}')], + [], + [('fnd','main'),('punc','()'),('p',' '),('punc','{')], + [('p',' '),('kw','for'),('p',' '),('var','host'),('p',' '),('kw','in'),('p',' '),('str','"$@"'),('punc',';'),('p',' '),('kw','do')], + [('p',' '),('fnc','deploy'),('p',' '),('str','"$host"'),('p',' '),('op','||'),('p',' '),('bi','exit'),('p',' '),('num','1')], + [('p',' '),('kw','done')], + [('p',' '),('bi','echo'),('p',' '),('op','-e'),('p',' '),('str','"all done'),('esc','\\n'),('str','"')], + [('punc','}')], + [], + [('fnc','main'),('p',' '),('str','"$@"')], +] + +cols="".join(f'

{n}

{render(s)}
' for n,s in [("Elisp",ELS),("Go",GOS),("Python",PYS),("TypeScript",TSS),("Java",JAS),("C",CS),("C++",CPS),("Shell",SHS)]) +legend_rows=[ + ("keyword (bold)","kw","class def if return import"),("builtin","bi","len range print"), + ("function — definition (bold)","fnd","resolve cache-get push"),("function — call","fnc","get append fmt.Errorf"), + ("decorator / attribute","dec","@dataclass @Injectable"),("type / class","ty","str dict Order Queue boolean"), + ("property / field / key","prop","name colors items id re"),("constant","con","None nil true MaxItems :test"), + ("number","num","8080 100 42 0"),("string","str",'"dupre" "fmt" `id`'),("escape","esc",r'\t \n'), + ("regexp","re",'/^#[0-9a-f]{6}$/i'),("docstring","doc",'"""..." "Memo table."'), + ("comment","cm","# reject nil // fallback"),("comment delimiter","cmd","# // ;; /*"), + ("variable / use","var","v key self q console"),("operator","op",": = -> | == === . *"), + ("punctuation / bracket","punc","{ } ( ) [ ] , ;"), +] +def lrow(label,k,ex): + c,b=COLS[k] + return f'{NAMES.get(c,"")}{c}{label}{esc(ex)}' +legend="".join(lrow(l,k,e) for l,k,e in legend_rows) +def grp(title,items): + sw="".join(f'
{n}
{h}
' for n,h in items) + return f'
{title}
{sw}
' +palette=(grp("ground / foreground",[("ground","#0d0b0a"),("bg-dim","#1a1714"),("fg","#cdced1")]) + + grp("syntax hues",[("blue · keyword","#67809c"),("gold · variable","#e8bd30"),("regal · type","#9b5fd0"),("emerald · string","#2ba178"),("terracotta · const/num","#cb6b4d"),("tan · comment","#be9e74")]) + + grp("metallic greyscale (structural)",[("gunmetal","#2f343a"),("metal","#474e56"),("pewter","#5e6770"),("steel · property","#838d97"),("silver · fn/op/punct","#a9b2bb"),("bright · fg","#cdced1")]) + + grp("special green + fills",[("muted emerald · doc/regexp","#5d9b86"),("navy fill","#264364"),("gunmetal fill","#2f343a")])) +html=f'''dupre revision — canonical + +

code samples

+
{cols}
+

color → tree-sitter category assignment — click a header to sort

+{legend}
color △hex △category △example
+

palette

+{palette}\n''' +open("/tmp/dupre-canon.html","w").write(html) +print("wrote /tmp/dupre-canon.html") diff --git a/scripts/theme-studio/theme-studio.html b/scripts/theme-studio/theme-studio.html new file mode 100644 index 000000000..61466d420 --- /dev/null +++ b/scripts/theme-studio/theme-studio.html @@ -0,0 +1,738 @@ +theme-studio + +

Untitled: theme

+
+
+

palette

+
+
+
+ + + + + +
+
+
+
+
+
#888888
+
limit
+
+
+
+
+
+

export, import, and save

+
+ +
+
+ + + + +
+ +
+
+

code/color assignments

+
+
+
elements △color △styleexamplecontrast
+
+
+
+

+ 
+
+

ui faces

+
+
+
face △foreground △background △stylepreview
+
+
+
+
+
+
+

package faces

+
+ + + +
+
+
+
face △fg △bg △styleinherit △size △contrast △
+
+
+
+
+
+
+ \ No newline at end of file diff --git a/tests/test-build-theme.el b/tests/test-build-theme.el index 954e95425..f74b94173 100644 --- a/tests/test-build-theme.el +++ b/tests/test-build-theme.el @@ -2,8 +2,8 @@ ;;; Commentary: -;; ERT tests for scripts/theme-selector/build-theme.el, the converter that -;; turns a theme.json exported by the theme-selector into a loadable Emacs +;; ERT tests for scripts/theme-studio/build-theme.el, the converter that +;; turns a theme.json exported by the theme-studio into a loadable Emacs ;; deftheme file. This is the correctness-sensitive end of the pipeline, so ;; it is covered Normal / Boundary / Error per category. @@ -18,7 +18,7 @@ (eval-and-compile (add-to-list 'load-path (expand-file-name - "../scripts/theme-selector" + "../scripts/theme-studio" (file-name-directory (or load-file-name (bound-and-true-p byte-compile-current-file) @@ -55,7 +55,7 @@ "A self-contained theme.json exercising every tier: default, syntax (bold + italic + the unmappable dec key), UI, and packages (a plain face, an inherit+height face, and a cleared face). Owned by the test so it can't drift -the way Craig's downloaded exports under scripts/theme-selector/ can.") +the way Craig's downloaded exports under scripts/theme-studio/ can.") (defun test-build-theme--write-fixture (dir) "Write the fixture JSON into DIR and return its path." -- cgit v1.2.3