diff options
| -rw-r--r-- | docs/design/theme-selector-package-faces-spec.org | 103 | ||||
| -rw-r--r-- | scripts/theme-selector/generate.py | 40 | ||||
| -rw-r--r-- | scripts/theme-selector/theme-selector.html | 18 | ||||
| -rw-r--r-- | todo.org | 2 |
4 files changed, 117 insertions, 46 deletions
diff --git a/docs/design/theme-selector-package-faces-spec.org b/docs/design/theme-selector-package-faces-spec.org index 80a38301..973f3d72 100644 --- a/docs/design/theme-selector-package-faces-spec.org +++ b/docs/design/theme-selector-package-faces-spec.org @@ -45,8 +45,9 @@ A new "package faces" section with: 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, and an optional inherit, all drawing from the same palette as the - other tables. Grouped, with a text filter for the large apps. + 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. @@ -110,7 +111,7 @@ 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}}}=, edited live, rendered into +={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. @@ -207,8 +208,8 @@ a gate. This is the v1 answer to "some will want to touch every package." "ui": {...}, "packages": { "org-mode": { - "org-level-1": {"fg":"#67809c","bg":null,"bold":true,"italic":false,"inherit":null}, - "org-level-2": {"fg":"#e8bd30","bg":null,"bold":false,"italic":false,"inherit":"org-level-1"}, + "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} } } @@ -243,7 +244,8 @@ TDD-worthy part (JSON in, valid faces out), same as the rest of the converter. 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= field). +- 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. @@ -252,15 +254,18 @@ TDD-worthy part (JSON in, valid faces out), same as the rest of the converter. 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,source}}}) - and the =APPS= registry. Extend export/import with the =packages= key; old - JSON (no =packages=) still imports cleanly. No UI yet. +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=. Pure data. + (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; 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=. + 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). @@ -302,7 +307,8 @@ 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, inherit:null, source:"default" } +{ fg:"#67809c", bg:null, bold:true, italic:false, inherit:null, height:1.0, source:"default" } +// 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 @@ -312,19 +318,56 @@ Export policy: - 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, -and a missing =inherit=. 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. +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/source= round-trip through import/export. + =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. @@ -516,3 +559,27 @@ generalized face-control helper, package style kept inside the package object, - *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/scripts/theme-selector/generate.py b/scripts/theme-selector/generate.py index bf1c979b..e8f1f699 100644 --- a/scripts/theme-selector/generate.py +++ b/scripts/theme-selector/generate.py @@ -41,23 +41,23 @@ UIMAP={"cursor":{"fg":None,"bg":"#a9b2bb"},"region":{"fg":None,"bg":"#264364"}, # Tier-3 package faces. Phase 1 ships the schema + an org starter; Phase 2 fills # the complete org/magit/elfeed sets. Defaults reference palette names. APPS={"org-mode":{"label":"org-mode","preview":"org","faces":[ - ["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-document-title","document title",{"fg":"gold","bold":True,"height":1.5}], + ["org-level-1","heading 1",{"fg":"blue","bold":True,"height":1.3}], + ["org-level-2","heading 2",{"fg":"gold","height":1.2}], + ["org-level-3","heading 3",{"fg":"regal","height":1.15}], ["org-todo","TODO keyword",{"fg":"terracotta","bold":True}], ["org-done","DONE keyword",{"fg":"sage","bold":True}], ["org-link","link",{"fg":"blue"}], - ["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-code","inline code",{"fg":"terracotta","inherit":"fixed-pitch"}], + ["org-verbatim","verbatim",{"fg":"steel","inherit":"fixed-pitch"}], + ["org-block","src block body",{"fg":"white","bg":"bg-dim","inherit":"fixed-pitch"}], + ["org-block-begin-line","block delim",{"fg":"pewter","bg":"bg-dim","inherit":"fixed-pitch"}], + ["org-table","table",{"fg":"steel","inherit":"fixed-pitch"}], + ["org-date","timestamp",{"fg":"steel","inherit":"fixed-pitch"}], ["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-meta-line","#+meta line",{"fg":"pewter","inherit":"fixed-pitch"}], + ["org-checkbox","checkbox",{"fg":"gold","inherit":"fixed-pitch"}], ["org-headline-done","done headline",{"fg":"pewter"}] ]}} HTML = """<!doctype html><meta charset=utf-8><title>theme-selector</title> @@ -153,9 +153,9 @@ const SAMPLES=SAMPLES_J, CATS=CATS_J, UI_FACES=UIFACES_J, APPS=APPS_J; let MAP=MAP_J, PALETTE=PALETTE_J, BOLD=BOLD_J, ITALIC={}, UIMAP=UIMAP_J; // --- tier-3 package faces: pure state helpers (Phase 1) --- function pname(n){if(!n)return null;if(/^#/.test(n))return n;const p=PALETTE.find(p=>p[1]===n);return p?p[0]:null;} -function seedPkgmap(){const m={};for(const app in APPS){m[app]={};for(const row of APPS[app].faces){const face=row[0],d=row[2]||{};m[app][face]={fg:pname(d.fg),bg:pname(d.bg),bold:!!d.bold,italic:!!d.italic,inherit:d.inherit||null,source:'default'};}}return m;} -function packagesForExport(map){const out={};for(const app in map){const faces={};for(const face in map[app]){const f=map[app][face];if(f.source==='default'||f.source==='user'||f.source==='cleared'){faces[face]={fg:f.fg,bg:f.bg,bold:f.bold,italic:f.italic,inherit:f.inherit,source:f.source};}}if(Object.keys(faces).length)out[app]=faces;}return out;} -function mergePackagesInto(map,pkgs){if(!pkgs)return;for(const app in pkgs){if(!map[app])map[app]={};for(const face in pkgs[app]){const f=pkgs[app][face]||{};map[app][face]={fg:f.fg??null,bg:f.bg??null,bold:!!f.bold,italic:!!f.italic,inherit:f.inherit??null,source:f.source||'user'};}}} +function seedPkgmap(){const m={};for(const app in APPS){m[app]={};for(const row of APPS[app].faces){const face=row[0],d=row[2]||{};m[app][face]={fg:pname(d.fg),bg:pname(d.bg),bold:!!d.bold,italic:!!d.italic,inherit:d.inherit||null,height:d.height||1,source:'default'};}}return m;} +function packagesForExport(map){const out={};for(const app in map){const faces={};for(const face in map[app]){const f=map[app][face];if(f.source==='default'||f.source==='user'||f.source==='cleared'){const o={fg:f.fg,bg:f.bg,bold:f.bold,italic:f.italic,inherit:f.inherit,source:f.source};if(f.height&&f.height!==1)o.height=f.height;faces[face]=o;}}if(Object.keys(faces).length)out[app]=faces;}return out;} +function mergePackagesInto(map,pkgs){if(!pkgs)return;for(const app in pkgs){if(!map[app])map[app]={};for(const face in pkgs[app]){const f=pkgs[app][face]||{};map[app][face]={fg:f.fg??null,bg:f.bg??null,bold:!!f.bold,italic:!!f.italic,inherit:f.inherit??null,height:f.height||1,source:f.source||'user'};}}} let PKGMAP=seedPkgmap(); function esc(t){return t.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');} function lin(c){c/=255;return c<=0.03928?c/12.92:Math.pow((c+0.055)/1.055,2.4);} @@ -339,15 +339,17 @@ buildLangSel();renderPalette();buildTable();buildUITable();renderCode();applyGro // Phase-1 self-test (open with #selftest): seed -> export -> import -> compare. function pkgSelftest(){ const seeded=seedPkgmap(); - seeded['org-mode']['org-level-2']={fg:'#e8bd30',bg:null,bold:false,italic:false,inherit:'org-level-1',source:'user'}; + seeded['org-mode']['org-level-2']={fg:'#e8bd30',bg:null,bold:false,italic:false,inherit:'org-level-1',height:1.2,source:'user'}; const exp=packagesForExport(seeded); const round=seedPkgmap();mergePackagesInto(round,exp); const roundtrip=JSON.stringify(exp)===JSON.stringify(packagesForExport(round)); let oldjson=true;try{const m=seedPkgmap();mergePackagesInto(m,undefined);oldjson=!!(m['org-mode']&&m['org-mode']['org-todo'].source==='default');}catch(e){oldjson=false;} - const inherited=exp['org-mode']['org-level-2'].inherit==='org-level-1'&&exp['org-mode']['org-level-2'].source==='user'; - const verdict=(roundtrip&&oldjson&&inherited)?'PASS':'FAIL'; + const l2=exp['org-mode']['org-level-2']; + const inherited=l2.inherit==='org-level-1'&&l2.source==='user'; + const height=l2.height===1.2 && !('height' in (exp['org-mode']['org-todo'])); + const verdict=(roundtrip&&oldjson&&inherited&&height)?'PASS':'FAIL'; document.title='SELFTEST '+verdict; - const d=document.createElement('div');d.id='selftest';d.textContent='SELFTEST '+verdict+' roundtrip='+roundtrip+' oldjson='+oldjson+' inherit='+inherited;document.body.appendChild(d); + const d=document.createElement('div');d.id='selftest';d.textContent='SELFTEST '+verdict+' roundtrip='+roundtrip+' oldjson='+oldjson+' inherit='+inherited+' height='+height;document.body.appendChild(d); } if(location.hash==='#selftest')pkgSelftest(); </script>""" diff --git a/scripts/theme-selector/theme-selector.html b/scripts/theme-selector/theme-selector.html index 3cff684e..ebff03b9 100644 --- a/scripts/theme-selector/theme-selector.html +++ b/scripts/theme-selector/theme-selector.html @@ -87,13 +87,13 @@ </section> </div> <script> -const SAMPLES={"Elisp": [[["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", "("], ["fnc", "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", ")"]]], "Go": [[["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\""], ["punc", ")"]], [["p", " "], ["punc", "}"]], [["p", " "], ["var", "q"], ["op", "."], ["prop", "items"], ["p", " "], ["op", "="], ["p", " "], ["fnc", "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", "}"]]], "Python": [[["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"]], [], [["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", "))"]]], "TypeScript": [[["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", ";"]]], "Shell": [[["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", " "], ["str", "\"all done\""]], [["punc", "}"]], [], [["fnc", "main"], ["p", " "], ["str", "\"$@\""]]], "C/C++": [[["cmd", "//"], ["cm", " theme.c"]], [["pp", "#include"], ["p", " "], ["str", "<stdio.h>"]], [["pp", "#define"], ["p", " "], ["con", "MAX_PORT"], ["p", " "], ["num", "8080"]], [], [["kw", "typedef"], ["p", " "], ["kw", "struct"], ["p", " "], ["punc", "{"]], [["p", " "], ["ty", "int"], ["p", " "], ["prop", "id"], ["punc", ";"]], [["p", " "], ["ty", "char"], ["p", " "], ["op", "*"], ["prop", "name"], ["punc", ";"]], [["punc", "}"], ["p", " "], ["ty", "Order"], ["punc", ";"]], [], [["ty", "int"], ["p", " "], ["fnd", "push"], ["punc", "("], ["ty", "Order"], ["p", " "], ["op", "*"], ["var", "o"], ["punc", ")"], ["p", " "], ["punc", "{"]], [["p", " "], ["kw", "if"], ["p", " "], ["punc", "("], ["var", "o"], ["p", " "], ["op", "=="], ["p", " "], ["con", "NULL"], ["punc", ")"], ["p", " "], ["punc", "{"]], [["p", " "], ["kw", "return"], ["p", " "], ["num", "-1"], ["punc", ";"]], [["p", " "], ["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", "}"]], [], [["cmd", "//"], ["cm", " entrypoint"]], [["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"], ["p", " "], ["punc", "}"], ["punc", ";"]], [["p", " "], ["fnc", "push"], ["punc", "("], ["op", "&"], ["var", "o"], ["punc", ")"], ["punc", ";"]], [["p", " "], ["kw", "return"], ["p", " "], ["num", "0"], ["punc", ";"]], [["punc", "}"]]]}, CATS=[["bg", "background (ground)", "Aa Bb 123"], ["p", "fg \u00b7 default text", "other / whitespace"], ["kw", "keyword", "class def if return"], ["bi", "builtin", "len echo printf"], ["pp", "preprocessor", "#include #define"], ["fnd", "function \u00b7 def", "resolve push"], ["fnc", "function \u00b7 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", "|"]], APPS={"org-mode": {"label": "org-mode", "preview": "org", "faces": [["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"}], ["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"}]]}}; +const SAMPLES={"Elisp": [[["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", "("], ["fnc", "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", ")"]]], "Go": [[["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\""], ["punc", ")"]], [["p", " "], ["punc", "}"]], [["p", " "], ["var", "q"], ["op", "."], ["prop", "items"], ["p", " "], ["op", "="], ["p", " "], ["fnc", "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", "}"]]], "Python": [[["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"]], [], [["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", "))"]]], "TypeScript": [[["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", ";"]]], "Shell": [[["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", " "], ["str", "\"all done\""]], [["punc", "}"]], [], [["fnc", "main"], ["p", " "], ["str", "\"$@\""]]], "C/C++": [[["cmd", "//"], ["cm", " theme.c"]], [["pp", "#include"], ["p", " "], ["str", "<stdio.h>"]], [["pp", "#define"], ["p", " "], ["con", "MAX_PORT"], ["p", " "], ["num", "8080"]], [], [["kw", "typedef"], ["p", " "], ["kw", "struct"], ["p", " "], ["punc", "{"]], [["p", " "], ["ty", "int"], ["p", " "], ["prop", "id"], ["punc", ";"]], [["p", " "], ["ty", "char"], ["p", " "], ["op", "*"], ["prop", "name"], ["punc", ";"]], [["punc", "}"], ["p", " "], ["ty", "Order"], ["punc", ";"]], [], [["ty", "int"], ["p", " "], ["fnd", "push"], ["punc", "("], ["ty", "Order"], ["p", " "], ["op", "*"], ["var", "o"], ["punc", ")"], ["p", " "], ["punc", "{"]], [["p", " "], ["kw", "if"], ["p", " "], ["punc", "("], ["var", "o"], ["p", " "], ["op", "=="], ["p", " "], ["con", "NULL"], ["punc", ")"], ["p", " "], ["punc", "{"]], [["p", " "], ["kw", "return"], ["p", " "], ["num", "-1"], ["punc", ";"]], [["p", " "], ["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", "}"]], [], [["cmd", "//"], ["cm", " entrypoint"]], [["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"], ["p", " "], ["punc", "}"], ["punc", ";"]], [["p", " "], ["fnc", "push"], ["punc", "("], ["op", "&"], ["var", "o"], ["punc", ")"], ["punc", ";"]], [["p", " "], ["kw", "return"], ["p", " "], ["num", "0"], ["punc", ";"]], [["punc", "}"]]]}, CATS=[["bg", "background (ground)", "Aa Bb 123"], ["p", "fg \u00b7 default text", "other / whitespace"], ["kw", "keyword", "class def if return"], ["bi", "builtin", "len echo printf"], ["pp", "preprocessor", "#include #define"], ["fnd", "function \u00b7 def", "resolve push"], ["fnc", "function \u00b7 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", "|"]], APPS={"org-mode": {"label": "org-mode", "preview": "org", "faces": [["org-document-title", "document title", {"fg": "gold", "bold": true, "height": 1.5}], ["org-level-1", "heading 1", {"fg": "blue", "bold": true, "height": 1.3}], ["org-level-2", "heading 2", {"fg": "gold", "height": 1.2}], ["org-level-3", "heading 3", {"fg": "regal", "height": 1.15}], ["org-todo", "TODO keyword", {"fg": "terracotta", "bold": true}], ["org-done", "DONE keyword", {"fg": "sage", "bold": true}], ["org-link", "link", {"fg": "blue"}], ["org-code", "inline code", {"fg": "terracotta", "inherit": "fixed-pitch"}], ["org-verbatim", "verbatim", {"fg": "steel", "inherit": "fixed-pitch"}], ["org-block", "src block body", {"fg": "white", "bg": "bg-dim", "inherit": "fixed-pitch"}], ["org-block-begin-line", "block delim", {"fg": "pewter", "bg": "bg-dim", "inherit": "fixed-pitch"}], ["org-table", "table", {"fg": "steel", "inherit": "fixed-pitch"}], ["org-date", "timestamp", {"fg": "steel", "inherit": "fixed-pitch"}], ["org-tag", "tag", {"fg": "tan"}], ["org-special-keyword", "keyword/drawer", {"fg": "pewter"}], ["org-meta-line", "#+meta line", {"fg": "pewter", "inherit": "fixed-pitch"}], ["org-checkbox", "checkbox", {"fg": "gold", "inherit": "fixed-pitch"}], ["org-headline-done", "done headline", {"fg": "pewter"}]]}}; let MAP={"kw": "#67809c", "bi": "#67809c", "pp": "#67809c", "fnd": "#a9b2bb", "fnc": "#a9b2bb", "dec": "#e8bd30", "ty": "#9b5fd0", "prop": "#838d97", "con": "#cb6b4d", "num": "#cb6b4d", "esc": "#cb6b4d", "str": "#5d9b86", "re": "#5d9b86", "doc": "#5d9b86", "cm": "#be9e74", "cmd": "#a9b2bb", "var": "#e8bd30", "op": "#a9b2bb", "punc": "#a9b2bb", "p": "#cdced1", "bg": "#0d0b0a"}, PALETTE=[["#67809c", "blue"], ["#e8bd30", "gold"], ["#9b5fd0", "regal"], ["#2ba178", "emerald"], ["#5d9b86", "sage"], ["#cb6b4d", "terracotta"], ["#be9e74", "tan"], ["#cdced1", "white"], ["#a9b2bb", "silver"], ["#838d97", "steel"], ["#5e6770", "pewter"], ["#2f343a", "gunmetal"], ["#264364", "navy"], ["#0d0b0a", "ground"], ["#1a1714", "bg-dim"]], BOLD={"kw": true, "bi": false, "pp": false, "fnd": true, "fnc": false, "dec": false, "ty": false, "prop": false, "con": false, "num": false, "esc": false, "str": false, "re": false, "doc": false, "cm": false, "cmd": false, "var": false, "op": false, "punc": false, "p": false}, ITALIC={}, UIMAP={"cursor": {"fg": null, "bg": "#a9b2bb"}, "region": {"fg": null, "bg": "#264364"}, "hl-line": {"fg": null, "bg": "#1a1714"}, "highlight": {"fg": null, "bg": "#2f343a"}, "mode-line": {"fg": "#cdced1", "bg": "#2f343a"}, "mode-line-inactive": {"fg": "#838d97", "bg": "#1a1714"}, "fringe": {"fg": null, "bg": "#0d0b0a"}, "line-number": {"fg": "#5e6770", "bg": null}, "line-number-current-line": {"fg": "#e8bd30", "bg": "#1a1714"}, "minibuffer-prompt": {"fg": "#67809c", "bg": null}, "isearch": {"fg": "#0d0b0a", "bg": "#e8bd30"}, "lazy-highlight": {"fg": "#0d0b0a", "bg": "#838d97"}, "isearch-fail": {"fg": "#cb6b4d", "bg": null}, "show-paren-match": {"fg": null, "bg": "#264364"}, "show-paren-mismatch": {"fg": "#0d0b0a", "bg": "#cb6b4d"}, "link": {"fg": "#67809c", "bg": null}, "error": {"fg": "#cb6b4d", "bg": null}, "warning": {"fg": "#e8bd30", "bg": null}, "success": {"fg": "#5d9b86", "bg": null}, "vertical-border": {"fg": "#2f343a", "bg": null}}; // --- tier-3 package faces: pure state helpers (Phase 1) --- function pname(n){if(!n)return null;if(/^#/.test(n))return n;const p=PALETTE.find(p=>p[1]===n);return p?p[0]:null;} -function seedPkgmap(){const m={};for(const app in APPS){m[app]={};for(const row of APPS[app].faces){const face=row[0],d=row[2]||{};m[app][face]={fg:pname(d.fg),bg:pname(d.bg),bold:!!d.bold,italic:!!d.italic,inherit:d.inherit||null,source:'default'};}}return m;} -function packagesForExport(map){const out={};for(const app in map){const faces={};for(const face in map[app]){const f=map[app][face];if(f.source==='default'||f.source==='user'||f.source==='cleared'){faces[face]={fg:f.fg,bg:f.bg,bold:f.bold,italic:f.italic,inherit:f.inherit,source:f.source};}}if(Object.keys(faces).length)out[app]=faces;}return out;} -function mergePackagesInto(map,pkgs){if(!pkgs)return;for(const app in pkgs){if(!map[app])map[app]={};for(const face in pkgs[app]){const f=pkgs[app][face]||{};map[app][face]={fg:f.fg??null,bg:f.bg??null,bold:!!f.bold,italic:!!f.italic,inherit:f.inherit??null,source:f.source||'user'};}}} +function seedPkgmap(){const m={};for(const app in APPS){m[app]={};for(const row of APPS[app].faces){const face=row[0],d=row[2]||{};m[app][face]={fg:pname(d.fg),bg:pname(d.bg),bold:!!d.bold,italic:!!d.italic,inherit:d.inherit||null,height:d.height||1,source:'default'};}}return m;} +function packagesForExport(map){const out={};for(const app in map){const faces={};for(const face in map[app]){const f=map[app][face];if(f.source==='default'||f.source==='user'||f.source==='cleared'){const o={fg:f.fg,bg:f.bg,bold:f.bold,italic:f.italic,inherit:f.inherit,source:f.source};if(f.height&&f.height!==1)o.height=f.height;faces[face]=o;}}if(Object.keys(faces).length)out[app]=faces;}return out;} +function mergePackagesInto(map,pkgs){if(!pkgs)return;for(const app in pkgs){if(!map[app])map[app]={};for(const face in pkgs[app]){const f=pkgs[app][face]||{};map[app][face]={fg:f.fg??null,bg:f.bg??null,bold:!!f.bold,italic:!!f.italic,inherit:f.inherit??null,height:f.height||1,source:f.source||'user'};}}} let PKGMAP=seedPkgmap(); function esc(t){return t.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');} function lin(c){c/=255;return c<=0.03928?c/12.92:Math.pow((c+0.055)/1.055,2.4);} @@ -277,15 +277,17 @@ buildLangSel();renderPalette();buildTable();buildUITable();renderCode();applyGro // Phase-1 self-test (open with #selftest): seed -> export -> import -> compare. function pkgSelftest(){ const seeded=seedPkgmap(); - seeded['org-mode']['org-level-2']={fg:'#e8bd30',bg:null,bold:false,italic:false,inherit:'org-level-1',source:'user'}; + seeded['org-mode']['org-level-2']={fg:'#e8bd30',bg:null,bold:false,italic:false,inherit:'org-level-1',height:1.2,source:'user'}; const exp=packagesForExport(seeded); const round=seedPkgmap();mergePackagesInto(round,exp); const roundtrip=JSON.stringify(exp)===JSON.stringify(packagesForExport(round)); let oldjson=true;try{const m=seedPkgmap();mergePackagesInto(m,undefined);oldjson=!!(m['org-mode']&&m['org-mode']['org-todo'].source==='default');}catch(e){oldjson=false;} - const inherited=exp['org-mode']['org-level-2'].inherit==='org-level-1'&&exp['org-mode']['org-level-2'].source==='user'; - const verdict=(roundtrip&&oldjson&&inherited)?'PASS':'FAIL'; + const l2=exp['org-mode']['org-level-2']; + const inherited=l2.inherit==='org-level-1'&&l2.source==='user'; + const height=l2.height===1.2 && !('height' in (exp['org-mode']['org-todo'])); + const verdict=(roundtrip&&oldjson&&inherited&&height)?'PASS':'FAIL'; document.title='SELFTEST '+verdict; - const d=document.createElement('div');d.id='selftest';d.textContent='SELFTEST '+verdict+' roundtrip='+roundtrip+' oldjson='+oldjson+' inherit='+inherited;document.body.appendChild(d); + const d=document.createElement('div');d.id='selftest';d.textContent='SELFTEST '+verdict+' roundtrip='+roundtrip+' oldjson='+oldjson+' inherit='+inherited+' height='+height;document.body.appendChild(d); } if(location.hash==='#selftest')pkgSelftest(); </script>
\ No newline at end of file @@ -67,7 +67,7 @@ Full design + methodology + starting palette + open questions in the spec: [[fil Package-specific face editing in the theme-selector: org/magit/elfeed bespoke (complete face tables + live previews) plus a generated all-package inventory so every installed package is themeable. Spec is Ready, all opens resolved: [[file:docs/design/theme-selector-package-faces-spec.org][docs/design/theme-selector-package-faces-spec.org]]. Phases below run in dependency order; phases 1-5 deliver the three high-value apps, phase 6 opens the long tail, phase 7 documents. The =theme.json= -> =dupre-*.el= converter (Elisp) is a separate downstream task. *** 2026-06-08 Mon @ 00:17:41 -0500 Phase 1 — package state + schema landed -Added =APPS= (org starter) and =PKGMAP= ({app:{face:{fg,bg,bold,italic,inherit,source}}}), pure helpers (=seedPkgmap= / =packagesForExport= / =mergePackagesInto=), and wired export/import for the =packages= key with old-JSON compat. No UI yet (Phase 3). Verified: node-check, plus a guarded =#selftest= harness (headless Chrome) confirming seed->export->import round-trip, old-JSON merge, and inherit/source survival — all PASS. +Added =APPS= (org starter) and =PKGMAP= ({app:{face:{fg,bg,bold,italic,inherit,height,source}}}), pure helpers (=seedPkgmap= / =packagesForExport= / =mergePackagesInto=), and wired export/import for the =packages= key with old-JSON compat. The =height= float (relative size, read off the face not cascaded through inherit) and the fixed-pitch inherits are seeded in the org starter. No UI yet (Phase 3). Verified: node-check, plus a guarded =#selftest= harness (headless Chrome) confirming seed->export->import round-trip, old-JSON merge, and inherit/height/source survival — all PASS. *** TODO [#B] Phase 2 — curated app data (org/magit/elfeed) :solo: Complete own-defface face lists + seeded defaults modeling org's real inherit cascade, for org (incl. org-agenda), magit, elfeed, in =APPS=. Pure data. Verify: each app lists its full grouped set. Seeded-default aesthetics go to Manual testing. Spec phase 2. |
