diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-13 16:53:22 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-13 16:53:22 -0500 |
| commit | f2285ee52f9712ef431fa010ffeb1590dcc6983c (patch) | |
| tree | 2df2bb36a1886fe5dd51676e409e1d5b61a87f77 | |
| parent | 4ec1d39e5be7dfcd12103b9ba998e44b9d50d97f (diff) | |
| download | dotemacs-f2285ee52f9712ef431fa010ffeb1590dcc6983c.tar.gz dotemacs-f2285ee52f9712ef431fa010ffeb1590dcc6983c.zip | |
Fix theme studio bg-prefixed span inference
| -rw-r--r-- | scripts/theme-studio/app-core.js | 9 | ||||
| -rw-r--r-- | scripts/theme-studio/app.js | 6 | ||||
| -rw-r--r-- | scripts/theme-studio/test-columns.mjs | 8 | ||||
| -rw-r--r-- | scripts/theme-studio/theme-studio.html | 15 |
4 files changed, 26 insertions, 12 deletions
diff --git a/scripts/theme-studio/app-core.js b/scripts/theme-studio/app-core.js index 3e9e93d8..1a4a121f 100644 --- a/scripts/theme-studio/app-core.js +++ b/scripts/theme-studio/app-core.js @@ -135,9 +135,12 @@ function lMax(hue,chroma,fgSet,target){ // assignment re-point across a regenerate. function oklchOf(hex){return oklab2oklch(srgb2oklab(hex));} +function isReservedGroundLikeName(name){return /^(bg|fg)(?:[-_+].+|\d.*)$/i.test(name||'');} 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 columnIdOf(entry){return (entry&&entry[2])||columnStem(entry&&entry[1]);} +function legacyColumnStem(name){return isReservedGroundLikeName(name)?name:columnStem(name);} +function legacyColumnOffset(name){return isReservedGroundLikeName(name)?0:columnOffset(name);} +function columnIdOf(entry){return (entry&&entry[2])||legacyColumnStem(entry&&entry[1]);} function groundRoleOfEntry(entry,ground){ if(!entry)return null; const [hex,name]=entry,col=entry[2],n=(name||'').toLowerCase(),h=(hex||'').toLowerCase(); @@ -171,9 +174,9 @@ function columnsFromPalette(palette,ground){ for(const entry of palette){ const [hex,name]=entry; if(groundRoleOfEntry(entry,ground))continue; - const column=columnIdOf(entry); + const column=columnIdOf(entry),offset=entry[2]?columnOffset(name):legacyColumnOffset(name); if(!byColumn.has(column))byColumn.set(column,{column,members:[]}); - byColumn.get(column).members.push({hex,name,offset:columnOffset(name),column}); + byColumn.get(column).members.push({hex,name,offset,column}); } for(const f of byColumn.values()){ const base=(f.members.find(m=>m.offset===0)||f.members[0]).hex; diff --git a/scripts/theme-studio/app.js b/scripts/theme-studio/app.js index 444408f2..18118f7c 100644 --- a/scripts/theme-studio/app.js +++ b/scripts/theme-studio/app.js @@ -158,7 +158,7 @@ function repointHex(oldHex,newHex){ function healGone(name,newHex){const k=name.toLowerCase();if(!(k in lastGone))return false;const g=lastGone[k];delete lastGone[k];repointHex(g,newHex);return true;} function normalizePaletteEntry(entry){ const hex=entry&&entry[0],name=(entry&&entry[1])||'color'; - return [hex,name,(entry&&entry[2])||columnStem(name)]; + return [hex,name,(entry&&entry[2])||columnIdOf(entry)]; } function normalizePalette(){PALETTE=PALETTE.map(normalizePaletteEntry);} // The ground column is explicit: bg pins the dark endpoint, fg pins the light @@ -458,7 +458,7 @@ function initPicker(){const sw=document.getElementById('swatch');if(!sw)return;s function addColor(){const h=curHex();const name=document.getElementById('newname').value.trim(); if(!name){notify('name the color before adding it',true);return;} if(PALETTE.some(p=>p[1].toLowerCase()===name.toLowerCase())){notify('a color named "'+name+'" already exists — select it and use Update selected to change its value',true);return;} - PALETTE.push([h,name,columnStem(name)]);const healed=healGone(name,h);document.getElementById('newname').value='';selectedIdx=null;closePicker(); + PALETTE.push([h,name,columnIdOf([h,name])]);const healed=healGone(name,h);document.getElementById('newname').value='';selectedIdx=null;closePicker(); renderPalette();buildTable();buildUITable(); if(healed){renderCode();applyGround();if(document.getElementById('pkgbody'))buildPkgTable();buildPkgPreview();} notify(healed?('added "'+name+'" and reconnected its assignments'):('added "'+name+'"'),false);} @@ -1313,7 +1313,7 @@ if(location.hash==='#columntest'||location.hash==='#familytest'){let ok=true;con A(!!renamed&&renamed.closest('.fstrip').dataset.column===redColumn,'a renamed color stays in the same strip'); PALETTE=[['#0d0b0a','bg','ground'],['#f0fef0','fg','ground'],['#0d0b0a','bg2'],['#0d0b0a','bg-alt']];MAP['bg']='#0d0b0a';MAP['p']='#f0fef0';selectedIdx=null;renderPalette(); const bg2Chip=[...document.querySelectorAll('#pals .pchip')].find(c=>c.querySelector('.nm')&&c.querySelector('.nm').value==='bg2'); - A(!!bg2Chip&&bg2Chip.closest('.fstrip').dataset.column==='bg'&&!!bg2Chip.querySelector('.rm')&&!bg2Chip.querySelector('.lock'),'same-hex bg2 remains a normal removable color column chip'); + A(!!bg2Chip&&bg2Chip.closest('.fstrip').dataset.column==='bg2'&&!!bg2Chip.querySelector('.rm')&&!bg2Chip.querySelector('.lock'),'same-hex bg2 remains a normal removable color column chip'); if(bg2Chip){bg2Chip.click();document.getElementById('newhexstr').value='#101820';document.getElementById('newname').value='bg2';updateColor();} A(MAP['bg']==='#0d0b0a','editing same-hex bg2 does not repoint the real bg assignment'); A(PALETTE.some(p=>p[1]==='bg2'&&p[0]==='#101820'),'editing same-hex bg2 updates only that palette tile'); diff --git a/scripts/theme-studio/test-columns.mjs b/scripts/theme-studio/test-columns.mjs index abfab0a3..4f5ae6a0 100644 --- a/scripts/theme-studio/test-columns.mjs +++ b/scripts/theme-studio/test-columns.mjs @@ -92,6 +92,14 @@ test('columnsFromPalette: Boundary - imported bg-like names are not ground just assert.deepEqual(ground.map(g => [g.role, g.name]), [['bg', 'bg'], ['fg', 'fg']]); assert.ok(columnOf(columns, 'bg2'), 'bg2 remains in a normal imported color column'); assert.ok(columnOf(columns, 'bg-alt'), 'bg-alt remains in a normal imported color column'); + assert.deepEqual(columns.map(f => f.column), ['bg2', 'bg-alt']); +}); + +test('columnsFromPalette: Boundary - bg and fg prefixed legacy names are independent bases, not generated steps', () => { + const pal = [['#101010', 'bg-1'], ['#202020', 'bg-2'], ['#eeeeee', 'fg-1'], ['#dddddd', 'fg-alt']]; + const { columns } = columnsFromPalette(pal, { bg: '#000000', fg: '#ffffff' }); + assert.deepEqual(columns.map(f => f.column), ['bg-1', 'bg-2', 'fg-1', 'fg-alt']); + assert.deepEqual(columns.map(f => f.members.map(m => m.name)), [['bg-1'], ['bg-2'], ['fg-1'], ['fg-alt']]); }); test('groundRoleOfEntry: Boundary - exact ground roles only, not bg-prefix names', () => { diff --git a/scripts/theme-studio/theme-studio.html b/scripts/theme-studio/theme-studio.html index 7d4ccf9d..44156648 100644 --- a/scripts/theme-studio/theme-studio.html +++ b/scripts/theme-studio/theme-studio.html @@ -551,9 +551,12 @@ function lMax(hue,chroma,fgSet,target){ // assignment re-point across a regenerate. function oklchOf(hex){return oklab2oklch(srgb2oklab(hex));} +function isReservedGroundLikeName(name){return /^(bg|fg)(?:[-_+].+|\d.*)$/i.test(name||'');} 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 columnIdOf(entry){return (entry&&entry[2])||columnStem(entry&&entry[1]);} +function legacyColumnStem(name){return isReservedGroundLikeName(name)?name:columnStem(name);} +function legacyColumnOffset(name){return isReservedGroundLikeName(name)?0:columnOffset(name);} +function columnIdOf(entry){return (entry&&entry[2])||legacyColumnStem(entry&&entry[1]);} function groundRoleOfEntry(entry,ground){ if(!entry)return null; const [hex,name]=entry,col=entry[2],n=(name||'').toLowerCase(),h=(hex||'').toLowerCase(); @@ -587,9 +590,9 @@ function columnsFromPalette(palette,ground){ for(const entry of palette){ const [hex,name]=entry; if(groundRoleOfEntry(entry,ground))continue; - const column=columnIdOf(entry); + const column=columnIdOf(entry),offset=entry[2]?columnOffset(name):legacyColumnOffset(name); if(!byColumn.has(column))byColumn.set(column,{column,members:[]}); - byColumn.get(column).members.push({hex,name,offset:columnOffset(name),column}); + byColumn.get(column).members.push({hex,name,offset,column}); } for(const f of byColumn.values()){ const base=(f.members.find(m=>m.offset===0)||f.members[0]).hex; @@ -811,7 +814,7 @@ function repointHex(oldHex,newHex){ function healGone(name,newHex){const k=name.toLowerCase();if(!(k in lastGone))return false;const g=lastGone[k];delete lastGone[k];repointHex(g,newHex);return true;} function normalizePaletteEntry(entry){ const hex=entry&&entry[0],name=(entry&&entry[1])||'color'; - return [hex,name,(entry&&entry[2])||columnStem(name)]; + return [hex,name,(entry&&entry[2])||columnIdOf(entry)]; } function normalizePalette(){PALETTE=PALETTE.map(normalizePaletteEntry);} // The ground column is explicit: bg pins the dark endpoint, fg pins the light @@ -1111,7 +1114,7 @@ function initPicker(){const sw=document.getElementById('swatch');if(!sw)return;s function addColor(){const h=curHex();const name=document.getElementById('newname').value.trim(); if(!name){notify('name the color before adding it',true);return;} if(PALETTE.some(p=>p[1].toLowerCase()===name.toLowerCase())){notify('a color named "'+name+'" already exists — select it and use Update selected to change its value',true);return;} - PALETTE.push([h,name,columnStem(name)]);const healed=healGone(name,h);document.getElementById('newname').value='';selectedIdx=null;closePicker(); + PALETTE.push([h,name,columnIdOf([h,name])]);const healed=healGone(name,h);document.getElementById('newname').value='';selectedIdx=null;closePicker(); renderPalette();buildTable();buildUITable(); if(healed){renderCode();applyGround();if(document.getElementById('pkgbody'))buildPkgTable();buildPkgPreview();} notify(healed?('added "'+name+'" and reconnected its assignments'):('added "'+name+'"'),false);} @@ -1966,7 +1969,7 @@ if(location.hash==='#columntest'||location.hash==='#familytest'){let ok=true;con A(!!renamed&&renamed.closest('.fstrip').dataset.column===redColumn,'a renamed color stays in the same strip'); PALETTE=[['#0d0b0a','bg','ground'],['#f0fef0','fg','ground'],['#0d0b0a','bg2'],['#0d0b0a','bg-alt']];MAP['bg']='#0d0b0a';MAP['p']='#f0fef0';selectedIdx=null;renderPalette(); const bg2Chip=[...document.querySelectorAll('#pals .pchip')].find(c=>c.querySelector('.nm')&&c.querySelector('.nm').value==='bg2'); - A(!!bg2Chip&&bg2Chip.closest('.fstrip').dataset.column==='bg'&&!!bg2Chip.querySelector('.rm')&&!bg2Chip.querySelector('.lock'),'same-hex bg2 remains a normal removable color column chip'); + A(!!bg2Chip&&bg2Chip.closest('.fstrip').dataset.column==='bg2'&&!!bg2Chip.querySelector('.rm')&&!bg2Chip.querySelector('.lock'),'same-hex bg2 remains a normal removable color column chip'); if(bg2Chip){bg2Chip.click();document.getElementById('newhexstr').value='#101820';document.getElementById('newname').value='bg2';updateColor();} A(MAP['bg']==='#0d0b0a','editing same-hex bg2 does not repoint the real bg assignment'); A(PALETTE.some(p=>p[1]==='bg2'&&p[0]==='#101820'),'editing same-hex bg2 updates only that palette tile'); |
