aboutsummaryrefslogtreecommitdiff
path: root/scripts/theme-studio/theme-studio.html
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/theme-studio/theme-studio.html')
-rw-r--r--scripts/theme-studio/theme-studio.html30
1 files changed, 20 insertions, 10 deletions
diff --git a/scripts/theme-studio/theme-studio.html b/scripts/theme-studio/theme-studio.html
index 70726afa..72cb2022 100644
--- a/scripts/theme-studio/theme-studio.html
+++ b/scripts/theme-studio/theme-studio.html
@@ -557,6 +557,7 @@ function lMax(hue,chroma,fgSet,target){
function oklchOf(hex){return oklab2oklch(srgb2oklab(hex));}
function isReservedGroundLikeName(name){return /^(bg|fg)(?:[-_+].+|\d.*)$/i.test(name||'');}
+function isPureEndpointHex(hex){const h=(hex||'').toLowerCase();return h==='#ffffff'||h==='#000000';}
function columnStem(name){name=name||'color';if(/^color-\d+$/.test(name))return name;name=name.replace(/[+-]\d+$/,'');return name.replace(/\d+$/,'')||'color';}
function columnOffset(name){const m=(name||'').match(/([+-]\d+)$/);return m?parseInt(m[1],10):0;}
function legacyColumnStem(name){return isReservedGroundLikeName(name)?name:columnStem(name);}
@@ -566,7 +567,7 @@ function groundRoleOfEntry(entry,ground){
if(!entry)return null;
const [hex,name]=entry,col=entry[2],n=(name||'').toLowerCase(),h=(hex||'').toLowerCase();
const bg=(ground&&ground.bg||'').toLowerCase(),fg=(ground&&ground.fg||'').toLowerCase();
- if(/^ground-\d+$/i.test(name||''))return 'step';
+ if(/^ground[+-]\d+$/i.test(name||''))return 'step';
if(col==='ground'){
if(bg&&h===bg)return 'bg';
if(fg&&h===fg)return 'fg';
@@ -593,7 +594,7 @@ function groundColumnMembersFromPalette(palette,ground){
if(role==='bg'||role==='fg')byRole[role]={hex:entry[0],name:entry[1]};
else if(role==='step')byRole.steps.push({hex:entry[0],name:entry[1]});
}
- const stepIndex=m=>{const x=(m.name||'').match(/^ground-(\d+)$/i);return x?parseInt(x[1],10):Infinity;};
+ const stepIndex=m=>{const x=(m.name||'').match(/^ground[+-](\d+)$/i);return x?parseInt(x[1],10):Infinity;};
byRole.steps.sort((a,b)=>stepIndex(a)-stepIndex(b));
return [byRole.bg||{hex:ground&&ground.bg,name:'bg'},...byRole.steps,byRole.fg||{hex:ground&&ground.fg,name:'fg'}].filter(m=>m.hex);
}
@@ -632,7 +633,7 @@ function toggleLockSet(keys,locked){
// Group a flat palette into the ground strip plus structural columns. ground is
// {bg,fg}; those endpoint hexes form the pinned ground column even when absent
-// from the palette, and ground-N entries are reserved for that column. Everything
+// from the palette, and ground+N entries are reserved for that column. Everything
// else groups by its stable column id, not by OKLCH hue/chroma or display name.
// Legacy two-field entries fall back to their generated-name stem until edited.
function columnsFromPalette(palette,ground){
@@ -664,7 +665,7 @@ function regenColumn(baseHex,n,opts){
if(k===0)return {members:[{hex,offset:0,clamped:false}]};
const r=ramp(hex,Object.assign({},opts,{n:k}));
if(r.error)return {members:[],error:r.error};
- const members=[...r.steps,{hex,offset:0,clamped:false}].sort((a,b)=>a.offset-b.offset);
+ const members=[...r.steps.filter(s=>!isPureEndpointHex(s.hex)),{hex,offset:0,clamped:false}].sort((a,b)=>a.offset-b.offset);
return {members};
}
// Rank a column's current member hexes by lightness and give each a signed offset
@@ -905,7 +906,7 @@ function ensureGroundEndpoints(){
}
function normalizePalette(){PALETTE=PALETTE.map(normalizePaletteEntry);ensureGroundEndpoints();}
// The ground column is explicit: bg pins the top endpoint, fg pins the bottom
-// endpoint, and generated ground-N steps live between them.
+// endpoint, and generated ground+N steps live between them.
function groundColumnMembers(){
return groundColumnMembersFromPalette(PALETTE,{bg:MAP['bg'],fg:MAP['p']});
}
@@ -920,10 +921,13 @@ function setGroundSpan(n){
const old=PALETTE.filter(entry=>groundRoleOfEntry(entry,{bg:MAP['bg'],fg:MAP['p']})==='step');
const bg=srgb2oklab(MAP['bg']),fg=srgb2oklab(MAP['p']);
const entries=[];
+ let step=1;
for(let i=1;i<=n;i++){
const t=i/(n+1);
const lab={L:bg.L+(fg.L-bg.L)*t,a:bg.a+(fg.a-bg.a)*t,b:bg.b+(fg.b-bg.b)*t};
- entries.push([lrgb2hex(oklab2lrgb(lab.L,lab.a,lab.b)),'ground-'+i,'ground']);
+ const hex=lrgb2hex(oklab2lrgb(lab.L,lab.a,lab.b));
+ if(hex.toLowerCase()==='#ffffff'||hex.toLowerCase()==='#000000')continue;
+ entries.push([hex,'ground+'+(step++),'ground']);
}
for(const [oldHex,oldName] of old){
const next=entries.find(([,name])=>name===oldName);
@@ -2144,16 +2148,22 @@ if(location.hash==='#counttest'){let ok=true;const notes=[];const A=(c,n)=>{if(!
PALETTE=[['#204060','bg'],['#f0fef0','fg']];
setGroundSpan(2);
A(MAP['bg']==='#204060'&&MAP['p']==='#f0fef0','spanning ground keeps bg/fg assignments on endpoints');
- A(PALETTE.some(p=>p[1]==='ground-1')&&PALETTE.some(p=>p[1]==='ground-2'),'spanning ground adds interior ground-N entries');
+ A(PALETTE.some(p=>p[1]==='ground+1')&&PALETTE.some(p=>p[1]==='ground+2'),'spanning ground adds interior ground+N entries');
A(document.querySelector('#pals .fstrip[data-column="ground"] .fhead + .fcount + .pchip'),'ground span control renders before tiles');
MAP['bg']='#ffffff';MAP['p']='#000000';
- PALETTE=[['#ffffff','bg'],['#bbbbbb','ground-1','ground'],['#777777','ground-2','ground'],['#000000','fg']];
+ PALETTE=[['#ffffff','bg'],['#bbbbbb','ground+1','ground'],['#777777','ground+2','ground'],['#000000','fg']];
renderPalette();
const groundNames=[...document.querySelectorAll('#pals .fstrip[data-column="ground"] .pchip .nm')].map(e=>e.value);
- A(groundNames.join('|')==='bg|ground-1|ground-2|fg','ground column order is bg, ground steps, fg even when bg is lighter: '+groundNames.join('|'));
+ A(groundNames.join('|')==='bg|ground+1|ground+2|fg','ground column order is bg, ground steps, fg even when bg is lighter: '+groundNames.join('|'));
MAP['bg']='#204060';MAP['p']='#f0fef0';
setGroundSpan(1);
- A(!PALETTE.some(p=>p[1]==='ground-2'),'lowering ground span removes dropped interior steps');
+ A(!PALETTE.some(p=>p[1]==='ground+2'),'lowering ground span removes dropped interior steps');
+ PALETTE=[['#204060','bg'],['#f0fef0','fg'],['#e0e0e0','near-white','near-white']];
+ setColumnCount('#e0e0e0',4);
+ A(!PALETTE.some(p=>p[0].toLowerCase()==='#ffffff'&&p[1]!=='fg'),'spanning a near-white base skips generated pure-white tiles');
+ PALETTE=[['#204060','bg'],['#f0fef0','fg'],['#101010','near-black','near-black']];
+ setColumnCount('#101010',4);
+ A(!PALETTE.some(p=>p[0].toLowerCase()==='#000000'&&p[1]!=='bg'),'spanning a near-black base skips generated pure-black tiles');
PALETTE=[['#204060','bg'],['#f0fef0','fg']];
regenColumn('#67809c',2).members.forEach(m=>PALETTE.push([m.hex,m.offset===0?'blue':'blue'+(m.offset>0?'+'+m.offset:m.offset)]));
const innerOld=regenColumn('#67809c',2).members.find(m=>m.offset===1).hex; // survives a count change