From 5f915271f8d5da97ec86b44d6233872960092653 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Mon, 15 Jun 2026 17:20:37 -0500 Subject: refactor(theme-studio): collapse assignment views into one dropdown panel The assignment area was three stacked sections (color/code, ui faces, package faces), and package faces carried its own application selector. I merged them into one panel driven by a single dropdown: color/code assignments, ui faces, then a non-selectable "package faces" optgroup holding every app in order. Picking an entry swaps the left table and right preview, so only one view shows at a time. curApp now reads the selected app from that dropdown, and the old appsel is gone. A #viewtest browser gate locks in the dropdown order, the optgroup, and the one-view-at-a-time switching. --- scripts/theme-studio/app.js | 22 +++++++-- scripts/theme-studio/browser-gates.js | 25 +++++++++++ scripts/theme-studio/theme-studio.html | 59 ++++++++++++++++++++++--- scripts/theme-studio/theme-studio.template.html | 12 +++-- 4 files changed, 104 insertions(+), 14 deletions(-) (limited to 'scripts/theme-studio') diff --git a/scripts/theme-studio/app.js b/scripts/theme-studio/app.js index 5f4e1c3f1..a9bfd1501 100644 --- a/scripts/theme-studio/app.js +++ b/scripts/theme-studio/app.js @@ -485,10 +485,25 @@ function uiSelect(face,attr){const cur=UIMAP[face][attr]||''; const BASE_INHERITS=['fixed-pitch','variable-pitch','default','link','bold','italic','shadow']; function uiFaceBlank(){return {fg:null,bg:null,bold:false,italic:false,underline:false,strike:false};} function seedFace(d){return normalizePkgFace({fg:pname(d.fg),bg:pname(d.bg),bold:d.bold,italic:d.italic,underline:d.underline,strike:d.strike,inherit:d.inherit,height:d.height,box:d.box},'default');} -function curApp(){const s=document.getElementById('appsel');return s&&s.value?s.value:Object.keys(APPS)[0];} +function curApp(){const s=document.getElementById('viewsel');const v=s&&s.value;return (v&&v[0]!=='@')?v:Object.keys(APPS)[0];} function pkgEffFg(app,face,seen){return effResolve(PKGMAP,app,face,'fg',seen);} function pkgEffBg(app,face,seen){return effResolve(PKGMAP,app,face,'bg',seen);} -function buildAppSel(){const s=document.getElementById('appsel');if(!s)return;s.innerHTML='';for(const app in APPS){const o=document.createElement('option');o.value=app;o.textContent=APPS[app].label;s.appendChild(o);}s.onchange=pkgChanged;} +// One dropdown drives the whole assignment panel: two editor entries (@code, +// @ui) then a non-selectable "package faces" optgroup holding every app, in +// APPS order. onViewChange shows exactly one of the three view blocks. +function buildViewSel(){const s=document.getElementById('viewsel');if(!s)return;s.innerHTML=''; + const mk=(v,t)=>{const o=document.createElement('option');o.value=v;o.textContent=t;return o;}; + s.appendChild(mk('@code','color/code assignments')); + s.appendChild(mk('@ui','ui faces')); + const og=document.createElement('optgroup');og.label='package faces'; + for(const app in APPS)og.appendChild(mk(app,APPS[app].label)); + s.appendChild(og);} +function onViewChange(){const s=document.getElementById('viewsel');const v=(s&&s.value)||'@code'; + const show=(id,on)=>{const e=document.getElementById(id);if(e)e.style.display=on?'':'none';}; + show('view-code',v==='@code');show('view-ui',v==='@ui');show('view-pkg',v[0]!=='@'); + if(v==='@code')renderCode(); + else if(v==='@ui'){buildUITable();buildMockFrame();syncMockHeight();} + else pkgChanged();} function pkgChanged(){buildPkgTable();buildPkgPreview();syncPkgHeight();} function buildPkgTable(){ const app=curApp(),tb=document.getElementById('pkgbody');if(!tb)return;tb.innerHTML=''; @@ -992,9 +1007,10 @@ function cellVal(td){if(!td)return '';const dd=td.querySelector('.cdd');if(dd)re function srtTable(tbId,col){tableSort[tbId]={col,asc:!(tableSort[tbId]&&tableSort[tbId].col===col&&tableSort[tbId].asc)};applyTableSort(tbId);} function applyTableSort(tbId){const s=tableSort[tbId];if(!s)return;const tb=document.getElementById(tbId);if(!tb)return;const dir=s.asc?1:-1;const r=[...tb.rows];r.sort((a,b)=>{const x=cellVal(a.cells[s.col]),y=cellVal(b.cells[s.col]);return ((typeof x==='number'&&typeof y==='number')?x-y:(xy?1:0))*dir;});r.forEach(x=>tb.appendChild(x));} function initApp(){ - buildLangSel();buildAppSel();renderPalette();rebuildColorTables();renderCode();applyGround(); + buildLangSel();buildViewSel();renderPalette();rebuildColorTables();renderCode();applyGround(); initGeneratorControls(); updateTitle();initPicker();buildPkgPreview();syncMockHeight();syncPkgHeight(); + onViewChange(); } initApp(); addEventListener('resize',()=>{syncMockHeight();syncPkgHeight();}); diff --git a/scripts/theme-studio/browser-gates.js b/scripts/theme-studio/browser-gates.js index ad7a586df..2b85fc8bb 100644 --- a/scripts/theme-studio/browser-gates.js +++ b/scripts/theme-studio/browser-gates.js @@ -649,3 +649,28 @@ if(location.hash==='#roundtriptest'){let ok=true;const notes=[];const A=(c,n)=>{ PALETTE=saveP;for(const k in MAP)delete MAP[k];Object.assign(MAP,saveM);syncSyntaxFromCache();LOCKED=saveL; document.title='ROUNDTRIPTEST '+(ok?'PASS':'FAIL'); const d=document.createElement('div');d.id='roundtriptest';d.textContent='ROUNDTRIPTEST '+(ok?'PASS':'FAIL')+(notes.length?' | '+notes.join(' ; '):'');document.body.appendChild(d);} +// View-selector gate (open with #viewtest): the assignment panel is driven by a +// single #viewsel dropdown -- two editor entries (@code, @ui) then a "package +// faces" optgroup of every app, in order -- and switching it shows exactly one +// of the three view blocks. +if(location.hash==='#viewtest'){let ok=true;const notes=[];const A=(c,n)=>{if(!c){ok=false;notes.push(n);}}; + const sel=document.getElementById('viewsel'); + A(!!sel,'viewsel-exists'); + if(sel){ + A(sel.options[0]&&sel.options[0].value==='@code','first-option-code'); + A(sel.options[1]&&sel.options[1].value==='@ui','second-option-ui'); + const og=sel.querySelector('optgroup'); + A(og&&og.label==='package faces','package-faces-optgroup'); + if(og){const appOpts=[...og.querySelectorAll('option')].map(o=>o.value); + A(JSON.stringify(appOpts)===JSON.stringify(Object.keys(APPS)),'optgroup-lists-apps-in-order');} + const vis=id=>{const e=document.getElementById(id);return !!e&&e.style.display!=='none';}; + sel.value='@code';onViewChange(); + A(vis('view-code')&&!vis('view-ui')&&!vis('view-pkg'),'code-view-only'); + sel.value='@ui';onViewChange(); + A(!vis('view-code')&&vis('view-ui')&&!vis('view-pkg'),'ui-view-only'); + const firstApp=Object.keys(APPS)[0];sel.value=firstApp;onViewChange(); + A(!vis('view-code')&&!vis('view-ui')&&vis('view-pkg'),'pkg-view-only'); + A(curApp()===firstApp,'curApp-returns-selected-app'); + } + document.title='VIEWTEST '+(ok?'PASS':'FAIL'); + const d=document.createElement('div');d.id='viewtest';d.textContent='VIEWTEST '+(ok?'PASS':'FAIL')+(notes.length?' fails='+notes.join(','):'');document.body.appendChild(d);} diff --git a/scripts/theme-studio/theme-studio.html b/scripts/theme-studio/theme-studio.html index 38aeda546..9f74e79e2 100644 --- a/scripts/theme-studio/theme-studio.html +++ b/scripts/theme-studio/theme-studio.html @@ -200,7 +200,9 @@
-

code/color assignments

+

assignment

+
+
@@ -211,7 +213,8 @@

  
-

ui faces

+
+ + diff --git a/scripts/theme-studio/theme-studio.template.html b/scripts/theme-studio/theme-studio.template.html index 49c15509c..964724d9e 100644 --- a/scripts/theme-studio/theme-studio.template.html +++ b/scripts/theme-studio/theme-studio.template.html @@ -58,7 +58,9 @@ STYLES_CSS
-

code/color assignments

+

assignment

+
+
@@ -69,7 +71,8 @@ STYLES_CSS

  
-

ui faces

+
+ + -- cgit v1.2.3