aboutsummaryrefslogtreecommitdiff
path: root/scripts/theme-selector
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-07 17:32:34 -0500
committerCraig Jennings <c@cjennings.net>2026-06-07 17:32:34 -0500
commit452380268034959e0b2d8052b6593a5ba802cf42 (patch)
treec26434f5f02a36220a80251bc5ff66438c23bdb5 /scripts/theme-selector
parenteea80c78cfce99b504b60acab8509fb49999bdc7 (diff)
downloaddotemacs-452380268034959e0b2d8052b6593a5ba802cf42.tar.gz
dotemacs-452380268034959e0b2d8052b6593a5ba802cf42.zip
feat(theme-selector): add a live mock Emacs frame for the interface faces
I split the interface-faces row into the face table on the left and a mock Emacs buffer on the right. The mock paints the faces in context: line numbers and fringe, a current line with hl-line and the current-line number, a region selection, an isearch match, a lazy-highlight, a show-paren match, a block cursor, active and inactive mode-lines, and a minibuffer prompt with an echo line for link, error, warning, and success. It rebuilds whenever a syntax or interface color changes, so it always reflects the current theme. The face table told you each color. The mock shows what they look like together in a real buffer.
Diffstat (limited to 'scripts/theme-selector')
-rw-r--r--scripts/theme-selector/generate.py52
-rw-r--r--scripts/theme-selector/theme-selector.html52
2 files changed, 100 insertions, 4 deletions
diff --git a/scripts/theme-selector/generate.py b/scripts/theme-selector/generate.py
index 087920d4..8a89f6b1 100644
--- a/scripts/theme-selector/generate.py
+++ b/scripts/theme-selector/generate.py
@@ -70,6 +70,10 @@ HTML = """<!doctype html><meta charset=utf-8><title>theme-selector</title>
.pane h1{margin-top:0}
.filebar.end{justify-content:flex-end} .langbar{margin-bottom:10px;display:flex;gap:8px;align-items:center}
#codepre{width:100%;box-sizing:border-box}
+ .mock{border:1px solid #252321;border-radius:8px;overflow:hidden;font:15px/1.7 monospace}
+ .mock .ln{display:flex;align-items:stretch;white-space:pre}
+ .mock .fr{width:10px;flex:0 0 auto} .mock .num{width:36px;flex:0 0 auto;text-align:right;padding-right:10px}
+ .mock .cd{flex:1;padding-left:8px} .mock .bar,.mock .echo{padding:4px 10px;white-space:pre}
</style>
<h1 id="pagetitle">Untitled: theme</h1>
<div class="cols">
@@ -107,7 +111,15 @@ HTML = """<!doctype html><meta charset=utf-8><title>theme-selector</title>
</section>
</div>
<h1>interface faces</h1>
-<table class="leg" id="uitable"><thead><tr><th>face</th><th>foreground</th><th>background</th><th>preview</th></tr></thead><tbody id="uibody"></tbody></table>
+<div class="cols">
+ <section class="pane">
+ <table class="leg" id="uitable"><thead><tr><th>face</th><th>foreground</th><th>background</th><th>preview</th></tr></thead><tbody id="uibody"></tbody></table>
+ </section>
+ <section class="pane grow">
+ <div class="langbar"><label style="color:#b4b1a2">mock frame — the faces in a live buffer</label></div>
+ <div id="mockframe" class="mock"></div>
+ </section>
+</div>
<script>
const SAMPLES=SAMPLES_J, CATS=CATS_J, UI_FACES=UIFACES_J;
let MAP=MAP_J, PALETTE=PALETTE_J, BOLD=BOLD_J, ITALIC={}, UIMAP=UIMAP_J;
@@ -128,6 +140,7 @@ function renderCode(){
html+=`<span style="color:${c};font-weight:${w};font-style:${s}">${esc(t)}</span>`;}
html+='\\n';}
document.getElementById('codepre').innerHTML=html;
+ buildMockFrame();
}
function buildTable(){
const tb=document.getElementById('legbody');tb.innerHTML='';
@@ -192,6 +205,41 @@ function importFile(ev){const f=ev.target.files[0];if(!f)return;const r=new File
renderPalette();buildTable();buildUITable();renderCode();applyGround();updateTitle();}catch(e){alert('bad theme file: '+e.message);}};
r.readAsText(f);ev.target.value='';}
function applyGround(){document.querySelectorAll('pre').forEach(p=>p.style.background=MAP['bg']);document.querySelectorAll('.ex').forEach(e=>e.style.background=MAP['bg']);}
+function uf(f){return UIMAP[f]||{};}
+function mockSpan(k,t){return `<span style="color:${MAP[k]||MAP['p']};font-weight:${BOLD[k]?'bold':'normal'};font-style:${ITALIC[k]?'italic':'normal'}">${esc(t)}</span>`;}
+function buildMockFrame(){
+ const fr=document.getElementById('mockframe');if(!fr)return;
+ const bg=MAP['bg'],fg=MAP['p'];
+ const ln=uf('line-number'),lnc=uf('line-number-current-line'),hl=uf('hl-line'),reg=uf('region'),isr=uf('isearch'),laz=uf('lazy-highlight'),par=uf('show-paren-match'),cur=uf('cursor'),ml=uf('mode-line'),mli=uf('mode-line-inactive'),mb=uf('minibuffer-prompt'),frng=uf('fringe'),lnk=uf('link'),err=uf('error'),wrn=uf('warning'),suc=uf('success');
+ const lines=[
+ {t:[['cmd',';; '],['cm','init.el - your config']]},
+ {t:[['punc','('],['kw','defun'],['p',' '],['fnd','cj/greet'],['p',' '],['punc','('],['var','name'],['punc',')']]},
+ {t:[['p',' '],['punc','('],['fnc','message'],['p',' '],['str','"hi %s"'],['p',' '],['var','name'],['punc','))']],cur:1},
+ {t:[['p',' '],['punc','('],['kw','setq'],['p',' '],['var','count'],['p',' '],['num','42'],['punc',')']],region:1},
+ {plain:' (if (> count 0)',match:1},
+ {t:[['p',' '],['punc','('],['fnc','process'],['p',' '],['var','items'],['punc',')']]},
+ {plain:' (cl-incf count)',lazy:1},
+ {t:[['p',' '],['punc','('],['kw','setq'],['p',' '],['var','done'],['p',' '],['con','t'],['punc',')']],paren:1}
+ ];
+ let html='';
+ lines.forEach((L,i)=>{
+ const isc=L.cur;
+ const nFg=isc?(lnc.fg||fg):(ln.fg||fg), nBg=isc?(lnc.bg||'transparent'):(ln.bg||'transparent');
+ const rowBg=isc?(hl.bg||'transparent'):'transparent';
+ let cd;
+ if(L.plain&&L.match){cd=`<span style="color:${isr.fg||fg};background:${isr.bg||'transparent'}">${esc(L.plain)}</span>`;}
+ else if(L.plain&&L.lazy){cd=`<span style="color:${laz.fg||fg};background:${laz.bg||'transparent'}">${esc(L.plain)}</span>`;}
+ else if(L.paren){cd=L.t.map(([k,t],j)=>j===L.t.length-1?`<span style="background:${par.bg||'transparent'};color:${par.fg||MAP[k]||fg};font-weight:bold">${esc(t)}</span>`:mockSpan(k,t)).join('');}
+ else{cd=L.t.map(([k,t])=>mockSpan(k,t)).join('');if(L.region)cd=`<span style="background:${reg.bg||'transparent'}">${cd}</span>`;}
+ if(isc)cd+=`<span style="background:${cur.bg||fg};color:${bg}"> </span>`;
+ html+=`<div class="ln" style="background:${rowBg}"><span class="fr" style="background:${frng.bg||bg}"></span><span class="num" style="color:${nFg};background:${nBg}">${i+1}</span><span class="cd">${cd}</span></div>`;
+ });
+ html+=`<div class="bar" style="background:${ml.bg||fg};color:${ml.fg||bg}"> init.el (Emacs Lisp) L3 git:main </div>`;
+ html+=`<div class="bar" style="background:${mli.bg||bg};color:${mli.fg||fg}"> *Messages* (Fundamental) </div>`;
+ html+=`<div class="echo" style="color:${fg}"><span style="color:${mb.fg||fg}">I-search:</span> count</div>`;
+ html+=`<div class="echo"><span style="color:${lnk.fg||fg};text-decoration:underline">https://gnu.org</span> <span style="color:${err.fg||fg}">error</span> <span style="color:${wrn.fg||fg}">warning</span> <span style="color:${suc.fg||fg}">ok</span></div>`;
+ fr.innerHTML=html;fr.style.background=bg;fr.style.color=fg;
+}
function uiSelect(face,attr){
const sel=document.createElement('select');sel.className='chip';
const none=document.createElement('option');none.value='';none.textContent='— none —';none.style.background='#161412';none.style.color='#b4b1a2';sel.appendChild(none);
@@ -199,7 +247,7 @@ function uiSelect(face,attr){
sel.value=UIMAP[face][attr]||'';
function style(){if(sel.value){sel.style.background=sel.value;sel.style.color=textOn(sel.value);}else{sel.style.background='#161412';sel.style.color='#b4b1a2';}}
style();
- sel.onchange=()=>{UIMAP[face][attr]=sel.value||null;style();paintUI(face);};
+ sel.onchange=()=>{UIMAP[face][attr]=sel.value||null;style();paintUI(face);buildMockFrame();};
return sel;
}
function paintUI(face){const pv=document.getElementById('uiprev-'+face);if(!pv)return;pv.style.color=UIMAP[face].fg||MAP['p'];pv.style.background=UIMAP[face].bg||MAP['bg'];}
diff --git a/scripts/theme-selector/theme-selector.html b/scripts/theme-selector/theme-selector.html
index cda479fa..a4818c5a 100644
--- a/scripts/theme-selector/theme-selector.html
+++ b/scripts/theme-selector/theme-selector.html
@@ -30,6 +30,10 @@
.pane h1{margin-top:0}
.filebar.end{justify-content:flex-end} .langbar{margin-bottom:10px;display:flex;gap:8px;align-items:center}
#codepre{width:100%;box-sizing:border-box}
+ .mock{border:1px solid #252321;border-radius:8px;overflow:hidden;font:15px/1.7 monospace}
+ .mock .ln{display:flex;align-items:stretch;white-space:pre}
+ .mock .fr{width:10px;flex:0 0 auto} .mock .num{width:36px;flex:0 0 auto;text-align:right;padding-right:10px}
+ .mock .cd{flex:1;padding-left:8px} .mock .bar,.mock .echo{padding:4px 10px;white-space:pre}
</style>
<h1 id="pagetitle">Untitled: theme</h1>
<div class="cols">
@@ -67,7 +71,15 @@
</section>
</div>
<h1>interface faces</h1>
-<table class="leg" id="uitable"><thead><tr><th>face</th><th>foreground</th><th>background</th><th>preview</th></tr></thead><tbody id="uibody"></tbody></table>
+<div class="cols">
+ <section class="pane">
+ <table class="leg" id="uitable"><thead><tr><th>face</th><th>foreground</th><th>background</th><th>preview</th></tr></thead><tbody id="uibody"></tbody></table>
+ </section>
+ <section class="pane grow">
+ <div class="langbar"><label style="color:#b4b1a2">mock frame — the faces in a live buffer</label></div>
+ <div id="mockframe" class="mock"></div>
+ </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", "|"]];
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}};
@@ -88,6 +100,7 @@ function renderCode(){
html+=`<span style="color:${c};font-weight:${w};font-style:${s}">${esc(t)}</span>`;}
html+='\n';}
document.getElementById('codepre').innerHTML=html;
+ buildMockFrame();
}
function buildTable(){
const tb=document.getElementById('legbody');tb.innerHTML='';
@@ -152,6 +165,41 @@ function importFile(ev){const f=ev.target.files[0];if(!f)return;const r=new File
renderPalette();buildTable();buildUITable();renderCode();applyGround();updateTitle();}catch(e){alert('bad theme file: '+e.message);}};
r.readAsText(f);ev.target.value='';}
function applyGround(){document.querySelectorAll('pre').forEach(p=>p.style.background=MAP['bg']);document.querySelectorAll('.ex').forEach(e=>e.style.background=MAP['bg']);}
+function uf(f){return UIMAP[f]||{};}
+function mockSpan(k,t){return `<span style="color:${MAP[k]||MAP['p']};font-weight:${BOLD[k]?'bold':'normal'};font-style:${ITALIC[k]?'italic':'normal'}">${esc(t)}</span>`;}
+function buildMockFrame(){
+ const fr=document.getElementById('mockframe');if(!fr)return;
+ const bg=MAP['bg'],fg=MAP['p'];
+ const ln=uf('line-number'),lnc=uf('line-number-current-line'),hl=uf('hl-line'),reg=uf('region'),isr=uf('isearch'),laz=uf('lazy-highlight'),par=uf('show-paren-match'),cur=uf('cursor'),ml=uf('mode-line'),mli=uf('mode-line-inactive'),mb=uf('minibuffer-prompt'),frng=uf('fringe'),lnk=uf('link'),err=uf('error'),wrn=uf('warning'),suc=uf('success');
+ const lines=[
+ {t:[['cmd',';; '],['cm','init.el - your config']]},
+ {t:[['punc','('],['kw','defun'],['p',' '],['fnd','cj/greet'],['p',' '],['punc','('],['var','name'],['punc',')']]},
+ {t:[['p',' '],['punc','('],['fnc','message'],['p',' '],['str','"hi %s"'],['p',' '],['var','name'],['punc','))']],cur:1},
+ {t:[['p',' '],['punc','('],['kw','setq'],['p',' '],['var','count'],['p',' '],['num','42'],['punc',')']],region:1},
+ {plain:' (if (> count 0)',match:1},
+ {t:[['p',' '],['punc','('],['fnc','process'],['p',' '],['var','items'],['punc',')']]},
+ {plain:' (cl-incf count)',lazy:1},
+ {t:[['p',' '],['punc','('],['kw','setq'],['p',' '],['var','done'],['p',' '],['con','t'],['punc',')']],paren:1}
+ ];
+ let html='';
+ lines.forEach((L,i)=>{
+ const isc=L.cur;
+ const nFg=isc?(lnc.fg||fg):(ln.fg||fg), nBg=isc?(lnc.bg||'transparent'):(ln.bg||'transparent');
+ const rowBg=isc?(hl.bg||'transparent'):'transparent';
+ let cd;
+ if(L.plain&&L.match){cd=`<span style="color:${isr.fg||fg};background:${isr.bg||'transparent'}">${esc(L.plain)}</span>`;}
+ else if(L.plain&&L.lazy){cd=`<span style="color:${laz.fg||fg};background:${laz.bg||'transparent'}">${esc(L.plain)}</span>`;}
+ else if(L.paren){cd=L.t.map(([k,t],j)=>j===L.t.length-1?`<span style="background:${par.bg||'transparent'};color:${par.fg||MAP[k]||fg};font-weight:bold">${esc(t)}</span>`:mockSpan(k,t)).join('');}
+ else{cd=L.t.map(([k,t])=>mockSpan(k,t)).join('');if(L.region)cd=`<span style="background:${reg.bg||'transparent'}">${cd}</span>`;}
+ if(isc)cd+=`<span style="background:${cur.bg||fg};color:${bg}"> </span>`;
+ html+=`<div class="ln" style="background:${rowBg}"><span class="fr" style="background:${frng.bg||bg}"></span><span class="num" style="color:${nFg};background:${nBg}">${i+1}</span><span class="cd">${cd}</span></div>`;
+ });
+ html+=`<div class="bar" style="background:${ml.bg||fg};color:${ml.fg||bg}"> init.el (Emacs Lisp) L3 git:main </div>`;
+ html+=`<div class="bar" style="background:${mli.bg||bg};color:${mli.fg||fg}"> *Messages* (Fundamental) </div>`;
+ html+=`<div class="echo" style="color:${fg}"><span style="color:${mb.fg||fg}">I-search:</span> count</div>`;
+ html+=`<div class="echo"><span style="color:${lnk.fg||fg};text-decoration:underline">https://gnu.org</span> <span style="color:${err.fg||fg}">error</span> <span style="color:${wrn.fg||fg}">warning</span> <span style="color:${suc.fg||fg}">ok</span></div>`;
+ fr.innerHTML=html;fr.style.background=bg;fr.style.color=fg;
+}
function uiSelect(face,attr){
const sel=document.createElement('select');sel.className='chip';
const none=document.createElement('option');none.value='';none.textContent='— none —';none.style.background='#161412';none.style.color='#b4b1a2';sel.appendChild(none);
@@ -159,7 +207,7 @@ function uiSelect(face,attr){
sel.value=UIMAP[face][attr]||'';
function style(){if(sel.value){sel.style.background=sel.value;sel.style.color=textOn(sel.value);}else{sel.style.background='#161412';sel.style.color='#b4b1a2';}}
style();
- sel.onchange=()=>{UIMAP[face][attr]=sel.value||null;style();paintUI(face);};
+ sel.onchange=()=>{UIMAP[face][attr]=sel.value||null;style();paintUI(face);buildMockFrame();};
return sel;
}
function paintUI(face){const pv=document.getElementById('uiprev-'+face);if(!pv)return;pv.style.color=UIMAP[face].fg||MAP['p'];pv.style.background=UIMAP[face].bg||MAP['bg'];}