diff options
Diffstat (limited to 'scripts/theme-selector')
| -rw-r--r-- | scripts/theme-selector/generate.py | 32 | ||||
| -rw-r--r-- | scripts/theme-selector/samples.py | 2 | ||||
| -rw-r--r-- | scripts/theme-selector/theme-selector.html | 28 |
3 files changed, 41 insertions, 21 deletions
diff --git a/scripts/theme-selector/generate.py b/scripts/theme-selector/generate.py index a4de26a8..2f4a3f80 100644 --- a/scripts/theme-selector/generate.py +++ b/scripts/theme-selector/generate.py @@ -5,10 +5,10 @@ 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'],"Shell":ns['SHS'],"C/C++":ns['CS']} 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']='#0d0b0a' +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"],["#cdced1","white"],["#a9b2bb","silver"],["#838d97","steel"], - ["#5e6770","pewter"],["#2f343a","gunmetal"],["#264364","navy"],["#0d0b0a","ground"],["#1a1714","bg-dim"]] + ["#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"], @@ -80,6 +80,7 @@ HTML = """<!doctype html><meta charset=utf-8><title>theme-selector</title> .pchip.drag{opacity:.4} .pchip.sel{outline:3px solid #e8bd30;outline-offset:2px} .pchip.over{outline:2px dashed #e8bd30;outline-offset:1px} .pchip input.nm{background:transparent;border:none;text-align:center;font:bold 10pt monospace;width:108px;outline:none} .pchip .mv{position:absolute;bottom:-1px;background:none;border:none;cursor:pointer;font-size:22px;line-height:1;font-weight:bold;opacity:.5;padding:0 5px} .pchip .mv:hover{opacity:1} .pchip .mv.l{left:0} .pchip .mv.r{right:0} .pchip .hx{font-size:10pt;opacity:.8} .pchip .rm{position:absolute;top:2px;right:5px;background:none;border:none;cursor:pointer;font-size:14px;font-weight:bold;opacity:.7} + .pchip .lock{position:absolute;top:3px;right:5px;font-size:10px;opacity:.6} .palctl{margin-top:12px;display:flex;gap:8px;align-items:center;flex-wrap:wrap} .palctl input[type=text]{background:#161412;border:1px solid #252321;color:#cdced1;border-radius:4px;padding:5px 8px;font:10pt monospace} .palctl input[type=text]::placeholder{color:#b4b1a2;opacity:1} @@ -139,9 +140,10 @@ HTML = """<!doctype html><meta charset=utf-8><title>theme-selector</title> <label style="color:#b4b1a2">theme name</label><input type="text" id="themename" value="" placeholder="untitled" oninput="updateTitle()" style="background:#161412;border:1px solid #252321;color:#cdced1;border-radius:4px;padding:5px 8px;font:10pt monospace;width:200px"> </div> <div class="filebar end"> - <button onclick="download()">⬇ download <name>.json</button> - <label class="fbtn">⬆ load theme.json<input type="file" accept=".json" onchange="importFile(event)" style="display:none"></label> - <button id="jsonbtn" onclick="toggleJSON()">show JSON</button> + <button id="savebtn" onclick="saveTheme()" style="display:none">💾 save</button> + <button onclick="exportTheme()">⬇ export</button> + <label class="fbtn">⬆ import<input type="file" accept=".json" onchange="importFile(event)" style="display:none"></label> + <button id="jsonbtn" onclick="toggleJSON()">show</button> </div> <textarea id="export" style="display:none" readonly></textarea> </section> @@ -229,11 +231,13 @@ let dragFrom=null,selectedIdx=null; function renderPalette(){ const p=document.getElementById('pals');p.innerHTML=''; PALETTE.forEach((pc,i)=>{const [hex,name]=pc;const tc=textOn(hex); + const locked=(hex===MAP['bg']||hex===MAP['p']); const d=document.createElement('div');d.className='pchip'+(i===selectedIdx?' sel':'');d.style.background=hex;d.draggable=true; const lft=i>0?`<button class="mv l" title="move left" style="color:${tc}">‹</button>`:''; const rgt=i<PALETTE.length-1?`<button class="mv r" title="move right" style="color:${tc}">›</button>`:''; - d.innerHTML=`<button class="rm" title="remove" style="color:${tc}">×</button>${lft}${rgt}<input class="nm" value="${name}" style="color:${tc}"><div class="hx" style="color:${tc}">${hex}</div>`; - d.querySelector('.rm').onclick=(e)=>{e.stopPropagation();PALETTE.splice(i,1);if(selectedIdx===i)selectedIdx=null;renderPalette();buildTable();buildUITable();}; + const rm=locked?`<span class="lock" title="${hex===MAP['bg']?'background':'foreground'} — can't remove" style="color:${tc}">🔒</span>`:`<button class="rm" title="remove" style="color:${tc}">×</button>`; + d.innerHTML=`${rm}${lft}${rgt}<input class="nm" value="${name}" style="color:${tc}"><div class="hx" style="color:${tc}">${hex}</div>`; + if(!locked)d.querySelector('.rm').onclick=(e)=>{e.stopPropagation();PALETTE.splice(i,1);if(selectedIdx===i)selectedIdx=null;renderPalette();buildTable();buildUITable();}; if(lft)d.querySelector('.mv.l').onclick=(e)=>{e.stopPropagation();moveColor(i,-1);}; if(rgt)d.querySelector('.mv.r').onclick=(e)=>{e.stopPropagation();moveColor(i,1);}; d.querySelector('.nm').onchange=(e)=>{PALETTE[i][1]=e.target.value;buildTable();buildUITable();}; @@ -289,9 +293,15 @@ function themeName(){return (document.getElementById('themename').value||'theme' function fileSlug(){return themeName().replace(/[^A-Za-z0-9._-]+/g,'-').replace(/^-+|-+$/g,'')||'theme';} function exportObj(){const a={};CATS.forEach(c=>a[c[0]]=MAP[c[0]]);const o={name:themeName(),palette:PALETTE,assignments:a,bold:Object.keys(BOLD).filter(k=>BOLD[k]),italic:Object.keys(ITALIC).filter(k=>ITALIC[k]),ui:UIMAP};const pk=packagesForExport(PKGMAP);if(Object.keys(pk).length)o.packages=pk;return o;} function exportState(){const t=document.getElementById('export');t.value=JSON.stringify(exportObj(),null,1);t.style.display='block';t.focus();t.select();} -function toggleJSON(){const t=document.getElementById('export'),b=document.getElementById('jsonbtn');if(t.style.display==='block'){t.style.display='none';b.textContent='show JSON';}else{exportState();b.textContent='hide JSON';}} -function updateTitle(){const n=document.getElementById('themename').value.trim();document.getElementById('pagetitle').textContent=(n||'Untitled')+': theme';} -function download(){const blob=new Blob([JSON.stringify(exportObj(),null,1)],{type:'application/json'});const a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download=fileSlug()+'.json';a.click();} +function toggleJSON(){const t=document.getElementById('export'),b=document.getElementById('jsonbtn');if(t.style.display==='block'){t.style.display='none';b.textContent='show';}else{exportState();b.textContent='hide';}} +function updateTitle(){const n=document.getElementById('themename').value.trim();document.getElementById('pagetitle').textContent=(n||'Untitled')+': theme';const sb=document.getElementById('savebtn');if(sb)sb.style.display=n?'':'none';fileHandle=null;} +let fileHandle=null; +function exportTheme(){const blob=new Blob([JSON.stringify(exportObj(),null,1)],{type:'application/json'});const a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download=fileSlug()+'.json';a.click();} +async function saveTheme(){const data=JSON.stringify(exportObj(),null,1); + if(!window.showSaveFilePicker){exportTheme();notify('saved via download (browser has no Save-File support)',false);return;} + try{if(!fileHandle)fileHandle=await window.showSaveFilePicker({suggestedName:fileSlug()+'.json',types:[{description:'theme JSON',accept:{'application/json':['.json']}}]}); + const w=await fileHandle.createWritable();await w.write(data);await w.close();notify('saved "'+themeName()+'"',false); + }catch(e){if(e&&e.name!=='AbortError')notify('save failed: '+e.message,true);}} function importFile(ev){const f=ev.target.files[0];if(!f)return;const r=new FileReader(); r.onload=()=>{try{const d=JSON.parse(r.result);if(d.name)document.getElementById('themename').value=d.name;if(d.palette)PALETTE=d.palette;if(d.assignments)Object.assign(MAP,d.assignments); BOLD={};(d.bold||[]).forEach(k=>BOLD[k]=true);ITALIC={};(d.italic||[]).forEach(k=>ITALIC[k]=true); diff --git a/scripts/theme-selector/samples.py b/scripts/theme-selector/samples.py index 41307a1c..38d57092 100644 --- a/scripts/theme-selector/samples.py +++ b/scripts/theme-selector/samples.py @@ -6,7 +6,7 @@ COLS={ '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':("#cdced1",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(">",">") diff --git a/scripts/theme-selector/theme-selector.html b/scripts/theme-selector/theme-selector.html index eb7209bb..03307b6c 100644 --- a/scripts/theme-selector/theme-selector.html +++ b/scripts/theme-selector/theme-selector.html @@ -18,6 +18,7 @@ .pchip.drag{opacity:.4} .pchip.sel{outline:3px solid #e8bd30;outline-offset:2px} .pchip.over{outline:2px dashed #e8bd30;outline-offset:1px} .pchip input.nm{background:transparent;border:none;text-align:center;font:bold 10pt monospace;width:108px;outline:none} .pchip .mv{position:absolute;bottom:-1px;background:none;border:none;cursor:pointer;font-size:22px;line-height:1;font-weight:bold;opacity:.5;padding:0 5px} .pchip .mv:hover{opacity:1} .pchip .mv.l{left:0} .pchip .mv.r{right:0} .pchip .hx{font-size:10pt;opacity:.8} .pchip .rm{position:absolute;top:2px;right:5px;background:none;border:none;cursor:pointer;font-size:14px;font-weight:bold;opacity:.7} + .pchip .lock{position:absolute;top:3px;right:5px;font-size:10px;opacity:.6} .palctl{margin-top:12px;display:flex;gap:8px;align-items:center;flex-wrap:wrap} .palctl input[type=text]{background:#161412;border:1px solid #252321;color:#cdced1;border-radius:4px;padding:5px 8px;font:10pt monospace} .palctl input[type=text]::placeholder{color:#b4b1a2;opacity:1} @@ -77,9 +78,10 @@ <label style="color:#b4b1a2">theme name</label><input type="text" id="themename" value="" placeholder="untitled" oninput="updateTitle()" style="background:#161412;border:1px solid #252321;color:#cdced1;border-radius:4px;padding:5px 8px;font:10pt monospace;width:200px"> </div> <div class="filebar end"> - <button onclick="download()">⬇ download <name>.json</button> - <label class="fbtn">⬆ load theme.json<input type="file" accept=".json" onchange="importFile(event)" style="display:none"></label> - <button id="jsonbtn" onclick="toggleJSON()">show JSON</button> + <button id="savebtn" onclick="saveTheme()" style="display:none">💾 save</button> + <button onclick="exportTheme()">⬇ export</button> + <label class="fbtn">⬆ import<input type="file" accept=".json" onchange="importFile(event)" style="display:none"></label> + <button id="jsonbtn" onclick="toggleJSON()">show</button> </div> <textarea id="export" style="display:none" readonly></textarea> </section> @@ -106,7 +108,7 @@ </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, "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}}; +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": "#ffffff", "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"]], 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,height:d.height||1,source:'default'};}}return m;} @@ -167,11 +169,13 @@ let dragFrom=null,selectedIdx=null; function renderPalette(){ const p=document.getElementById('pals');p.innerHTML=''; PALETTE.forEach((pc,i)=>{const [hex,name]=pc;const tc=textOn(hex); + const locked=(hex===MAP['bg']||hex===MAP['p']); const d=document.createElement('div');d.className='pchip'+(i===selectedIdx?' sel':'');d.style.background=hex;d.draggable=true; const lft=i>0?`<button class="mv l" title="move left" style="color:${tc}">‹</button>`:''; const rgt=i<PALETTE.length-1?`<button class="mv r" title="move right" style="color:${tc}">›</button>`:''; - d.innerHTML=`<button class="rm" title="remove" style="color:${tc}">×</button>${lft}${rgt}<input class="nm" value="${name}" style="color:${tc}"><div class="hx" style="color:${tc}">${hex}</div>`; - d.querySelector('.rm').onclick=(e)=>{e.stopPropagation();PALETTE.splice(i,1);if(selectedIdx===i)selectedIdx=null;renderPalette();buildTable();buildUITable();}; + const rm=locked?`<span class="lock" title="${hex===MAP['bg']?'background':'foreground'} — can't remove" style="color:${tc}">🔒</span>`:`<button class="rm" title="remove" style="color:${tc}">×</button>`; + d.innerHTML=`${rm}${lft}${rgt}<input class="nm" value="${name}" style="color:${tc}"><div class="hx" style="color:${tc}">${hex}</div>`; + if(!locked)d.querySelector('.rm').onclick=(e)=>{e.stopPropagation();PALETTE.splice(i,1);if(selectedIdx===i)selectedIdx=null;renderPalette();buildTable();buildUITable();}; if(lft)d.querySelector('.mv.l').onclick=(e)=>{e.stopPropagation();moveColor(i,-1);}; if(rgt)d.querySelector('.mv.r').onclick=(e)=>{e.stopPropagation();moveColor(i,1);}; d.querySelector('.nm').onchange=(e)=>{PALETTE[i][1]=e.target.value;buildTable();buildUITable();}; @@ -227,9 +231,15 @@ function themeName(){return (document.getElementById('themename').value||'theme' function fileSlug(){return themeName().replace(/[^A-Za-z0-9._-]+/g,'-').replace(/^-+|-+$/g,'')||'theme';} function exportObj(){const a={};CATS.forEach(c=>a[c[0]]=MAP[c[0]]);const o={name:themeName(),palette:PALETTE,assignments:a,bold:Object.keys(BOLD).filter(k=>BOLD[k]),italic:Object.keys(ITALIC).filter(k=>ITALIC[k]),ui:UIMAP};const pk=packagesForExport(PKGMAP);if(Object.keys(pk).length)o.packages=pk;return o;} function exportState(){const t=document.getElementById('export');t.value=JSON.stringify(exportObj(),null,1);t.style.display='block';t.focus();t.select();} -function toggleJSON(){const t=document.getElementById('export'),b=document.getElementById('jsonbtn');if(t.style.display==='block'){t.style.display='none';b.textContent='show JSON';}else{exportState();b.textContent='hide JSON';}} -function updateTitle(){const n=document.getElementById('themename').value.trim();document.getElementById('pagetitle').textContent=(n||'Untitled')+': theme';} -function download(){const blob=new Blob([JSON.stringify(exportObj(),null,1)],{type:'application/json'});const a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download=fileSlug()+'.json';a.click();} +function toggleJSON(){const t=document.getElementById('export'),b=document.getElementById('jsonbtn');if(t.style.display==='block'){t.style.display='none';b.textContent='show';}else{exportState();b.textContent='hide';}} +function updateTitle(){const n=document.getElementById('themename').value.trim();document.getElementById('pagetitle').textContent=(n||'Untitled')+': theme';const sb=document.getElementById('savebtn');if(sb)sb.style.display=n?'':'none';fileHandle=null;} +let fileHandle=null; +function exportTheme(){const blob=new Blob([JSON.stringify(exportObj(),null,1)],{type:'application/json'});const a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download=fileSlug()+'.json';a.click();} +async function saveTheme(){const data=JSON.stringify(exportObj(),null,1); + if(!window.showSaveFilePicker){exportTheme();notify('saved via download (browser has no Save-File support)',false);return;} + try{if(!fileHandle)fileHandle=await window.showSaveFilePicker({suggestedName:fileSlug()+'.json',types:[{description:'theme JSON',accept:{'application/json':['.json']}}]}); + const w=await fileHandle.createWritable();await w.write(data);await w.close();notify('saved "'+themeName()+'"',false); + }catch(e){if(e&&e.name!=='AbortError')notify('save failed: '+e.message,true);}} function importFile(ev){const f=ev.target.files[0];if(!f)return;const r=new FileReader(); r.onload=()=>{try{const d=JSON.parse(r.result);if(d.name)document.getElementById('themename').value=d.name;if(d.palette)PALETTE=d.palette;if(d.assignments)Object.assign(MAP,d.assignments); BOLD={};(d.bold||[]).forEach(k=>BOLD[k]=true);ITALIC={};(d.italic||[]).forEach(k=>ITALIC[k]=true); |
