From 64153c8d995f1603986f3b44ccbdf9ddb21dfd55 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Thu, 18 Jun 2026 21:42:40 -0500 Subject: feat(theme-studio): widen the face model with the additive attributes This is Phase 2 of the face-attribute expansion. The model now carries distant-fg, family, overline, inverse, and extend in final shape across all three tiers, and inherit and height are no longer package-only (a ui or syntax face can set them too). I kept bold/italic/underline/strike as the legacy booleans for now. The cutover to weight/slant and the underline/strike object forms lands in the next phase with the editor widgets that force it, so the representation and the controls that drive it move together. face_specs.py holds the canonical defaults. In app-core.js, normalizePkgFace and packagesForExport carry and emit the new attrs: distant-fg resolves through the palette like fg/bg, and each attr exports only when set, so existing presets re-export unchanged. app.js syntaxBlank, uiFaceBlank, and seedFace match the shape. Nothing changed shape, so dupre, distinguished, sterling, now, theme, and WIP all emit byte-identical themes. make check green: Python 58, Node 193, ERT 40. --- scripts/theme-studio/app.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'scripts/theme-studio/app.js') diff --git a/scripts/theme-studio/app.js b/scripts/theme-studio/app.js index 3949268d0..82dccc111 100644 --- a/scripts/theme-studio/app.js +++ b/scripts/theme-studio/app.js @@ -4,7 +4,7 @@ let MAP=MAP_J, PALETTE=PALETTE_J, SYNTAX=SYNTAX_J, UIMAP=UIMAP_J; let LOCKED=new Set(LOCKS_J); // rows whose choice is decided (controls disabled, skipped by erase/reset batch actions) const DELTAE_MIN=0.02; // OKLab ΔE below this = colors too close to tell apart (perceptual-metrics spec) const DEFAULT_UIMAP=JSON.parse(JSON.stringify(UIMAP)); -function syntaxBlank(k){return {fg:MAP[k]||null,bg:null,bold:false,italic:false,underline:false,strike:false,box:null};} +function syntaxBlank(k){return {fg:MAP[k]||null,bg:null,'distant-fg':null,family:null,bold:false,italic:false,underline:false,strike:false,overline:null,box:null,inverse:false,extend:false,inherit:null,height:null};} function syncSyntaxCache(k){const s=SYNTAX[k]||syntaxBlank(k);MAP[k]=s.fg||'';} function syncAllSyntaxCache(){CATS.forEach(c=>syncSyntaxCache(c[0]));} function syncSyntaxFromCache(){CATS.forEach(c=>{const k=c[0];syntaxFace(k).fg=MAP[k]||null;});} @@ -514,8 +514,8 @@ function buildMockFrame(){ function uiSelect(face,attr){const cur=UIMAP[face][attr]||''; return mkColorDropdown(ddList(cur),cur,h=>{UIMAP[face][attr]=h||null;paintUI(face);buildMockFrame();},{compact:true,defaultHex:attr==='fg'?effFg(null):effBg(null)});} 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 uiFaceBlank(){return {fg:null,bg:null,'distant-fg':null,family:null,bold:false,italic:false,underline:false,strike:false,overline:null,box:null,inverse:false,extend:false,inherit:null,height:null};} +function seedFace(d){return normalizePkgFace({fg:pname(d.fg),bg:pname(d.bg),'distant-fg':pname(d['distant-fg']),family:d.family,bold:d.bold,italic:d.italic,underline:d.underline,strike:d.strike,overline:d.overline,inherit:d.inherit,height:d.height,box:d.box,inverse:d.inverse,extend:d.extend},'default');} 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);} -- cgit v1.2.3