diff options
Diffstat (limited to 'scripts/theme-studio')
| -rw-r--r-- | scripts/theme-studio/app.js | 30 | ||||
| -rw-r--r-- | scripts/theme-studio/generate.py | 2 | ||||
| -rwxr-xr-x | scripts/theme-studio/run-tests.sh | 2 | ||||
| -rw-r--r-- | scripts/theme-studio/theme-studio.html | 32 |
4 files changed, 45 insertions, 21 deletions
diff --git a/scripts/theme-studio/app.js b/scripts/theme-studio/app.js index be70f0ed..c4ff36fb 100644 --- a/scripts/theme-studio/app.js +++ b/scripts/theme-studio/app.js @@ -705,15 +705,11 @@ function buildUITable(){ } applyTableSort('uibody'); } -let D={}; -function srt(c){const tb=document.getElementById('legbody');const r=[...tb.rows];D[c]=!D[c]; - r.sort((a,b)=>{const x=(c===0?(MAP[a.dataset.kind]||''):a.cells[0].innerText).toLowerCase(), - y=(c===0?(MAP[b.dataset.kind]||''):b.cells[0].innerText).toLowerCase(); - return (x<y?-1:x>y?1:0)*(D[c]?1:-1);});r.forEach(x=>tb.appendChild(x));} -// Generic header-click sort for the package and UI tables. Reads a select -// value, a numeric input, or cell text (numeric when the text leads with a -// number, e.g. contrast or size). The sort is remembered per table and -// re-applied after a rebuild so editing a face does not reset it. +// Generic header-click sort, shared by all three tables. Reads a swatch +// dropdown's value, a select value, a numeric input, or cell text (numeric when +// the text leads with a number, e.g. contrast or size). The UI and package +// tables remember the sort (applyTableSort runs on rebuild) so editing a row +// does not reset it; the syntax table sorts on click only. let tableSort={}; function cellVal(td){if(!td)return '';const dd=td.querySelector('.cdd');if(dd)return (dd.dataset.val||'').toLowerCase();const s=td.querySelector('select');if(s)return s.value.toLowerCase();const i=td.querySelector('input');if(i)return parseFloat(i.value)||0;const t=td.innerText.trim();const n=parseFloat(t);return (!isNaN(n)&&/^[-\d.]/.test(t))?n:t.toLowerCase();} function srtTable(tbId,col){tableSort[tbId]={col,asc:!(tableSort[tbId]&&tableSort[tbId].col===col&&tableSort[tbId].asc)};applyTableSort(tbId);} @@ -771,6 +767,22 @@ if(location.hash==='#locktest'){let ok=true;const notes=[];const A=(c,n)=>{if(!c A(PKGMAP[app][p1].fg==='#111111','pkg-clear-keeps-locked');A(PKGMAP[app][p2].fg===null,'pkg-clear-wipes-unlocked');} document.title='LOCKTEST '+(ok?'PASS':'FAIL'); const d=document.createElement('div');d.id='locktest';d.textContent='LOCKTEST '+(ok?'PASS':'FAIL')+(notes.length?' | '+notes.join(' ; '):'');document.body.appendChild(d);} +// Sort gate (open with #sorttest): all three tables now share srtTable/cellVal. +// Verifies the syntax table (which used to have its own srt) sorts by color +// value and by element name, that a repeat click reverses, and that the UI and +// package tables still sort. Guards the unified sort for the later stages. +if(location.hash==='#sorttest'){let ok=true;const notes=[];const A=(c,n)=>{if(!c){ok=false;notes.push(n);}}; + const ddVals=tb=>[...document.querySelectorAll('#'+tb+' tr')].map(tr=>{const dd=tr.cells[2].querySelector('.cdd');return dd?(dd.dataset.val||''):'';}); + const txtVals=tb=>[...document.querySelectorAll('#'+tb+' tr')].map(tr=>tr.cells[0].innerText.trim().toLowerCase()); + const asc=a=>a.every((v,i)=>i===0||a[i-1]<=v),desc=a=>a.every((v,i)=>i===0||a[i-1]>=v); + buildTable(); + srtTable('legbody',2);A(asc(ddVals('legbody')),'legbody-color-asc'); + srtTable('legbody',2);A(desc(ddVals('legbody')),'legbody-color-desc'); + srtTable('legbody',0);A(asc(txtVals('legbody')),'legbody-elements-asc'); + buildUITable();srtTable('uibody',0);A(asc(txtVals('uibody')),'uibody-face-asc'); + buildPkgTable();srtTable('pkgbody',2);A(asc(ddVals('pkgbody')),'pkgbody-fg-asc'); + document.title='SORTTEST '+(ok?'PASS':'FAIL'); + const d=document.createElement('div');d.id='sorttest';d.textContent='SORTTEST '+(ok?'PASS':'FAIL')+(notes.length?' | '+notes.join(' ; '):'');document.body.appendChild(d);} if(location.hash.startsWith('#pick')){openPicker();const m=location.hash.slice(5);if(m){const b=document.querySelector('.pmode button[data-m="'+m+'"]');if(b)b.click();}} if(location.hash==='#cursortest'){document.getElementById('newhexstr').value='#67809c';openPicker();const sc=document.getElementById('svcur'),hc=document.getElementById('huecur');const L=parseFloat(sc.style.left||'0'),T=parseFloat(sc.style.top||'0'),H=parseFloat(hc.style.top||'0');const ok=L>1&&T>1&&H>1;document.title='CURSORTEST '+(ok?'PASS':'FAIL');const d=document.createElement('div');d.id='cursortest';d.textContent='CURSORTEST '+(ok?'PASS':'FAIL')+' left='+sc.style.left+' top='+sc.style.top+' hue='+hc.style.top;document.body.appendChild(d);} if(location.hash.startsWith('#app')){const ap=location.hash.slice(4),s=document.getElementById('appsel');if(s&&ap){s.value=ap;pkgChanged();}} diff --git a/scripts/theme-studio/generate.py b/scripts/theme-studio/generate.py index aab8acaa..0b23bc69 100644 --- a/scripts/theme-studio/generate.py +++ b/scripts/theme-studio/generate.py @@ -461,7 +461,7 @@ STYLES_CSS</style> <div class="cols"> <section class="pane"> <div class="legctl"><button class="fbtn" onclick="clearUnlocked()" title="reset every unlocked element to default (reads as plain foreground text); locked rows are left untouched">clear unlocked</button></div> - <table class="leg" id="legtable"><thead><tr><th onclick="srt(1)">elements △</th><th title="lock a decided element↔color association"></th><th onclick="srt(0)">color △</th><th>style</th><th title="WCAG contrast of this color on the background">contrast</th><th>example</th></tr></thead><tbody id="legbody"></tbody></table> + <table class="leg" id="legtable"><thead><tr><th onclick="srtTable('legbody',0)">elements △</th><th title="lock a decided element↔color association"></th><th onclick="srtTable('legbody',2)">color △</th><th>style</th><th title="WCAG contrast of this color on the background">contrast</th><th>example</th></tr></thead><tbody id="legbody"></tbody></table> </section> <section class="pane grow"> <div class="langbar"><label style="color:#b4b1a2">language</label><select id="langsel" class="chip" style="width:auto;font:bold 10pt monospace" onchange="renderCode()"></select></div> diff --git a/scripts/theme-studio/run-tests.sh b/scripts/theme-studio/run-tests.sh index 51751cc2..d57f0044 100755 --- a/scripts/theme-studio/run-tests.sh +++ b/scripts/theme-studio/run-tests.sh @@ -49,7 +49,7 @@ CHROME="" for c in google-chrome-stable google-chrome chromium chromium-browser; do if command -v "$c" >/dev/null 2>&1; then CHROME="$c"; break; fi done -HASHES="selftest cursortest readouttest deltatest oklchtest planetest locktest" +HASHES="selftest cursortest readouttest deltatest oklchtest planetest locktest sorttest" if [ -z "$CHROME" ]; then for t in $HASHES; do skip_msg "#$t (no Chromium-family browser found)"; done else diff --git a/scripts/theme-studio/theme-studio.html b/scripts/theme-studio/theme-studio.html index 09317c99..2d9ef4d7 100644 --- a/scripts/theme-studio/theme-studio.html +++ b/scripts/theme-studio/theme-studio.html @@ -138,7 +138,7 @@ <div class="cols"> <section class="pane"> <div class="legctl"><button class="fbtn" onclick="clearUnlocked()" title="reset every unlocked element to default (reads as plain foreground text); locked rows are left untouched">clear unlocked</button></div> - <table class="leg" id="legtable"><thead><tr><th onclick="srt(1)">elements △</th><th title="lock a decided element↔color association"></th><th onclick="srt(0)">color △</th><th>style</th><th title="WCAG contrast of this color on the background">contrast</th><th>example</th></tr></thead><tbody id="legbody"></tbody></table> + <table class="leg" id="legtable"><thead><tr><th onclick="srtTable('legbody',0)">elements △</th><th title="lock a decided element↔color association"></th><th onclick="srtTable('legbody',2)">color △</th><th>style</th><th title="WCAG contrast of this color on the background">contrast</th><th>example</th></tr></thead><tbody id="legbody"></tbody></table> </section> <section class="pane grow"> <div class="langbar"><label style="color:#b4b1a2">language</label><select id="langsel" class="chip" style="width:auto;font:bold 10pt monospace" onchange="renderCode()"></select></div> @@ -1070,15 +1070,11 @@ function buildUITable(){ } applyTableSort('uibody'); } -let D={}; -function srt(c){const tb=document.getElementById('legbody');const r=[...tb.rows];D[c]=!D[c]; - r.sort((a,b)=>{const x=(c===0?(MAP[a.dataset.kind]||''):a.cells[0].innerText).toLowerCase(), - y=(c===0?(MAP[b.dataset.kind]||''):b.cells[0].innerText).toLowerCase(); - return (x<y?-1:x>y?1:0)*(D[c]?1:-1);});r.forEach(x=>tb.appendChild(x));} -// Generic header-click sort for the package and UI tables. Reads a select -// value, a numeric input, or cell text (numeric when the text leads with a -// number, e.g. contrast or size). The sort is remembered per table and -// re-applied after a rebuild so editing a face does not reset it. +// Generic header-click sort, shared by all three tables. Reads a swatch +// dropdown's value, a select value, a numeric input, or cell text (numeric when +// the text leads with a number, e.g. contrast or size). The UI and package +// tables remember the sort (applyTableSort runs on rebuild) so editing a row +// does not reset it; the syntax table sorts on click only. let tableSort={}; function cellVal(td){if(!td)return '';const dd=td.querySelector('.cdd');if(dd)return (dd.dataset.val||'').toLowerCase();const s=td.querySelector('select');if(s)return s.value.toLowerCase();const i=td.querySelector('input');if(i)return parseFloat(i.value)||0;const t=td.innerText.trim();const n=parseFloat(t);return (!isNaN(n)&&/^[-\d.]/.test(t))?n:t.toLowerCase();} function srtTable(tbId,col){tableSort[tbId]={col,asc:!(tableSort[tbId]&&tableSort[tbId].col===col&&tableSort[tbId].asc)};applyTableSort(tbId);} @@ -1136,6 +1132,22 @@ if(location.hash==='#locktest'){let ok=true;const notes=[];const A=(c,n)=>{if(!c A(PKGMAP[app][p1].fg==='#111111','pkg-clear-keeps-locked');A(PKGMAP[app][p2].fg===null,'pkg-clear-wipes-unlocked');} document.title='LOCKTEST '+(ok?'PASS':'FAIL'); const d=document.createElement('div');d.id='locktest';d.textContent='LOCKTEST '+(ok?'PASS':'FAIL')+(notes.length?' | '+notes.join(' ; '):'');document.body.appendChild(d);} +// Sort gate (open with #sorttest): all three tables now share srtTable/cellVal. +// Verifies the syntax table (which used to have its own srt) sorts by color +// value and by element name, that a repeat click reverses, and that the UI and +// package tables still sort. Guards the unified sort for the later stages. +if(location.hash==='#sorttest'){let ok=true;const notes=[];const A=(c,n)=>{if(!c){ok=false;notes.push(n);}}; + const ddVals=tb=>[...document.querySelectorAll('#'+tb+' tr')].map(tr=>{const dd=tr.cells[2].querySelector('.cdd');return dd?(dd.dataset.val||''):'';}); + const txtVals=tb=>[...document.querySelectorAll('#'+tb+' tr')].map(tr=>tr.cells[0].innerText.trim().toLowerCase()); + const asc=a=>a.every((v,i)=>i===0||a[i-1]<=v),desc=a=>a.every((v,i)=>i===0||a[i-1]>=v); + buildTable(); + srtTable('legbody',2);A(asc(ddVals('legbody')),'legbody-color-asc'); + srtTable('legbody',2);A(desc(ddVals('legbody')),'legbody-color-desc'); + srtTable('legbody',0);A(asc(txtVals('legbody')),'legbody-elements-asc'); + buildUITable();srtTable('uibody',0);A(asc(txtVals('uibody')),'uibody-face-asc'); + buildPkgTable();srtTable('pkgbody',2);A(asc(ddVals('pkgbody')),'pkgbody-fg-asc'); + document.title='SORTTEST '+(ok?'PASS':'FAIL'); + const d=document.createElement('div');d.id='sorttest';d.textContent='SORTTEST '+(ok?'PASS':'FAIL')+(notes.length?' | '+notes.join(' ; '):'');document.body.appendChild(d);} if(location.hash.startsWith('#pick')){openPicker();const m=location.hash.slice(5);if(m){const b=document.querySelector('.pmode button[data-m="'+m+'"]');if(b)b.click();}} if(location.hash==='#cursortest'){document.getElementById('newhexstr').value='#67809c';openPicker();const sc=document.getElementById('svcur'),hc=document.getElementById('huecur');const L=parseFloat(sc.style.left||'0'),T=parseFloat(sc.style.top||'0'),H=parseFloat(hc.style.top||'0');const ok=L>1&&T>1&&H>1;document.title='CURSORTEST '+(ok?'PASS':'FAIL');const d=document.createElement('div');d.id='cursortest';d.textContent='CURSORTEST '+(ok?'PASS':'FAIL')+' left='+sc.style.left+' top='+sc.style.top+' hue='+hc.style.top;document.body.appendChild(d);} if(location.hash.startsWith('#app')){const ap=location.hash.slice(4),s=document.getElementById('appsel');if(s&&ap){s.value=ap;pkgChanged();}} |
