aboutsummaryrefslogtreecommitdiff
path: root/scripts/theme-studio/theme-studio.html
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-13 16:16:29 -0500
committerCraig Jennings <c@cjennings.net>2026-06-13 16:16:29 -0500
commita71d8e0eaf7a26805a261c8854636d7d13d084ca (patch)
tree912f88ff8da136dafc7c08ff2a4535d779e5d811 /scripts/theme-studio/theme-studio.html
parent02dfe94eba40ae351a257656b5563c98a82a4b37 (diff)
downloaddotemacs-a71d8e0eaf7a26805a261c8854636d7d13d084ca.tar.gz
dotemacs-a71d8e0eaf7a26805a261c8854636d7d13d084ca.zip
Add theme studio column selection and reorder controls
Diffstat (limited to 'scripts/theme-studio/theme-studio.html')
-rw-r--r--scripts/theme-studio/theme-studio.html65
1 files changed, 60 insertions, 5 deletions
diff --git a/scripts/theme-studio/theme-studio.html b/scripts/theme-studio/theme-studio.html
index 0a1a576d..9cdf58ed 100644
--- a/scripts/theme-studio/theme-studio.html
+++ b/scripts/theme-studio/theme-studio.html
@@ -28,7 +28,12 @@
.pals{display:flex;flex-direction:row;flex-wrap:wrap;gap:10px;align-items:flex-start}
.fstrip{display:flex;flex-direction:column;gap:6px;padding:5px;border-radius:7px;border:1px solid transparent}
.fstrip.ground{border-color:#252321;background:#161412}
- .fhead{min-height:17px;max-width:128px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#b4b1a2;font:9pt monospace;text-align:center}
+ .fhead{min-height:17px;width:128px;display:flex;align-items:center;justify-content:center;gap:3px;color:#b4b1a2;font:9pt monospace;text-align:center}
+ .fhead .ctitle{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background:none;border:none;color:#b4b1a2;font:9pt monospace;text-align:center;cursor:pointer;padding:0}
+ .fhead .ctitle:hover{color:#e8bd30}
+ .fhead .cmove{width:18px;height:17px;background:#161412;border:1px solid #252321;border-radius:3px;color:#8a9496;font:10pt monospace;line-height:13px;padding:0;cursor:pointer}
+ .fhead .cmove:hover:not(:disabled){color:#e8bd30;border-color:#3a3a3a}
+ .fhead .cmove:disabled{opacity:.28;cursor:default}
.fcount{margin-top:3px;font:9pt monospace;color:#8a9496;text-align:center}
.fcount input{width:40px;background:#0d0b0a;border:1px solid #252321;color:#cdced1;border-radius:4px;padding:2px 4px;font:9pt monospace;text-align:center}
.palwarn{display:none;margin-top:8px;font:10pt monospace;color:#cb6b4d}
@@ -855,6 +860,49 @@ function paletteChip(i,nearest){
d.onclick=(e)=>{if(e.target.closest('.rm')||e.target.closest('.nm'))return;selectColor(i);};
return d;
}
+function paletteIndexByHexName(hex,name){
+ for(let i=0;i<PALETTE.length;i++)if(PALETTE[i][0]===hex&&PALETTE[i][1]===name)return i;
+ return -1;
+}
+function selectColumnBase(f){
+ const baseMember=f.members.find(m=>m.hex.toLowerCase()===f.base.toLowerCase())||f.members[0];
+ const i=paletteIndexByHexName(baseMember.hex,baseMember.name);
+ if(i>=0)selectColor(i);
+}
+function isGroundEntry(entry){
+ const [hex,name]=entry;
+ const lower=(hex||'').toLowerCase();
+ return lower===MAP['bg'].toLowerCase()||lower===MAP['p'].toLowerCase()||/^ground-\d+$/i.test(name||'');
+}
+function moveColumn(columnId,dir){
+ normalizePalette();
+ const columns=sortColumns(columnsFromPalette(PALETTE,{bg:MAP['bg'],fg:MAP['p']}).columns);
+ const pos=columns.findIndex(f=>f.column===columnId);
+ const next=columns[pos+dir];
+ if(pos<0||!next)return;
+ const moving=[],rest=[];
+ PALETTE.forEach(entry=>{
+ if(!isGroundEntry(entry)&&columnIdOf(entry)===columnId)moving.push(entry);
+ else rest.push(entry);
+ });
+ const nextPositions=[];
+ rest.forEach((entry,i)=>{if(!isGroundEntry(entry)&&columnIdOf(entry)===next.column)nextPositions.push(i);});
+ if(!nextPositions.length)return;
+ const at=dir<0?nextPositions[0]:nextPositions[nextPositions.length-1]+1;
+ PALETTE=rest.slice(0,at).concat(moving,rest.slice(at));
+ selectedIdx=null;renderPalette();buildTable();buildUITable();renderCode();applyGround();
+ notify('moved "'+columnId+'" '+(dir<0?'left':'right'),false);
+}
+function columnHeader(f,position,count){
+ const h=document.createElement('div');h.className='fhead';
+ const label=(f.members.find(m=>m.hex.toLowerCase()===f.base.toLowerCase())||{}).name||f.column||f.base;
+ h.innerHTML=`<button class="cmove left" title="move column left" ${position===0?'disabled':''}>&#8249;</button><button class="ctitle" title="select base color"></button><button class="cmove right" title="move column right" ${position===count-1?'disabled':''}>&#8250;</button>`;
+ h.querySelector('.ctitle').textContent=label;
+ h.querySelector('.ctitle').onclick=()=>selectColumnBase(f);
+ h.querySelector('.left').onclick=(e)=>{e.stopPropagation();moveColumn(f.column,-1);};
+ h.querySelector('.right').onclick=(e)=>{e.stopPropagation();moveColumn(f.column,1);};
+ return h;
+}
// Render the palette as structural color columns: pinned ground column, then
// first-seen palette columns. Grouping uses the stable column id stored on each
// palette entry, so renaming a color never moves it.
@@ -880,11 +928,10 @@ function renderPalette(){
// The too-similar warning stays on the full flat palette: a generated ramp's
// steps are a stepL apart (well above the warning's ΔE threshold), so they never
// trigger it, and any pair that does is a genuine near-duplicate worth flagging.
- sortColumns(columns).forEach(f=>{
+ const ordered=sortColumns(columns);
+ ordered.forEach((f,pos)=>{
const s=strip('');s.dataset.column=f.column||f.base;
- const h=document.createElement('div');h.className='fhead';
- h.textContent=(f.members.find(m=>m.hex.toLowerCase()===f.base.toLowerCase())||{}).name||f.column||f.base;
- s.appendChild(h);
+ s.appendChild(columnHeader(f,pos,ordered.length));
s.appendChild(columnCountControl(f));
f.members.forEach(m=>{const i=idxOf(m.hex,m.name);if(i>=0)s.appendChild(paletteChip(i,nearest));});
});
@@ -1875,6 +1922,14 @@ if(location.hash==='#columntest'||location.hash==='#familytest'){let ok=true;con
A(strips[0].querySelectorAll('.pchip').length===2,'ground column carries bg + fg endpoints');
A(!!strips[0].querySelector('.fhead + .fcount + .pchip'),'span control sits between header and tiles for ground');
A(strips.length>=4,'ground + red + blue + gray columns, got '+strips.length);
+ const blueHead=strips.find(s=>s.dataset.column==='blue')&&strips.find(s=>s.dataset.column==='blue').querySelector('.ctitle');
+ A(!!blueHead,'normal column header has a selectable title');
+ if(blueHead)blueHead.click();
+ A(selectedIdx!==null&&PALETTE[selectedIdx][1]==='blue'&&document.getElementById('newhexstr').value.toLowerCase()==='#3a6ea5','clicking a column title selects its base color');
+ const blueRight=strips.find(s=>s.dataset.column==='blue')&&strips.find(s=>s.dataset.column==='blue').querySelector('.cmove.right');
+ if(blueRight)blueRight.click();
+ const moved=[...document.querySelectorAll('#pals .fstrip')].map(s=>s.dataset.column);
+ A(moved.indexOf('blue')>moved.indexOf('gray'),'right arrow moves a color column after its neighbor');
const redChip=[...document.querySelectorAll('#pals .pchip')].find(c=>c.querySelector('.nm')&&c.querySelector('.nm').value==='red');
A(!!redChip&&!!redChip.querySelector('.rm')&&!!redChip.querySelector('.nm'),'a column chip keeps remove + rename controls');
const redColumn=redChip&&redChip.closest('.fstrip').dataset.column;