@taprootio/espalier 4.18.0 → 4.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- import{encodeTheme as D,isObjectRecord as p,isSemanticColorName as P,validateThemePair as G}from"../shared/theme.js";import{THEME_FIT_ANCHOR_FIELDS as C,THEME_FIT_APCA_FIELDS as N,THEME_FIT_DATA_PALETTE_LINT_FIELDS as M,THEME_FIT_DATA_PALETTE_LINT_IDS as X,THEME_FIT_EXPLICIT_VALUES as Y,THEME_FIT_LINT_FIELDS as R,THEME_FIT_LINT_IDS as B,THEME_FIT_LINT_SEVERITIES as Q,THEME_FIT_REPORT_FIELDS as _,THEME_FIT_SUITE_FIELDS as k,THEME_FIT_TOKEN_FIELDS as x,themeFitReportSuite as Z}from"../shared/theme-fit-report.js";import{COLOR_VISION_SIMULATIONS as v,DATA_SERIES_KEYS as w,isPaletteCollisionDescription as ee}from"../shared/data-colors.js";import{isSafeKey as te}from"../shared/unsafe-keys.js";const A="usage: espalier theme check <theme.json> [--json] [--expect <fit.json>] [--update-expect <fit.json>]";function Me(e,t){const[r,i,...n]=e;if(r!=="theme"||i!=="check")return t.error(A),2;const s=re(n,t);return s?ie(s,t):2}function re(e,t){const r={file:"",json:!1};for(let i=0;i<e.length;i+=1){const n=e[i];if(n==="--json")r.json=!0;else if(n==="--expect"||n==="--update-expect"){const s=e[i+1];if(s===void 0)return t.error(`${n} requires a file path. ${A}`),null;n==="--expect"?r.expect=s:r.updateExpect=s,i+=1}else if(!n.startsWith("--")&&r.file==="")r.file=n;else return t.error(`Unknown argument "${n}". ${A}`),null}return r.file===""?(t.error(A),null):r.expect!==void 0&&r.updateExpect!==void 0?(t.error(`--expect checks the committed fit tables and --update-expect rewrites them \u2014 pick one. ${A}`),null):r}function ne(e,t){let r;try{r=t.readFile(e)}catch(s){return t.error(`Cannot read ${e}: ${s instanceof Error?s.message:String(s)}`),null}let i;try{i=JSON.parse(r)}catch(s){return t.error(`${e} is not JSON: ${s instanceof Error?s.message:String(s)}`),null}if(typeof i!="object"||i===null||Array.isArray(i))return t.error(`${e} must contain a theme object.`),null;const n=i;if("light"in n||"dark"in n){for(const s of["light","dark"]){const o=n[s];if(o!==void 0&&(typeof o!="object"||o===null||Array.isArray(o)))return t.error(`${e}: "${s}" must be a theme object.`),null}return{light:n.light??{},dark:n.dark??{}}}return{light:n,dark:n}}function ie(e,t){const r=ne(e.file,t);if(!r)return 2;const i=G(D(r.light),D(r.dark));if(!i.valid){for(const c of i.warnings)t.error(`warning: ${c}`);for(const c of i.errors)t.error(`error: ${c}`);return t.error(`${e.file}: ${i.errors.length} validation error(s).`),1}const n=Z(r.light,r.dark);for(const c of i.warnings)Ie(c,n)||t.error(`warning: ${c}`);const s=Ae(n),o=e.json?t.error:t.log;if(e.updateExpect!==void 0){try{t.writeFile(e.updateExpect,`${JSON.stringify(n,null,2)}
2
- `)}catch(c){return t.error(`Cannot write ${e.updateExpect}: ${c instanceof Error?c.message:String(c)}`),2}o(`Wrote fit tables to ${e.updateExpect}.`)}if(e.expect!==void 0){let c;try{c=t.readFile(e.expect)}catch{return t.error(`Expected fit tables not found at ${e.expect} \u2014 create them with --update-expect ${e.expect}.`),2}let a;try{a=JSON.parse(c)}catch{return t.error(`${e.expect} is not JSON.`),2}if(se(a))return t.error(`${e.expect} was written by espalier \u22644.2.0 \u2014 regenerate it with --update-expect ${e.expect}.`),2;if(!oe(a))return t.error(`${e.expect} is not a fit-table suite \u2014 regenerate it with --update-expect ${e.expect}.`),2;const f=le(a,n);if(f.length>0){s>0&&we(n,s,t);for(const u of f)t.error(`drift: ${u}`);return t.error(`Fit tables drifted from ${e.expect} (${f.length} difference(s)). Review, then refresh deliberately with --update-expect.`),1}o(`Fit tables match ${e.expect}.`)}return e.json?(t.log(JSON.stringify(n,null,2)),s>0?1:0):(xe(n,s,t),s>0?1:0)}function oe(e){if(!p(e)||!k.every(({field:r,kind:i})=>T(e[String(r)],i)))return!1;const t=e;if(!I(t.surfaces))return!1;for(const r of["light","dark"]){const n=t.paletteLints[r].map(g);if(!I(n))return!1;const s=t[r],o=new Set;for(const c of s){if(!pe(c,r)||!t.surfaces.includes(c.surface)||o.has(c.surface))return!1;o.add(c.surface)}}return!0}function se(e){return p(e)&&H(e.surfaces)&&Array.isArray(e.light)&&Array.isArray(e.dark)&&!("dataPalette"in e)&&!("paletteLints"in e)}function ce(e){return typeof e=="number"&&Number.isFinite(e)}function H(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function I(e){return new Set(e).size===e.length}function ae(e){return p(e)&&M.every(({field:t,kind:r})=>T(e[String(t)],r))}function fe(e){return p(e)&&R.every(({field:t,kind:r})=>T(e[String(t)],r))}function ue(e){return p(e)&&x.every(({field:t,kind:r})=>T(e[String(t)],r))}function T(e,t){switch(t){case"anchor":return e===void 0||V(e,C);case"anchor-record":return p(e)&&Object.keys(e).every(te)&&Object.values(e).every(r=>typeof r=="string");case"apca":return e===void 0||V(e,N);case"boolean":return typeof e=="boolean";case"data-palette-record":return p(e)?["light","dark"].every(r=>{const i=e[r];return p(i)&&w.every(n=>typeof i[n]=="string")}):!1;case"explicit":return e===void 0||Y.includes(e);case"fit-lint-id":return B.includes(e);case"lint-array":return Array.isArray(e)&&e.every(fe);case"message":case"string":return typeof e=="string";case"number":return ce(e);case"palette-lint-id":return X.includes(e);case"palette-lint-record":return p(e)&&["light","dark"].every(r=>Array.isArray(e[r])&&e[r].every(ae));case"report-array":return Array.isArray(e);case"scheme":return e==="light"||e==="dark";case"semantic":return P(e);case"semantic-array":return Array.isArray(e)&&e.every(P);case"series-pair":return de(e);case"simulation":return v.includes(e);case"string-array":return H(e);case"token-array":return Array.isArray(e)&&e.every(ue);case"warning":return Q.includes(e);default:{const r=t;return!1}}}function V(e,t){return p(e)&&t.every(({field:r,kind:i})=>T(e[String(r)],i))}function de(e){if(!Array.isArray(e)||e.length!==2)return!1;const t=w.indexOf(e[0]),r=w.indexOf(e[1]);return t>=0&&r>t}function pe(e,t){if(!p(e)||!_.every(({field:i,kind:n})=>T(e[String(i)],n)))return!1;const r=e;return r.scheme!==t?!1:I(r.tokens.map(i=>i.token))&&I(r.adjustedTokens)&&I(r.lints.map(i=>i.id))}function le(e,t){const r=[];for(const n of k)n.compared&&me(r,n,e,t);return(h(z(e))!==h(z(t))||r.length===0&&h(e)!==h(t))&&r.push("fit tables differ outside compared fields"),r}function me(e,t,r,i){const{field:n,kind:s}=t;if(s==="string-array"){const o=r[n],c=i[n];U(e,String(n),o,c),E(e,String(n),o,c);return}if(s==="data-palette-record"){const o=r[n],c=i[n];for(const a of["light","dark"])for(const f of w)K(e,`${a}/${String(n)}/${f}`,o[a][f],c[a][f]);return}if(s==="palette-lint-record"){const o=r[n],c=i[n];for(const a of["light","dark"])ke(e,a,String(n),o[a],c[a]),E(e,`${a}/${String(n)}`,o[a].map(g),c[a].map(g));return}if(s==="report-array"&&(n==="light"||n==="dark")){he(e,n,r[n],i[n]);return}F(e,"fit tables",String(n),r[n],i[n],s)}function he(e,t,r,i){const n=new Map(r.map(o=>[o.surface,o])),s=new Map(i.map(o=>[o.surface,o]));E(e,`${t}/reports`,r.map(o=>o.surface),i.map(o=>o.surface));for(const o of L(n.keys(),s.keys())){const c=n.get(o),a=s.get(o),f=`${t}/${o}`;if(!c||!a){e.push(`${f}: surface ${c?"removed":"added"}`);continue}ge(e,f,c,a)}}function ge(e,t,r,i){for(const n of _){const{compared:s,field:o,kind:c}=n;if(!(!s||o==="scheme"||o==="surface")){if(c==="anchor-record"){Te(e,`${t}/${String(o)}`,r[o],i[o]);continue}if(c==="semantic-array"){const a=r[o],f=i[o];U(e,`${t}/${String(o)}`,a,f),E(e,`${t}/${String(o)}`,a,f);continue}if(c==="lint-array"){const a=r[o],f=i[o];Se(e,t,String(o),a,f),E(e,`${t}/${String(o)}`,a.map(u=>u.id),f.map(u=>u.id));continue}if(c==="token-array"){$e(e,t,String(o),r[o],i[o]);continue}F(e,t,String(o),r[o],i[o],c)}}}function $e(e,t,r,i,n){const s=new Map(i.map(a=>[a.token,a])),o=new Map(n.map(a=>[a.token,a]));E(e,`${t}/${r}`,i.map(a=>a.token),n.map(a=>a.token));const c=r==="tokens"?t:`${t}/${r}`;for(const a of L(s.keys(),o.keys())){const f=s.get(a),u=o.get(a),S=`${c}/${a}`;if(!f||!u){e.push(`${S}: token ${f?"removed":"added"}`);continue}ye(e,S,f,u)}}function ye(e,t,r,i){for(const{compared:n,field:s,kind:o}of x)if(!(!n||s==="token")){if(o==="anchor"){J(e,t,"anchor",r.anchor,i.anchor,C);continue}if(o==="apca"){J(e,t,"apca",r.apca,i.apca,N);continue}F(e,t,String(s),r[s],i[s],o)}}function J(e,t,r,i,n,s){const o=i,c=n;for(const{compared:a,field:f,kind:u}of s)a&&F(e,t,`${r}.${String(f)}`,o?.[f],c?.[f],u)}function Se(e,t,r,i,n){const s=new Map(i.map(c=>[c.id,c])),o=new Map(n.map(c=>[c.id,c]));for(const c of L(s.keys(),o.keys())){const a=s.get(c),f=o.get(c),u=`${t}/${r}/${c}`;if(!a||!f){e.push(`${u}: lint ${a?"removed":"added"}`);continue}for(const{compared:S,field:d,kind:b}of R)if(!(!S||d==="id")){if(b==="semantic-array"){Ee(e,u,String(d),a[d],f[d]);continue}F(e,u,String(d),a[d],f[d],b)}}}function ke(e,t,r,i,n){const s=new Map(i.map(c=>[g(c),c])),o=new Map(n.map(c=>[g(c),c]));for(const c of L(s.keys(),o.keys())){const a=s.get(c),f=o.get(c),u=`${t}/${r}/${c}`;if(!a||!f){e.push(`${u}: lint ${a?"removed":"added"}`);continue}for(const{compared:S,field:d,kind:b}of M)!S||d==="id"||d==="series"||d==="simulation"||F(e,u,String(d),a[d],f[d],b)}}function g(e){return`${e.id}/${e.series[0]}+${e.series[1]}/${e.simulation}`}function Te(e,t,r,i){for(const n of L(Object.keys(r),Object.keys(i)))K(e,`${t}/${n}`,r[n],i[n])}function U(e,t,r,i){const n=new Set(r),s=new Set(i);for(const o of L(n,s))n.has(o)!==s.has(o)&&e.push(`${t}/${o}: ${n.has(o)?"removed":"added"}`)}function E(e,t,r,i){const n=new Set(r),s=new Set(i),o=r.filter(a=>s.has(a)),c=i.filter(a=>n.has(a));o.some((a,f)=>a!==c[f])&&e.push(`${t}: order changed`)}function Ee(e,t,r,i,n){const s=Math.max(i.length,n.length);for(let o=0;o<s;o+=1)W(e,t,`${r}[${o}]`,i[o],n[o])}function K(e,t,r,i){r!==i&&e.push(`${t}: ${j(r)} \u2192 ${j(i)}`)}function W(e,t,r,i,n,s){if(!Object.is(i,n)){if(s==="message"){e.push(`${t}: message changed`);return}e.push(`${t}: ${r} ${j(i)} \u2192 ${j(n)}`)}}function Fe(e,t,r,i,n){h(i)!==h(n)&&e.push(`${t}: ${r} changed`)}function F(e,t,r,i,n,s){if(_e(s)){W(e,t,r,i,n,s);return}Fe(e,t,r,i,n)}const Le={anchor:!1,"anchor-record":!1,apca:!1,boolean:!0,"data-palette-record":!1,explicit:!0,"fit-lint-id":!0,"lint-array":!1,message:!0,number:!0,"palette-lint-id":!0,"palette-lint-record":!1,"report-array":!1,scheme:!0,semantic:!0,"semantic-array":!1,"series-pair":!1,simulation:!0,string:!0,"string-array":!1,"token-array":!1,warning:!0};function _e(e){return Le[e]===!0}function j(e){return e===void 0?"(absent)":e===""?'""':String(e)}function L(e,t){return[...new Set([...e,...t])].sort()}function h(e){return Array.isArray(e)?`[${e.map(h).join(",")}]`:p(e)?`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${h(e[t])}`).join(",")}}`:JSON.stringify(e)??"undefined"}function z(e){const t=m(e,$(k)),r=["light","dark"];if(y(k,"dataPalette")){const i=m(e.dataPalette,r);for(const n of r)l(i,n,m(e.dataPalette[n],w));l(t,"dataPalette",i)}if(y(k,"paletteLints")){const i=m(e.paletteLints,r);for(const n of r)l(i,n,q(e.paletteLints[n],g,$(M)));l(t,"paletteLints",i)}for(const i of r){if(!y(k,i))continue;const n=O();for(const s of e[i]){const o=m(s,$(_));if(y(_,"tokens")){const c=O();for(const a of s.tokens){const f=m(a,$(x));a.anchor&&y(x,"anchor")&&l(f,"anchor",m(a.anchor,$(C))),a.apca&&y(x,"apca")&&l(f,"apca",m(a.apca,$(N))),l(c,a.token,f)}l(o,"tokens",c)}y(_,"lints")&&l(o,"lints",q(s.lints,c=>c.id,$(R))),l(n,s.surface,o)}l(t,i,n)}return t}function q(e,t,r){const i=O();for(const n of e)l(i,t(n),m(n,r));return i}function $(e){return e.filter(({compared:t})=>t).map(({field:t})=>t)}function y(e,t){return e.some(r=>r.field===t&&r.compared)}function m(e,t){const r=new Set(t.map(String)),i=O();for(const[n,s]of Object.entries(e))r.has(n)||(i[n]=s);return i}function O(){return Object.create(null)}function l(e,t,r){Object.keys(r).length>0&&(e[t]=r)}function xe(e,t,r){for(const i of["light","dark"]){for(const n of e.paletteLints[i])r.log(` ${i} lint(${n.id}): ${n.message}`);for(const n of e[i]){r.log(be(i,n));for(const s of n.lints)r.log(` lint(${s.id}): ${s.message}`)}}r.log(`Checked ${e.surfaces.length} surface(s) \xD7 2 schemes \u2014 `+(t===0?"no lints.":`${t} lint(s), listed above.`))}function we(e,t,r){r.error(`${t} theme lint(s) are standing while fit-table drift is present:`);for(const i of["light","dark"]){for(const n of e.paletteLints[i])r.error(`lint: ${i}/paletteLints/${g(n)}: ${n.message}`);for(const n of e[i])for(const s of n.lints)r.error(`lint: ${i}/${n.surface}/lints/${s.id}: ${s.message}`)}}function Ae(e){return[...e.light,...e.dark].reduce((r,i)=>r+i.lints.length,0)+e.paletteLints.light.length+e.paletteLints.dark.length}function Ie(e,t){return["light","dark"].some(r=>{const i=`${r}: `;if(!e.startsWith(i))return!1;const n=e.slice(i.length);return t.paletteLints[r].some(s=>ee(n,s))})}function be(e,t){const r=t.adjustedTokens.length===0?"contrast enforcement moved nothing":`contrast enforcement moved ${t.adjustedTokens.join(", ")}`,i=t.tokens.filter(s=>s.explicit==="inherited").length,n=i===0?"":`; ${i} inherited explicit pin(s) \u2014 see the fit tables`;return`${e}/${t.surface}: ${t.tokens.length} tokens, ${r}${n}.`}export{Me as runEspalierCli};
1
+ import{encodeTheme as P,isObjectRecord as p,isSemanticColorName as D,validateThemePair as G}from"../shared/theme.js";import{THEME_FIT_ANCHOR_FIELDS as N,THEME_FIT_APCA_FIELDS as C,THEME_FIT_DATA_PALETTE_LINT_FIELDS as M,THEME_FIT_DATA_PALETTE_LINT_IDS as X,THEME_FIT_EXPLICIT_VALUES as Y,THEME_FIT_LINT_FIELDS as R,THEME_FIT_LINT_IDS as B,THEME_FIT_LINT_SEVERITIES as Q,THEME_FIT_PAIRING_VALUES as Z,THEME_FIT_REPORT_FIELDS as _,THEME_FIT_SUITE_FIELDS as k,THEME_FIT_TOKEN_FIELDS as x,themeFitReportSuite as v}from"../shared/theme-fit-report.js";import{COLOR_VISION_SIMULATIONS as ee,DATA_SERIES_KEYS as A,isPaletteCollisionDescription as te}from"../shared/data-colors.js";import{isSafeKey as re}from"../shared/unsafe-keys.js";const I="usage: espalier theme check <theme.json> [--json] [--expect <fit.json>] [--update-expect <fit.json>]";function Re(e,t){const[r,i,...n]=e;if(r!=="theme"||i!=="check")return t.error(I),2;const s=ne(n,t);return s?oe(s,t):2}function ne(e,t){const r={file:"",json:!1};for(let i=0;i<e.length;i+=1){const n=e[i];if(n==="--json")r.json=!0;else if(n==="--expect"||n==="--update-expect"){const s=e[i+1];if(s===void 0)return t.error(`${n} requires a file path. ${I}`),null;n==="--expect"?r.expect=s:r.updateExpect=s,i+=1}else if(!n.startsWith("--")&&r.file==="")r.file=n;else return t.error(`Unknown argument "${n}". ${I}`),null}return r.file===""?(t.error(I),null):r.expect!==void 0&&r.updateExpect!==void 0?(t.error(`--expect checks the committed fit tables and --update-expect rewrites them \u2014 pick one. ${I}`),null):r}function ie(e,t){let r;try{r=t.readFile(e)}catch(s){return t.error(`Cannot read ${e}: ${s instanceof Error?s.message:String(s)}`),null}let i;try{i=JSON.parse(r)}catch(s){return t.error(`${e} is not JSON: ${s instanceof Error?s.message:String(s)}`),null}if(typeof i!="object"||i===null||Array.isArray(i))return t.error(`${e} must contain a theme object.`),null;const n=i;if("light"in n||"dark"in n){for(const s of["light","dark"]){const o=n[s];if(o!==void 0&&(typeof o!="object"||o===null||Array.isArray(o)))return t.error(`${e}: "${s}" must be a theme object.`),null}return{light:n.light??{},dark:n.dark??{}}}return{light:n,dark:n}}function oe(e,t){const r=ie(e.file,t);if(!r)return 2;const i=G(P(r.light),P(r.dark));if(!i.valid){for(const c of i.warnings)t.error(`warning: ${c}`);for(const c of i.errors)t.error(`error: ${c}`);return t.error(`${e.file}: ${i.errors.length} validation error(s).`),1}const n=v(r.light,r.dark);for(const c of i.warnings)be(c,n)||t.error(`warning: ${c}`);const s=we(n),o=e.json?t.error:t.log;if(e.updateExpect!==void 0){try{t.writeFile(e.updateExpect,`${JSON.stringify(n,null,2)}
2
+ `)}catch(c){return t.error(`Cannot write ${e.updateExpect}: ${c instanceof Error?c.message:String(c)}`),2}o(`Wrote fit tables to ${e.updateExpect}.`)}if(e.expect!==void 0){let c;try{c=t.readFile(e.expect)}catch{return t.error(`Expected fit tables not found at ${e.expect} \u2014 create them with --update-expect ${e.expect}.`),2}let a;try{a=JSON.parse(c)}catch{return t.error(`${e.expect} is not JSON.`),2}if(ce(a))return t.error(`${e.expect} was written by espalier \u22644.2.0 \u2014 regenerate it with --update-expect ${e.expect}.`),2;if(!se(a))return t.error(`${e.expect} is not a fit-table suite \u2014 regenerate it with --update-expect ${e.expect}.`),2;const f=me(a,n);if(f.length>0){s>0&&Ie(n,s,t);for(const u of f)t.error(`drift: ${u}`);return t.error(`Fit tables drifted from ${e.expect} (${f.length} difference(s)). Review, then refresh deliberately with --update-expect.`),1}o(`Fit tables match ${e.expect}.`)}return e.json?(t.log(JSON.stringify(n,null,2)),s>0?1:0):(Ae(n,s,t),s>0?1:0)}function se(e){if(!p(e)||!k.every(({field:r,kind:i})=>T(e[String(r)],i)))return!1;const t=e;if(!w(t.surfaces))return!1;for(const r of["light","dark"]){const n=t.paletteLints[r].map(h);if(!w(n))return!1;const s=t[r],o=new Set;for(const c of s){if(!le(c,r)||!t.surfaces.includes(c.surface)||o.has(c.surface))return!1;o.add(c.surface)}}return!0}function ce(e){return p(e)&&H(e.surfaces)&&Array.isArray(e.light)&&Array.isArray(e.dark)&&!("dataPalette"in e)&&!("paletteLints"in e)}function ae(e){return typeof e=="number"&&Number.isFinite(e)}function H(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function w(e){return new Set(e).size===e.length}function fe(e){return p(e)&&M.every(({field:t,kind:r})=>T(e[String(t)],r))}function ue(e){return p(e)&&R.every(({field:t,kind:r})=>T(e[String(t)],r))}function de(e){return p(e)&&x.every(({field:t,kind:r})=>T(e[String(t)],r))}function T(e,t){switch(t){case"anchor":return e===void 0||V(e,N);case"anchor-record":return p(e)&&Object.keys(e).every(re)&&Object.values(e).every(r=>typeof r=="string");case"apca":return e===void 0||V(e,C);case"boolean":return typeof e=="boolean";case"data-palette-record":return p(e)?["light","dark"].every(r=>{const i=e[r];return p(i)&&A.every(n=>typeof i[n]=="string")}):!1;case"explicit":return e===void 0||Y.includes(e);case"fit-lint-id":return B.includes(e);case"lint-array":return Array.isArray(e)&&e.every(ue);case"message":case"string":return typeof e=="string";case"number":return ae(e);case"pairing":return e===void 0||Z.includes(e);case"palette-lint-id":return X.includes(e);case"palette-lint-record":return p(e)&&["light","dark"].every(r=>Array.isArray(e[r])&&e[r].every(fe));case"report-array":return Array.isArray(e);case"scheme":return e==="light"||e==="dark";case"semantic":return D(e);case"semantic-array":return Array.isArray(e)&&e.every(D);case"series-pair":return pe(e);case"simulation":return ee.includes(e);case"string-array":return H(e);case"token-array":return Array.isArray(e)&&e.every(de);case"warning":return Q.includes(e);default:{const r=t;return!1}}}function V(e,t){return p(e)&&t.every(({field:r,kind:i})=>T(e[String(r)],i))}function pe(e){if(!Array.isArray(e)||e.length!==2)return!1;const t=A.indexOf(e[0]),r=A.indexOf(e[1]);return t>=0&&r>t}function le(e,t){if(!p(e)||!_.every(({field:i,kind:n})=>T(e[String(i)],n)))return!1;const r=e;return r.scheme!==t?!1:w(r.tokens.map(i=>i.token))&&w(r.adjustedTokens)&&w(r.lints.map(i=>i.id))}function me(e,t){const r=[];for(const n of k)n.compared&&ge(r,n,e,t);return(g(z(e))!==g(z(t))||r.length===0&&g(e)!==g(t))&&r.push("fit tables differ outside compared fields"),r}function ge(e,t,r,i){const{field:n,kind:s}=t;if(s==="string-array"){const o=r[n],c=i[n];J(e,String(n),o,c),E(e,String(n),o,c);return}if(s==="data-palette-record"){const o=r[n],c=i[n];for(const a of["light","dark"])for(const f of A)K(e,`${a}/${String(n)}/${f}`,o[a][f],c[a][f]);return}if(s==="palette-lint-record"){const o=r[n],c=i[n];for(const a of["light","dark"])Te(e,a,String(n),o[a],c[a]),E(e,`${a}/${String(n)}`,o[a].map(h),c[a].map(h));return}if(s==="report-array"&&(n==="light"||n==="dark")){he(e,n,r[n],i[n]);return}F(e,"fit tables",String(n),r[n],i[n],s)}function he(e,t,r,i){const n=new Map(r.map(o=>[o.surface,o])),s=new Map(i.map(o=>[o.surface,o]));E(e,`${t}/reports`,r.map(o=>o.surface),i.map(o=>o.surface));for(const o of L(n.keys(),s.keys())){const c=n.get(o),a=s.get(o),f=`${t}/${o}`;if(!c||!a){e.push(`${f}: surface ${c?"removed":"added"}`);continue}$e(e,f,c,a)}}function $e(e,t,r,i){for(const n of _){const{compared:s,field:o,kind:c}=n;if(!(!s||o==="scheme"||o==="surface")){if(c==="anchor-record"){Ee(e,`${t}/${String(o)}`,r[o],i[o]);continue}if(c==="semantic-array"){const a=r[o],f=i[o];J(e,`${t}/${String(o)}`,a,f),E(e,`${t}/${String(o)}`,a,f);continue}if(c==="lint-array"){const a=r[o],f=i[o];ke(e,t,String(o),a,f),E(e,`${t}/${String(o)}`,a.map(u=>u.id),f.map(u=>u.id));continue}if(c==="token-array"){ye(e,t,String(o),r[o],i[o]);continue}F(e,t,String(o),r[o],i[o],c)}}}function ye(e,t,r,i,n){const s=new Map(i.map(a=>[a.token,a])),o=new Map(n.map(a=>[a.token,a]));E(e,`${t}/${r}`,i.map(a=>a.token),n.map(a=>a.token));const c=r==="tokens"?t:`${t}/${r}`;for(const a of L(s.keys(),o.keys())){const f=s.get(a),u=o.get(a),S=`${c}/${a}`;if(!f||!u){e.push(`${S}: token ${f?"removed":"added"}`);continue}Se(e,S,f,u)}}function Se(e,t,r,i){for(const{compared:n,field:s,kind:o}of x)if(!(!n||s==="token")){if(o==="anchor"){U(e,t,"anchor",r.anchor,i.anchor,N);continue}if(o==="apca"){U(e,t,"apca",r.apca,i.apca,C);continue}F(e,t,String(s),r[s],i[s],o)}}function U(e,t,r,i,n,s){const o=i,c=n;for(const{compared:a,field:f,kind:u}of s)a&&F(e,t,`${r}.${String(f)}`,o?.[f],c?.[f],u)}function ke(e,t,r,i,n){const s=new Map(i.map(c=>[c.id,c])),o=new Map(n.map(c=>[c.id,c]));for(const c of L(s.keys(),o.keys())){const a=s.get(c),f=o.get(c),u=`${t}/${r}/${c}`;if(!a||!f){e.push(`${u}: lint ${a?"removed":"added"}`);continue}for(const{compared:S,field:d,kind:b}of R)if(!(!S||d==="id")){if(b==="semantic-array"){Fe(e,u,String(d),a[d],f[d]);continue}F(e,u,String(d),a[d],f[d],b)}}}function Te(e,t,r,i,n){const s=new Map(i.map(c=>[h(c),c])),o=new Map(n.map(c=>[h(c),c]));for(const c of L(s.keys(),o.keys())){const a=s.get(c),f=o.get(c),u=`${t}/${r}/${c}`;if(!a||!f){e.push(`${u}: lint ${a?"removed":"added"}`);continue}for(const{compared:S,field:d,kind:b}of M)!S||d==="id"||d==="series"||d==="simulation"||F(e,u,String(d),a[d],f[d],b)}}function h(e){return`${e.id}/${e.series[0]}+${e.series[1]}/${e.simulation}`}function Ee(e,t,r,i){for(const n of L(Object.keys(r),Object.keys(i)))K(e,`${t}/${n}`,r[n],i[n])}function J(e,t,r,i){const n=new Set(r),s=new Set(i);for(const o of L(n,s))n.has(o)!==s.has(o)&&e.push(`${t}/${o}: ${n.has(o)?"removed":"added"}`)}function E(e,t,r,i){const n=new Set(r),s=new Set(i),o=r.filter(a=>s.has(a)),c=i.filter(a=>n.has(a));o.some((a,f)=>a!==c[f])&&e.push(`${t}: order changed`)}function Fe(e,t,r,i,n){const s=Math.max(i.length,n.length);for(let o=0;o<s;o+=1)W(e,t,`${r}[${o}]`,i[o],n[o])}function K(e,t,r,i){r!==i&&e.push(`${t}: ${j(r)} \u2192 ${j(i)}`)}function W(e,t,r,i,n,s){if(!Object.is(i,n)){if(s==="message"){e.push(`${t}: message changed`);return}e.push(`${t}: ${r} ${j(i)} \u2192 ${j(n)}`)}}function Le(e,t,r,i,n){g(i)!==g(n)&&e.push(`${t}: ${r} changed`)}function F(e,t,r,i,n,s){if(xe(s)){W(e,t,r,i,n,s);return}Le(e,t,r,i,n)}const _e={anchor:!1,"anchor-record":!1,apca:!1,boolean:!0,"data-palette-record":!1,explicit:!0,"fit-lint-id":!0,"lint-array":!1,message:!0,number:!0,pairing:!0,"palette-lint-id":!0,"palette-lint-record":!1,"report-array":!1,scheme:!0,semantic:!0,"semantic-array":!1,"series-pair":!1,simulation:!0,string:!0,"string-array":!1,"token-array":!1,warning:!0};function xe(e){return _e[e]===!0}function j(e){return e===void 0?"(absent)":e===""?'""':String(e)}function L(e,t){return[...new Set([...e,...t])].sort()}function g(e){return Array.isArray(e)?`[${e.map(g).join(",")}]`:p(e)?`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${g(e[t])}`).join(",")}}`:JSON.stringify(e)??"undefined"}function z(e){const t=m(e,$(k)),r=["light","dark"];if(y(k,"dataPalette")){const i=m(e.dataPalette,r);for(const n of r)l(i,n,m(e.dataPalette[n],A));l(t,"dataPalette",i)}if(y(k,"paletteLints")){const i=m(e.paletteLints,r);for(const n of r)l(i,n,q(e.paletteLints[n],h,$(M)));l(t,"paletteLints",i)}for(const i of r){if(!y(k,i))continue;const n=O();for(const s of e[i]){const o=m(s,$(_));if(y(_,"tokens")){const c=O();for(const a of s.tokens){const f=m(a,$(x));a.anchor&&y(x,"anchor")&&l(f,"anchor",m(a.anchor,$(N))),a.apca&&y(x,"apca")&&l(f,"apca",m(a.apca,$(C))),l(c,a.token,f)}l(o,"tokens",c)}y(_,"lints")&&l(o,"lints",q(s.lints,c=>c.id,$(R))),l(n,s.surface,o)}l(t,i,n)}return t}function q(e,t,r){const i=O();for(const n of e)l(i,t(n),m(n,r));return i}function $(e){return e.filter(({compared:t})=>t).map(({field:t})=>t)}function y(e,t){return e.some(r=>r.field===t&&r.compared)}function m(e,t){const r=new Set(t.map(String)),i=O();for(const[n,s]of Object.entries(e))r.has(n)||(i[n]=s);return i}function O(){return Object.create(null)}function l(e,t,r){Object.keys(r).length>0&&(e[t]=r)}function Ae(e,t,r){for(const i of["light","dark"]){for(const n of e.paletteLints[i])r.log(` ${i} lint(${n.id}): ${n.message}`);for(const n of e[i]){r.log(je(i,n));for(const s of n.lints)r.log(` lint(${s.id}): ${s.message}`)}}r.log(`Checked ${e.surfaces.length} surface(s) \xD7 2 schemes \u2014 `+(t===0?"no lints.":`${t} lint(s), listed above.`))}function Ie(e,t,r){r.error(`${t} theme lint(s) are standing while fit-table drift is present:`);for(const i of["light","dark"]){for(const n of e.paletteLints[i])r.error(`lint: ${i}/paletteLints/${h(n)}: ${n.message}`);for(const n of e[i])for(const s of n.lints)r.error(`lint: ${i}/${n.surface}/lints/${s.id}: ${s.message}`)}}function we(e){return[...e.light,...e.dark].reduce((r,i)=>r+i.lints.length,0)+e.paletteLints.light.length+e.paletteLints.dark.length}function be(e,t){return["light","dark"].some(r=>{const i=`${r}: `;if(!e.startsWith(i))return!1;const n=e.slice(i.length);return t.paletteLints[r].some(s=>te(n,s))})}function je(e,t){const r=t.adjustedTokens.length===0?"contrast enforcement moved nothing":`contrast enforcement moved ${t.adjustedTokens.join(", ")}`,i=t.tokens.filter(s=>s.explicit==="inherited").length,n=i===0?"":`; ${i} inherited explicit pin(s) \u2014 see the fit tables`;return`${e}/${t.surface}: ${t.tokens.length} tokens, ${r}${n}.`}export{Re as runEspalierCli};
@@ -1,4 +1,4 @@
1
- import{css as e}from"lit";const r=e`
1
+ import{css as e}from"lit";const t=e`
2
2
 
3
3
 
4
4
  .temporal-error {
@@ -109,7 +109,7 @@ import{css as e}from"lit";const r=e`
109
109
 
110
110
 
111
111
  &:focus-visible {
112
- outline: 2px solid var(--esp-color-action-text);
112
+ outline: 2px solid var(--esp-color-text);
113
113
  outline-offset: 2px;
114
114
  }
115
115
  }
@@ -135,7 +135,7 @@ import{css as e}from"lit";const r=e`
135
135
  }
136
136
 
137
137
  &:focus-visible {
138
- outline: 2px solid var(--esp-color-action-text);
138
+ outline: 2px solid var(--esp-color-text);
139
139
  outline-offset: -2px;
140
140
  }
141
141
  }
@@ -165,7 +165,7 @@ import{css as e}from"lit";const r=e`
165
165
 
166
166
 
167
167
  .day-grid:focus-within .day-cell.focused {
168
- outline: 2px solid var(--esp-color-action-text);
168
+ outline: 2px solid var(--esp-color-text);
169
169
  outline-offset: -2px;
170
170
  z-index: 1;
171
171
  }
@@ -199,19 +199,29 @@ import{css as e}from"lit";const r=e`
199
199
  }
200
200
 
201
201
  &:focus-visible {
202
- outline: 2px solid var(--esp-color-action-text);
202
+ outline: 2px solid var(--esp-color-text);
203
203
  outline-offset: -2px;
204
204
  z-index: 1;
205
205
  }
206
206
 
207
207
 
208
208
  &.selected {
209
- background-color: var(--esp-color-layer-3);
209
+ background-color: var(--esp-color-action-background);
210
210
  color: var(--esp-color-action-text);
211
211
  font-weight: bold;
212
212
  }
213
213
  }
214
214
 
215
+
216
+ .day-grid:focus-within .day-cell.focused.selected,
217
+ .jump-cell.selected:focus-visible {
218
+ outline-color: var(--esp-color-action-text);
219
+ }
220
+
221
+ .day-cell.today.selected {
222
+ box-shadow: inset 0 0 0 1px var(--esp-color-action-text);
223
+ }
224
+
215
225
  .day-cell {
216
226
  width: var(--_cell-size);
217
227
  height: var(--_cell-size);
@@ -321,4 +331,4 @@ import{css as e}from"lit";const r=e`
321
331
  color: var(--esp-color-headings);
322
332
  padding: 0 2px;
323
333
  }
324
- `;export{r as datePickerStyles};
334
+ `;export{t as datePickerStyles};
package/dist/index.d.ts CHANGED
@@ -80,6 +80,6 @@ export { canonicalFontStack, classifyFontStack, customWebSafeFont, isDeviceFontS
80
80
  export { WEIGHT_LABELS, extractWeights, normalizeWeight, bestAvailableWeight, extractFamily, getFallbackFont, googleFontStylesheetUrl, isLocalFontFamily, normalizeFontFaceWeight, removeRuntimeGoogleFontLink, removeRuntimeGoogleFontLinks, syncRuntimeGoogleFontLink, RUNTIME_GOOGLE_FONT_OWNER_ATTR, type RuntimeGoogleFontLinkOptions, } from "./shared/font-helpers.js";
81
81
  export { compileFontPlan, fontFallbackAliases, fontFallbackBaseIndexes, fontFaceRequestKey, type CompileFontPlanOptions, type CompiledFontPlan, type FontFaceRequest, type FontFallbackCatalog, type FontFallbackProfile, type FontPlanProfileMiss, type FontPlanScheme, type FontPlanSlot, type FontPlanStyle, type FontPlanTheme, type ResolvedFontSource, } from "./shared/font-plan.js";
82
82
  export { getGoogleFonts } from "./font-picker/esp-font-picker.js";
83
- export { ROOT_SURFACE, THEME_FIT_ANCHOR_FIELDS, THEME_FIT_APCA_FIELDS, THEME_FIT_DATA_PALETTE_LINT_FIELDS, THEME_FIT_DATA_PALETTE_LINT_IDS, THEME_FIT_EXPLICIT_VALUES, THEME_FIT_LINT_FIELDS, THEME_FIT_LINT_IDS, THEME_FIT_LINT_SEVERITIES, THEME_FIT_REPORT_FIELDS, THEME_FIT_SUITE_FIELDS, THEME_FIT_TOKEN_FIELDS, themeFitReport, themeFitRows, themeFitLints, themeFitReportSuite, printThemeFitReport, type ThemeFitReport, type ThemeFitReportOptions, type ThemeFitToken, type ThemeFitApcaAction, type ThemeFitAnchor, type ThemeFitRow, type ThemeFitLint, type ThemeFitDataPaletteLint, type ThemeFitFieldDescriptor, type ThemeFitFieldKind, type ThemeFitSurfaceReport, type ThemeFitSuite, } from "./shared/theme-fit-report.js";
83
+ export { ROOT_SURFACE, THEME_FIT_ANCHOR_FIELDS, THEME_FIT_APCA_FIELDS, THEME_FIT_DATA_PALETTE_LINT_FIELDS, THEME_FIT_DATA_PALETTE_LINT_IDS, THEME_FIT_EXPLICIT_VALUES, THEME_FIT_LINT_FIELDS, THEME_FIT_LINT_IDS, THEME_FIT_LINT_SEVERITIES, THEME_FIT_PAIRING_VALUES, THEME_FIT_REPORT_FIELDS, THEME_FIT_SUITE_FIELDS, THEME_FIT_TOKEN_FIELDS, themeFitReport, themeFitRows, themeFitLints, themeFitReportSuite, printThemeFitReport, type ThemeFitReport, type ThemeFitReportOptions, type ThemeFitToken, type ThemeFitApcaAction, type ThemeFitAnchor, type ThemeFitRow, type ThemeFitLint, type ThemeFitDataPaletteLint, type ThemeFitFieldDescriptor, type ThemeFitFieldKind, type ThemeFitSurfaceReport, type ThemeFitSuite, } from "./shared/theme-fit-report.js";
84
84
  export { ICON_SPRITE, ICON_SPRITE_ID, installIconSprite } from "./icons/index.js";
85
85
  export { deriveLightnessRamp, themeFromSwatches, type ThemeFromSwatchesOptions, type ThemeFromSwatchesResult, } from "./shared/theme-swatches.js";
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export*from"./box/esp-box.js";export*from"./action-menu/esp-action-menu.js";export*from"./action-menu/esp-action-menu-item.js";export*from"./avatar/esp-avatar.js";export*from"./avatar/esp-profile-chip.js";export*from"./badge/esp-badge.js";export*from"./checkbox/esp-checkbox.js";export*from"./checkbox/esp-checkbox-group.js";export*from"./data-cell/esp-data-cell.js";export*from"./radio-button/esp-radio-button.js";export*from"./radio-button/esp-radio-button-group.js";export*from"./repeater/esp-repeater.js";export*from"./breadcrumbs/esp-breadcrumbs.js";export*from"./button/esp-button.js";export*from"./button/esp-button-group.js";export*from"./color-picker/esp-color-picker.js";export*from"./flyout/esp-flyout.js";export*from"./form/esp-form.js";export*from"./form-item/esp-form-item.js";export*from"./help/esp-help-button.js";export*from"./help/esp-help-provider.js";export*from"./header/index.js";export*from"./page/esp-page.js";export*from"./section/esp-section.js";export*from"./stack/esp-stack.js";export*from"./row/esp-row.js";export*from"./popover/esp-popover.js";export*from"./dialog/esp-dialog.js";export*from"./empty-state/esp-empty-state.js";export*from"./image-upload/esp-image-upload.js";export*from"./image-picker/esp-image-picker.js";export*from"./file-upload/esp-file-upload.js";export*from"./font-picker/esp-font-picker.js";export*from"./focus-picker/esp-focus-picker.js";export*from"./footer/index.js";export*from"./grid/esp-grid.js";export*from"./image/esp-image.js";export*from"./image/image-focus.js";export*from"./lightbox/esp-lightbox.js";export*from"./info/esp-info.js";export*from"./input/esp-input.js";export*from"./textarea/esp-textarea.js";export*from"./pickers/esp-pick-one.js";export*from"./pickers/esp-pick-some.js";export*from"./pickers/esp-picker-menu.js";export*from"./pickers/esp-picker-item.js";export*from"./root/esp-root.js";export*from"./menu/index.js";export*from"./burger/esp-burger.js";export*from"./date-picker/esp-date-picker.js";export*from"./details/esp-details.js";export*from"./details/esp-details-group.js";export*from"./tabs/esp-tab.js";export*from"./tabs/esp-tab-group.js";export*from"./slider/esp-slider.js";export*from"./switch/esp-switch.js";export*from"./toaster/esp-toaster.js";export*from"./tooltip/esp-tooltip.js";export*from"./progress/esp-progress.js";export*from"./search/esp-search.js";export*from"./status-indicator/esp-status-indicator.js";export*from"./tree/esp-tree.js";export*from"./tree/esp-tree-item.js";import{DEFAULT_ICON_SPRITE_URL as ao,DEFAULT_ICON_VIEW_BOX as Io,INTENT_VARIANTS as io,normalizeIntentVariant as lo,getIconHrefForHost as Fo,getIconSpriteUrl as no,getIconSpriteReference as Ao}from"./shared/intent-values.js";import{EspalierElementBase as so}from"./shared/esp-element-base.js";import{VALIDITY_CHANGED_EVENT as Ro}from"./shared/validation.js";import{FormFieldController as Do}from"./shared/form-field-controller.js";import{traverseToClosest as go}from"./shared/utilities.js";import{showToast as Mo}from"./shared/toast-events.js";import{showFlyout as Ho,closeFlyout as Oo}from"./shared/flyout-events.js";import{requestHelp as Co}from"./shared/help-events.js";import{getEspBus as bo}from"./shared/bus-events.js";export*from"./shared/events.js";import{getImageDetails as yo,releasePreviewUrl as Vo}from"./image-upload/image-helpers.js";import{calculateAlbumLayout as vo,calculatePhotoLayout as wo,DEFAULT_ALBUM_MAX_ROW_HEIGHT_VH as Bo,normalizeAlbumMaxRowHeightVh as zo,normalizeMaxImagesPerRow as Ko}from"./shared/justified-layout.js";import{encodeTheme as qo,parseTheme as Yo,mergePartials as jo,layerThemes as Jo,validateThemePair as Qo,resolveContextTheme as Zo,isObjectRecord as $o,isSemanticColorName as oe,buildTaprootLightTheme as ee,buildTaprootDarkTheme as re,NESTED_THEME_KEYS as te}from"./shared/theme.js";import{auditDataPalette as pe,describePaletteCollision as fe,generateSequentialRamp as xe,generateDivergingRamp as Te,COLOR_VISION_SIMULATIONS as _e,DATA_SERIES_KEYS as Ee,DEFAULT_DATA_PALETTE as ae,DEFAULT_DATA_RAMP_STEPS as Ie,DEFAULT_DIVERGING_NEUTRAL as ie,MAX_DATA_RAMP_STEPS as le,MIN_DATA_COLOR_DISTANCE as Fe,MIN_DATA_RAMP_LIGHTNESS_STEP as ne,MIN_DATA_RAMP_STEPS as Ae}from"./shared/data-colors.js";import{canonicalFontStack as se,classifyFontStack as Le,customWebSafeFont as Re,isDeviceFontStack as ce,matchWebSafeFont as De,parseFontStack as Ne,webSafeFontWeights as ge}from"./shared/font-stacks.js";import{WEIGHT_LABELS as Me,extractWeights as Pe,normalizeWeight as He,bestAvailableWeight as Oe,extractFamily as ue,getFallbackFont as Ce,googleFontStylesheetUrl as Ue,isLocalFontFamily as be,normalizeFontFaceWeight as Ge,removeRuntimeGoogleFontLink as ke,removeRuntimeGoogleFontLinks as ye,syncRuntimeGoogleFontLink as Ve,RUNTIME_GOOGLE_FONT_OWNER_ATTR as We}from"./shared/font-helpers.js";import{compileFontPlan as ve,fontFallbackAliases as we,fontFallbackBaseIndexes as Be,fontFaceRequestKey as ze}from"./shared/font-plan.js";import{getGoogleFonts as Xe}from"./font-picker/esp-font-picker.js";import{ROOT_SURFACE as Ye,THEME_FIT_ANCHOR_FIELDS as je,THEME_FIT_APCA_FIELDS as Je,THEME_FIT_DATA_PALETTE_LINT_FIELDS as Qe,THEME_FIT_DATA_PALETTE_LINT_IDS as Ze,THEME_FIT_EXPLICIT_VALUES as $e,THEME_FIT_LINT_FIELDS as or,THEME_FIT_LINT_IDS as er,THEME_FIT_LINT_SEVERITIES as rr,THEME_FIT_REPORT_FIELDS as tr,THEME_FIT_SUITE_FIELDS as mr,THEME_FIT_TOKEN_FIELDS as pr,themeFitReport as fr,themeFitRows as xr,themeFitLints as Tr,themeFitReportSuite as _r,printThemeFitReport as Er}from"./shared/theme-fit-report.js";import{ICON_SPRITE as Ir,ICON_SPRITE_ID as ir,installIconSprite as lr}from"./icons/index.js";import{deriveLightnessRamp as nr,themeFromSwatches as Ar}from"./shared/theme-swatches.js";export{_e as COLOR_VISION_SIMULATIONS,Ee as DATA_SERIES_KEYS,Bo as DEFAULT_ALBUM_MAX_ROW_HEIGHT_VH,ae as DEFAULT_DATA_PALETTE,Ie as DEFAULT_DATA_RAMP_STEPS,ie as DEFAULT_DIVERGING_NEUTRAL,ao as DEFAULT_ICON_SPRITE_URL,Io as DEFAULT_ICON_VIEW_BOX,so as EspalierElementBase,Do as FormFieldController,Ir as ICON_SPRITE,ir as ICON_SPRITE_ID,io as INTENT_VARIANTS,le as MAX_DATA_RAMP_STEPS,Fe as MIN_DATA_COLOR_DISTANCE,ne as MIN_DATA_RAMP_LIGHTNESS_STEP,Ae as MIN_DATA_RAMP_STEPS,te as NESTED_THEME_KEYS,Ye as ROOT_SURFACE,We as RUNTIME_GOOGLE_FONT_OWNER_ATTR,je as THEME_FIT_ANCHOR_FIELDS,Je as THEME_FIT_APCA_FIELDS,Qe as THEME_FIT_DATA_PALETTE_LINT_FIELDS,Ze as THEME_FIT_DATA_PALETTE_LINT_IDS,$e as THEME_FIT_EXPLICIT_VALUES,or as THEME_FIT_LINT_FIELDS,er as THEME_FIT_LINT_IDS,rr as THEME_FIT_LINT_SEVERITIES,tr as THEME_FIT_REPORT_FIELDS,mr as THEME_FIT_SUITE_FIELDS,pr as THEME_FIT_TOKEN_FIELDS,Ro as VALIDITY_CHANGED_EVENT,Me as WEIGHT_LABELS,pe as auditDataPalette,Oe as bestAvailableWeight,re as buildTaprootDarkTheme,ee as buildTaprootLightTheme,vo as calculateAlbumLayout,wo as calculatePhotoLayout,se as canonicalFontStack,Le as classifyFontStack,Oo as closeFlyout,ve as compileFontPlan,Re as customWebSafeFont,nr as deriveLightnessRamp,fe as describePaletteCollision,qo as encodeTheme,ue as extractFamily,Pe as extractWeights,ze as fontFaceRequestKey,we as fontFallbackAliases,Be as fontFallbackBaseIndexes,Te as generateDivergingRamp,xe as generateSequentialRamp,bo as getEspBus,Ce as getFallbackFont,Xe as getGoogleFonts,Fo as getIconHrefForHost,Ao as getIconSpriteReference,no as getIconSpriteUrl,yo as getImageDetails,Ue as googleFontStylesheetUrl,lr as installIconSprite,ce as isDeviceFontStack,be as isLocalFontFamily,$o as isObjectRecord,oe as isSemanticColorName,Jo as layerThemes,De as matchWebSafeFont,jo as mergePartials,zo as normalizeAlbumMaxRowHeightVh,Ge as normalizeFontFaceWeight,lo as normalizeIntentVariant,Ko as normalizeMaxImagesPerRow,He as normalizeWeight,Ne as parseFontStack,Yo as parseTheme,Er as printThemeFitReport,Vo as releasePreviewUrl,ke as removeRuntimeGoogleFontLink,ye as removeRuntimeGoogleFontLinks,Co as requestHelp,Zo as resolveContextTheme,Ho as showFlyout,Mo as showToast,Ve as syncRuntimeGoogleFontLink,Tr as themeFitLints,fr as themeFitReport,_r as themeFitReportSuite,xr as themeFitRows,Ar as themeFromSwatches,go as traverseToClosest,Qo as validateThemePair,ge as webSafeFontWeights};
1
+ export*from"./box/esp-box.js";export*from"./action-menu/esp-action-menu.js";export*from"./action-menu/esp-action-menu-item.js";export*from"./avatar/esp-avatar.js";export*from"./avatar/esp-profile-chip.js";export*from"./badge/esp-badge.js";export*from"./checkbox/esp-checkbox.js";export*from"./checkbox/esp-checkbox-group.js";export*from"./data-cell/esp-data-cell.js";export*from"./radio-button/esp-radio-button.js";export*from"./radio-button/esp-radio-button-group.js";export*from"./repeater/esp-repeater.js";export*from"./breadcrumbs/esp-breadcrumbs.js";export*from"./button/esp-button.js";export*from"./button/esp-button-group.js";export*from"./color-picker/esp-color-picker.js";export*from"./flyout/esp-flyout.js";export*from"./form/esp-form.js";export*from"./form-item/esp-form-item.js";export*from"./help/esp-help-button.js";export*from"./help/esp-help-provider.js";export*from"./header/index.js";export*from"./page/esp-page.js";export*from"./section/esp-section.js";export*from"./stack/esp-stack.js";export*from"./row/esp-row.js";export*from"./popover/esp-popover.js";export*from"./dialog/esp-dialog.js";export*from"./empty-state/esp-empty-state.js";export*from"./image-upload/esp-image-upload.js";export*from"./image-picker/esp-image-picker.js";export*from"./file-upload/esp-file-upload.js";export*from"./font-picker/esp-font-picker.js";export*from"./focus-picker/esp-focus-picker.js";export*from"./footer/index.js";export*from"./grid/esp-grid.js";export*from"./image/esp-image.js";export*from"./image/image-focus.js";export*from"./lightbox/esp-lightbox.js";export*from"./info/esp-info.js";export*from"./input/esp-input.js";export*from"./textarea/esp-textarea.js";export*from"./pickers/esp-pick-one.js";export*from"./pickers/esp-pick-some.js";export*from"./pickers/esp-picker-menu.js";export*from"./pickers/esp-picker-item.js";export*from"./root/esp-root.js";export*from"./menu/index.js";export*from"./burger/esp-burger.js";export*from"./date-picker/esp-date-picker.js";export*from"./details/esp-details.js";export*from"./details/esp-details-group.js";export*from"./tabs/esp-tab.js";export*from"./tabs/esp-tab-group.js";export*from"./slider/esp-slider.js";export*from"./switch/esp-switch.js";export*from"./toaster/esp-toaster.js";export*from"./tooltip/esp-tooltip.js";export*from"./progress/esp-progress.js";export*from"./search/esp-search.js";export*from"./status-indicator/esp-status-indicator.js";export*from"./tree/esp-tree.js";export*from"./tree/esp-tree-item.js";import{DEFAULT_ICON_SPRITE_URL as ao,DEFAULT_ICON_VIEW_BOX as Io,INTENT_VARIANTS as io,normalizeIntentVariant as lo,getIconHrefForHost as Fo,getIconSpriteUrl as no,getIconSpriteReference as Ao}from"./shared/intent-values.js";import{EspalierElementBase as so}from"./shared/esp-element-base.js";import{VALIDITY_CHANGED_EVENT as Ro}from"./shared/validation.js";import{FormFieldController as Do}from"./shared/form-field-controller.js";import{traverseToClosest as go}from"./shared/utilities.js";import{showToast as Mo}from"./shared/toast-events.js";import{showFlyout as Ho,closeFlyout as Oo}from"./shared/flyout-events.js";import{requestHelp as Uo}from"./shared/help-events.js";import{getEspBus as bo}from"./shared/bus-events.js";export*from"./shared/events.js";import{getImageDetails as ko,releasePreviewUrl as yo}from"./image-upload/image-helpers.js";import{calculateAlbumLayout as vo,calculatePhotoLayout as wo,DEFAULT_ALBUM_MAX_ROW_HEIGHT_VH as Bo,normalizeAlbumMaxRowHeightVh as zo,normalizeMaxImagesPerRow as Ko}from"./shared/justified-layout.js";import{encodeTheme as qo,parseTheme as Yo,mergePartials as jo,layerThemes as Jo,validateThemePair as Qo,resolveContextTheme as Zo,isObjectRecord as $o,isSemanticColorName as oe,buildTaprootLightTheme as ee,buildTaprootDarkTheme as re,NESTED_THEME_KEYS as te}from"./shared/theme.js";import{auditDataPalette as pe,describePaletteCollision as fe,generateSequentialRamp as Te,generateDivergingRamp as xe,COLOR_VISION_SIMULATIONS as _e,DATA_SERIES_KEYS as Ee,DEFAULT_DATA_PALETTE as ae,DEFAULT_DATA_RAMP_STEPS as Ie,DEFAULT_DIVERGING_NEUTRAL as ie,MAX_DATA_RAMP_STEPS as le,MIN_DATA_COLOR_DISTANCE as Fe,MIN_DATA_RAMP_LIGHTNESS_STEP as ne,MIN_DATA_RAMP_STEPS as Ae}from"./shared/data-colors.js";import{canonicalFontStack as se,classifyFontStack as Le,customWebSafeFont as Re,isDeviceFontStack as ce,matchWebSafeFont as De,parseFontStack as Ne,webSafeFontWeights as ge}from"./shared/font-stacks.js";import{WEIGHT_LABELS as Me,extractWeights as Pe,normalizeWeight as He,bestAvailableWeight as Oe,extractFamily as ue,getFallbackFont as Ue,googleFontStylesheetUrl as Ce,isLocalFontFamily as be,normalizeFontFaceWeight as Ge,removeRuntimeGoogleFontLink as Ve,removeRuntimeGoogleFontLinks as ke,syncRuntimeGoogleFontLink as ye,RUNTIME_GOOGLE_FONT_OWNER_ATTR as We}from"./shared/font-helpers.js";import{compileFontPlan as ve,fontFallbackAliases as we,fontFallbackBaseIndexes as Be,fontFaceRequestKey as ze}from"./shared/font-plan.js";import{getGoogleFonts as Xe}from"./font-picker/esp-font-picker.js";import{ROOT_SURFACE as Ye,THEME_FIT_ANCHOR_FIELDS as je,THEME_FIT_APCA_FIELDS as Je,THEME_FIT_DATA_PALETTE_LINT_FIELDS as Qe,THEME_FIT_DATA_PALETTE_LINT_IDS as Ze,THEME_FIT_EXPLICIT_VALUES as $e,THEME_FIT_LINT_FIELDS as or,THEME_FIT_LINT_IDS as er,THEME_FIT_LINT_SEVERITIES as rr,THEME_FIT_PAIRING_VALUES as tr,THEME_FIT_REPORT_FIELDS as mr,THEME_FIT_SUITE_FIELDS as pr,THEME_FIT_TOKEN_FIELDS as fr,themeFitReport as Tr,themeFitRows as xr,themeFitLints as _r,themeFitReportSuite as Er,printThemeFitReport as ar}from"./shared/theme-fit-report.js";import{ICON_SPRITE as ir,ICON_SPRITE_ID as lr,installIconSprite as Fr}from"./icons/index.js";import{deriveLightnessRamp as Ar,themeFromSwatches as Sr}from"./shared/theme-swatches.js";export{_e as COLOR_VISION_SIMULATIONS,Ee as DATA_SERIES_KEYS,Bo as DEFAULT_ALBUM_MAX_ROW_HEIGHT_VH,ae as DEFAULT_DATA_PALETTE,Ie as DEFAULT_DATA_RAMP_STEPS,ie as DEFAULT_DIVERGING_NEUTRAL,ao as DEFAULT_ICON_SPRITE_URL,Io as DEFAULT_ICON_VIEW_BOX,so as EspalierElementBase,Do as FormFieldController,ir as ICON_SPRITE,lr as ICON_SPRITE_ID,io as INTENT_VARIANTS,le as MAX_DATA_RAMP_STEPS,Fe as MIN_DATA_COLOR_DISTANCE,ne as MIN_DATA_RAMP_LIGHTNESS_STEP,Ae as MIN_DATA_RAMP_STEPS,te as NESTED_THEME_KEYS,Ye as ROOT_SURFACE,We as RUNTIME_GOOGLE_FONT_OWNER_ATTR,je as THEME_FIT_ANCHOR_FIELDS,Je as THEME_FIT_APCA_FIELDS,Qe as THEME_FIT_DATA_PALETTE_LINT_FIELDS,Ze as THEME_FIT_DATA_PALETTE_LINT_IDS,$e as THEME_FIT_EXPLICIT_VALUES,or as THEME_FIT_LINT_FIELDS,er as THEME_FIT_LINT_IDS,rr as THEME_FIT_LINT_SEVERITIES,tr as THEME_FIT_PAIRING_VALUES,mr as THEME_FIT_REPORT_FIELDS,pr as THEME_FIT_SUITE_FIELDS,fr as THEME_FIT_TOKEN_FIELDS,Ro as VALIDITY_CHANGED_EVENT,Me as WEIGHT_LABELS,pe as auditDataPalette,Oe as bestAvailableWeight,re as buildTaprootDarkTheme,ee as buildTaprootLightTheme,vo as calculateAlbumLayout,wo as calculatePhotoLayout,se as canonicalFontStack,Le as classifyFontStack,Oo as closeFlyout,ve as compileFontPlan,Re as customWebSafeFont,Ar as deriveLightnessRamp,fe as describePaletteCollision,qo as encodeTheme,ue as extractFamily,Pe as extractWeights,ze as fontFaceRequestKey,we as fontFallbackAliases,Be as fontFallbackBaseIndexes,xe as generateDivergingRamp,Te as generateSequentialRamp,bo as getEspBus,Ue as getFallbackFont,Xe as getGoogleFonts,Fo as getIconHrefForHost,Ao as getIconSpriteReference,no as getIconSpriteUrl,ko as getImageDetails,Ce as googleFontStylesheetUrl,Fr as installIconSprite,ce as isDeviceFontStack,be as isLocalFontFamily,$o as isObjectRecord,oe as isSemanticColorName,Jo as layerThemes,De as matchWebSafeFont,jo as mergePartials,zo as normalizeAlbumMaxRowHeightVh,Ge as normalizeFontFaceWeight,lo as normalizeIntentVariant,Ko as normalizeMaxImagesPerRow,He as normalizeWeight,Ne as parseFontStack,Yo as parseTheme,ar as printThemeFitReport,yo as releasePreviewUrl,Ve as removeRuntimeGoogleFontLink,ke as removeRuntimeGoogleFontLinks,Uo as requestHelp,Zo as resolveContextTheme,Ho as showFlyout,Mo as showToast,ye as syncRuntimeGoogleFontLink,_r as themeFitLints,Tr as themeFitReport,Er as themeFitReportSuite,xr as themeFitRows,Sr as themeFromSwatches,go as traverseToClosest,Qo as validateThemePair,ge as webSafeFontWeights};
@@ -114,6 +114,19 @@ export interface ThemeFitToken {
114
114
  * since a zone's unexpected value then traces to a root theme line.
115
115
  */
116
116
  explicit?: "declared" | "inherited";
117
+ /**
118
+ * Present on a role-paired ink the role layer compiled (ESP0223).
119
+ * `"declared"` means the theme named the slot — a separate claim about
120
+ * this token's family. `"derived"` means it paired off another role,
121
+ * which for `action.ink` is the canvas: the token IS the page ground
122
+ * by contract, and its distance from the anchor it reports describes
123
+ * that contract rather than a theme that missed its brand.
124
+ *
125
+ * Absent when no such role is declared, and absent on a token an
126
+ * explicit mapping pinned — there the pin, not the role, chose the
127
+ * value, and {@link explicit} is the mark that says so.
128
+ */
129
+ pairing?: "declared" | "derived";
117
130
  }
118
131
  /**
119
132
  * A theme's fit report — the stable JSON shape the docs site renders.
@@ -184,7 +197,7 @@ export declare function printThemeFitReport(report: ThemeFitReport): void;
184
197
  /** One cross-token finding over a single surface's emitted values. */
185
198
  export interface ThemeFitLint {
186
199
  /** Stable lint identifier. */
187
- id: "action-canvas-separation" | "link-hover-ordering";
200
+ id: "action-anchor-inversion" | "action-canvas-separation" | "apca-target-unmet" | "link-hover-ordering";
188
201
  /** Lints describe design-quality hazards, not validation failures. */
189
202
  severity: "warning";
190
203
  /** The tokens the finding is about, recommended retune target first. */
@@ -212,7 +225,7 @@ export interface ThemeFitDataPaletteLint {
212
225
  message: string;
213
226
  }
214
227
  /** Runtime validation/comparison vocabulary used by the exported field descriptors. */
215
- export type ThemeFitFieldKind = "anchor" | "anchor-record" | "apca" | "boolean" | "data-palette-record" | "explicit" | "fit-lint-id" | "lint-array" | "message" | "number" | "palette-lint-id" | "palette-lint-record" | "report-array" | "scheme" | "semantic" | "semantic-array" | "series-pair" | "simulation" | "string" | "string-array" | "token-array" | "warning";
228
+ export type ThemeFitFieldKind = "anchor" | "anchor-record" | "apca" | "boolean" | "data-palette-record" | "explicit" | "fit-lint-id" | "lint-array" | "message" | "number" | "pairing" | "palette-lint-id" | "palette-lint-record" | "report-array" | "scheme" | "semantic" | "semantic-array" | "series-pair" | "simulation" | "string" | "string-array" | "token-array" | "warning";
216
229
  /** One runtime field descriptor used by fit-suite validation and comparison. */
217
230
  export interface ThemeFitFieldDescriptor<Field extends PropertyKey = PropertyKey> {
218
231
  readonly field: Field;
@@ -226,6 +239,8 @@ export declare const THEME_FIT_LINT_IDS: readonly ThemeFitLint["id"][];
226
239
  export declare const THEME_FIT_DATA_PALETTE_LINT_IDS: readonly ThemeFitDataPaletteLint["id"][];
227
240
  /** Every stable explicit-pin provenance value. */
228
241
  export declare const THEME_FIT_EXPLICIT_VALUES: readonly NonNullable<ThemeFitToken["explicit"]>[];
242
+ /** Every stable role-paired-ink provenance value. */
243
+ export declare const THEME_FIT_PAIRING_VALUES: readonly NonNullable<ThemeFitToken["pairing"]>[];
229
244
  type ThemeFitLintSeverity = ThemeFitLint["severity"] | ThemeFitDataPaletteLint["severity"];
230
245
  /** Every stable fit-suite lint severity. */
231
246
  export declare const THEME_FIT_LINT_SEVERITIES: readonly ThemeFitLintSeverity[];
@@ -256,6 +271,27 @@ export declare const THEME_FIT_DATA_PALETTE_LINT_FIELDS: readonly ThemeFitFieldD
256
271
  * wash itself signals the state and the ink must clear the text tier
257
272
  * there; when the wash equals the background (no visible wash), hover
258
273
  * must not read weaker than rest — a dimming hover feels disabled.
274
+ * - **action-anchor-inversion** (ESP0223) — the filled action rendered
275
+ * the *opposite way round* from the swatches it names: a surface whose
276
+ * swatch wants dark ink took light ink instead, or a declared pair's
277
+ * rendered lightness order reversed. Anchor drift on its own is by
278
+ * design — a stop supplies lightness (ADR-015) — and a raw distance
279
+ * cannot tell "a deeper rose" from "the gold became brown": a coral
280
+ * action drifts ΔE 0.29 and reads correctly, while a gold one drifts
281
+ * 0.34 and reads as a different design. The sign does tell them
282
+ * apart, and needs no threshold. This is also the one anchor drift an
283
+ * author cannot chase down: the action's stop is chosen by the engine,
284
+ * so seating the ramp — the remedy the lightness-ramps guide gives for
285
+ * every other token — cannot move it, and the fix is a pin.
286
+ * - **apca-target-unmet** (ESP0223) — enforcement moved a text token as
287
+ * far as its ramp allows and the pair still misses the legibility
288
+ * floor. The per-token `apca` block has always carried the numbers;
289
+ * nothing read them, so a theme could pin a surface no ink can carry
290
+ * and check clean.
291
+ *
292
+ * The two ESP0223 lints read `anchor`, `pairing` and `apca` rather than
293
+ * only `resolved`, because both findings are about how a token got where
294
+ * it is, which a colour alone cannot say.
259
295
  */
260
296
  export declare function themeFitLints(report: ThemeFitReport): ThemeFitLint[];
261
297
  /**
@@ -1 +1 @@
1
- import{apcaContrast as g,deltaEOK as _,parseOklch as v,serializeOklch as I}from"./color-engine.js";import{DATA_SERIES_KEYS as A,auditDataPalette as j,describePaletteCollision as C}from"./data-colors.js";import{DEFAULT_DARK_THEME as D,DEFAULT_LIGHT_THEME as $,explicitMappingTokens as w,mergeTheme as F,parseAnchorSource as P,resolveContextTheme as N}from"./theme.js";import{computeThemeProperties as y}from"../root/helpers/compute-theme-properties.js";function te(e,t,r={}){return L(e,t,r).report}function L(e,t,r={}){let o=e,s=null;if(r.context!==void 0){const n=N(e,r.context);if(!n)throw new Error(`themeFitReport: context "${r.context}" is not defined by the theme.`);o=n,s=new Set(Object.keys(e.contexts[r.context]?.semanticMappings??{}))}const c=[],m=y(o,t,c),k=w(o),l=c.map(n=>{const a=Y(n);return k.has(a.token)&&(a.explicit=s===null||s.has(a.token)?"declared":"inherited"),a});return{properties:m,report:{scheme:t,seedColor:o.seedColor,anchors:G(o.anchors),tokens:l,adjustedTokens:l.filter(n=>n.adjusted).map(n=>n.token)}}}function B(e){return e.tokens.map(t=>({token:t.token,source:t.source,requested:t.requested,resolved:t.resolved,apca:X(t),anchor:W(t)}))}function re(e){const t=e.adjustedTokens.length>0?`APCA moved ${e.adjustedTokens.join(", ")}`:"APCA moved nothing";console.info(`Espalier theme fit \u2014 ${e.scheme} scheme, seed ${e.seedColor}. ${t}.`),console.table(B(e))}function E(e){const t=Object.entries(e).map(([r,o])=>Object.freeze({field:r,kind:o.kind,compared:o.compared}));return Object.freeze(t)}const V={"action-canvas-separation":!0,"link-hover-ordering":!0},oe=Object.freeze(Object.keys(V)),q={"data-palette-cvd-collision":!0},ne=Object.freeze(Object.keys(q)),z={declared:!0,inherited:!0},se=Object.freeze(Object.keys(z)),U={warning:!0},ae=Object.freeze(Object.keys(U)),ce=E({token:{kind:"semantic",compared:!0},property:{kind:"string",compared:!0},source:{kind:"string",compared:!0},lightness:{kind:"string",compared:!0},requested:{kind:"string",compared:!0},resolved:{kind:"string",compared:!0},adjusted:{kind:"boolean",compared:!0},sourceResolved:{kind:"boolean",compared:!0},anchor:{kind:"anchor",compared:!0},apca:{kind:"apca",compared:!0},explicit:{kind:"explicit",compared:!0}}),ie=E({reference:{kind:"string",compared:!0},color:{kind:"string",compared:!0},deltaE:{kind:"number",compared:!0}}),de=E({from:{kind:"string",compared:!0},to:{kind:"string",compared:!0},targetLc:{kind:"number",compared:!0},against:{kind:"semantic",compared:!0},againstColor:{kind:"string",compared:!0},fromLc:{kind:"number",compared:!0},toLc:{kind:"number",compared:!0}}),ue=E({id:{kind:"fit-lint-id",compared:!0},severity:{kind:"warning",compared:!0},tokens:{kind:"semantic-array",compared:!0},measured:{kind:"number",compared:!0},message:{kind:"message",compared:!0}}),le=E({id:{kind:"palette-lint-id",compared:!0},severity:{kind:"warning",compared:!0},series:{kind:"series-pair",compared:!0},simulation:{kind:"simulation",compared:!0},measured:{kind:"number",compared:!0},threshold:{kind:"number",compared:!0},message:{kind:"message",compared:!0}}),x=.045,O=.5,b=75,K=.045;function S(e){const t=[],r=new Map(e.tokens.map(n=>[n.token,n.resolved])),o=n=>{const a=r.get(n);return a===void 0?null:v(a)},s=o("background"),c=o("actionBackground");if(s&&c){const n=_(c,s);if(n<x){const a=u(n,4);t.push({id:"action-canvas-separation",severity:"warning",tokens:["actionBackground","background"],measured:a,message:`actionBackground sits \u0394E ${a} from background (floor ${x}) \u2014 filled actions vanish into this canvas. Retune the surface apart: move the background stop, or pin actionBackground to a separated stop.`})}}const m=o("link"),k=o("linkHover"),l=o("linkHoverBg");if(s&&m&&k&&l){const n=Math.abs(g(m,s)),a=Math.abs(g(k,l));_(l,s)>=K?a+O<b&&t.push({id:"link-hover-ordering",severity:"warning",tokens:["linkHoverBg","linkHover"],measured:u(b-a,1),message:`linkHover reaches only Lc ${u(a,1)} on the linkHoverBg wash it actually paints (tier ${b}) \u2014 retune linkHoverBg before changing linkHover; the wash has one consumer while the ink has many.`}):a+O<n&&t.push({id:"link-hover-ordering",severity:"warning",tokens:["linkHover","link"],measured:u(n-a,1),message:`linkHover reads weaker than link on the same canvas (Lc ${u(a,1)} vs ${u(n,1)}) \u2014 a dimming hover feels disabled. Retune linkHover toward more contrast than the resting link.`})}return t}const R=":root",pe=E({scheme:{kind:"scheme",compared:!0},seedColor:{kind:"string",compared:!0},anchors:{kind:"anchor-record",compared:!0},tokens:{kind:"token-array",compared:!0},adjustedTokens:{kind:"semantic-array",compared:!0},surface:{kind:"string",compared:!0},lints:{kind:"lint-array",compared:!0}}),me=E({surfaces:{kind:"string-array",compared:!0},dataPalette:{kind:"data-palette-record",compared:!0},paletteLints:{kind:"palette-lint-record",compared:!0},light:{kind:"report-array",compared:!0},dark:{kind:"report-array",compared:!0}});function ke(e,t){const r=F($,e),o=F(D,t),s=[R,...[...new Set([...Object.keys(r.contexts),...Object.keys(o.contexts)])].sort()],c=p=>{const i={};for(let d=0;d<A.length;d+=1){const f=A[d],h=`--esp-color-series-${d+1}`,T=p[h];if(T===void 0)throw new Error(`Theme fit report did not emit required property ${h}.`);i[f]=T}return i},m=p=>j(p).map(i=>{const d=u(i.distance,4);return{id:"data-palette-cvd-collision",severity:"warning",series:i.series,simulation:i.simulation,measured:d,threshold:i.threshold,message:C(i)}}),k=(p,i)=>{const d=L(p,i);if(Object.keys(d.properties).length===0)throw new Error(`themeFitReportSuite: ${i} root emitted no properties for seed "${p.seedColor}".`);const f=[{...d.report,surface:R,lints:S(d.report)}];for(const h of s){if(h===R||!(h in p.contexts))continue;const H=L(p,i,{context:h}).report;f.push({...H,surface:h,lints:S(H)})}return{dataPalette:c(d.properties),reports:f}},l=k(r,"light"),n=k(o,"dark"),a={light:l.dataPalette,dark:n.dataPalette};return{surfaces:s,dataPalette:a,paletteLints:{light:m(a.light),dark:m(a.dark)},light:l.reports,dark:n.reports}}function u(e,t){const r=Math.pow(10,t);return Math.round(e*r)/r}function M(e,t){return t===void 0?e:`${e}.${t}`}function G(e){const t={};for(const[r,o]of Object.entries(e)){if(typeof o=="string"){t[r]=o;continue}for(const[s,c]of Object.entries(o))t[s==="color"?r:M(r,s)]=c}return t}function Y(e){const t=v(e.resolved),r=e.requested===void 0?e.resolved:I(e.requested),o={token:e.token,property:e.property,source:e.source,lightness:e.lightness,requested:r,resolved:e.resolved,adjusted:r!==e.resolved,sourceResolved:e.sourceResolved},s=P(e.source);if(s&&e.sourceResolved&&(o.anchor={reference:M(s.name,s.slot),color:I(e.base),deltaE:u(_(e.base,t),4)}),e.contrast){const c=e.contrast.background;o.apca={from:r,to:e.resolved,targetLc:e.contrast.targetLc,against:e.contrast.against,againstColor:I(c),fromLc:u(Math.abs(g(v(r),c)),1),toLc:u(Math.abs(g(t,c)),1)}}return o}function X(e){if(!e.apca)return"";const{targetLc:t,against:r,fromLc:o,toLc:s}=e.apca;return e.adjusted?`Lc ${t} vs ${r} \u2014 pushed from Lc ${o} to Lc ${s}`:`Lc ${t} vs ${r} \u2014 met at Lc ${o}`}function W(e){return e.anchor?`${e.anchor.reference} \u0394E ${e.anchor.deltaE}`:e.sourceResolved?"":"unresolved \u2014 seed used"}export{R as ROOT_SURFACE,ie as THEME_FIT_ANCHOR_FIELDS,de as THEME_FIT_APCA_FIELDS,le as THEME_FIT_DATA_PALETTE_LINT_FIELDS,ne as THEME_FIT_DATA_PALETTE_LINT_IDS,se as THEME_FIT_EXPLICIT_VALUES,ue as THEME_FIT_LINT_FIELDS,oe as THEME_FIT_LINT_IDS,ae as THEME_FIT_LINT_SEVERITIES,pe as THEME_FIT_REPORT_FIELDS,me as THEME_FIT_SUITE_FIELDS,ce as THEME_FIT_TOKEN_FIELDS,re as printThemeFitReport,S as themeFitLints,te as themeFitReport,ke as themeFitReportSuite,B as themeFitRows};
1
+ import{apcaContrast as T,deltaEOK as x,parseOklch as _,serializeOklch as $}from"./color-engine.js";import{DATA_SERIES_KEYS as C,auditDataPalette as q,describePaletteCollision as K}from"./data-colors.js";import{DEFAULT_DARK_THEME as G,DEFAULT_LIGHT_THEME as Y,explicitMappingTokens as W,mergeTheme as j,parseAnchorSource as X,resolveContextTheme as J,roleInkProvenance as Q}from"./theme.js";import{computeThemeProperties as Z}from"../root/helpers/compute-theme-properties.js";function ke(e,t,o={}){return H(e,t,o).report}function H(e,t,o={}){let n=e,c=null;if(o.context!==void 0){const s=J(e,o.context);if(!s)throw new Error(`themeFitReport: context "${o.context}" is not defined by the theme.`);n=s,c=new Set(Object.keys(e.contexts[o.context]?.semanticMappings??{}))}const p=[],v=Z(n,t,p),L=W(n),f=Q(n.roles),E=p.map(s=>{const a=ie(s);if(L.has(a.token))a.explicit=c===null||c.has(a.token)?"declared":"inherited";else{const i=f[a.token];i!==void 0&&(a.pairing=i)}return a});return{properties:v,report:{scheme:t,seedColor:n.seedColor,anchors:ce(n.anchors),tokens:E,adjustedTokens:E.filter(s=>s.adjusted).map(s=>s.token)}}}function ee(e){return e.tokens.map(t=>({token:t.token,source:t.source,requested:t.requested,resolved:t.resolved,apca:de(t),anchor:le(t)}))}function ge(e){const t=e.adjustedTokens.length>0?`APCA moved ${e.adjustedTokens.join(", ")}`:"APCA moved nothing";console.info(`Espalier theme fit \u2014 ${e.scheme} scheme, seed ${e.seedColor}. ${t}.`),console.table(ee(e))}function I(e){const t=Object.entries(e).map(([o,n])=>Object.freeze({field:o,kind:n.kind,compared:n.compared}));return Object.freeze(t)}const te={"action-anchor-inversion":!0,"action-canvas-separation":!0,"apca-target-unmet":!0,"link-hover-ordering":!0},fe=Object.freeze(Object.keys(te)),re={"data-palette-cvd-collision":!0},Ee=Object.freeze(Object.keys(re)),ne={declared:!0,inherited:!0},Te=Object.freeze(Object.keys(ne)),oe={declared:!0,derived:!0},_e=Object.freeze(Object.keys(oe)),ae={warning:!0},ve=Object.freeze(Object.keys(ae)),Le=I({token:{kind:"semantic",compared:!0},property:{kind:"string",compared:!0},source:{kind:"string",compared:!0},lightness:{kind:"string",compared:!0},requested:{kind:"string",compared:!0},resolved:{kind:"string",compared:!0},adjusted:{kind:"boolean",compared:!0},sourceResolved:{kind:"boolean",compared:!0},anchor:{kind:"anchor",compared:!0},apca:{kind:"apca",compared:!0},explicit:{kind:"explicit",compared:!0},pairing:{kind:"pairing",compared:!0}}),Ie=I({reference:{kind:"string",compared:!0},color:{kind:"string",compared:!0},deltaE:{kind:"number",compared:!0}}),be=I({from:{kind:"string",compared:!0},to:{kind:"string",compared:!0},targetLc:{kind:"number",compared:!0},against:{kind:"semantic",compared:!0},againstColor:{kind:"string",compared:!0},fromLc:{kind:"number",compared:!0},toLc:{kind:"number",compared:!0}}),Re=I({id:{kind:"fit-lint-id",compared:!0},severity:{kind:"warning",compared:!0},tokens:{kind:"semantic-array",compared:!0},measured:{kind:"number",compared:!0},message:{kind:"message",compared:!0}}),Me=I({id:{kind:"palette-lint-id",compared:!0},severity:{kind:"warning",compared:!0},series:{kind:"series-pair",compared:!0},simulation:{kind:"simulation",compared:!0},measured:{kind:"number",compared:!0},threshold:{kind:"number",compared:!0},message:{kind:"message",compared:!0}}),D=.045,N=.045,M=75,y={l:0,c:0,h:0},P={l:1,c:0,h:0},B=.5,F=75,se=.045;function V(e){const t=[],o=new Map(e.tokens.map(r=>[r.token,r.resolved])),n=r=>{const d=o.get(r);return d===void 0?null:_(d)},c=n("background"),p=n("actionBackground");if(c&&p){const r=x(p,c);if(r<D){const d=u(r,4);t.push({id:"action-canvas-separation",severity:"warning",tokens:["actionBackground","background"],measured:d,message:`actionBackground sits \u0394E ${d} from background (floor ${D}) \u2014 filled actions vanish into this canvas. Retune the surface apart: move the background stop, or pin actionBackground to a separated stop.`})}}const v=n("link"),L=n("linkHover"),f=n("linkHoverBg");if(c&&v&&L&&f){const r=Math.abs(T(v,c)),d=Math.abs(T(L,f));x(f,c)>=se?d+B<F&&t.push({id:"link-hover-ordering",severity:"warning",tokens:["linkHoverBg","linkHover"],measured:u(F-d,1),message:`linkHover reaches only Lc ${u(d,1)} on the linkHoverBg wash it actually paints (tier ${F}) \u2014 retune linkHoverBg before changing linkHover; the wash has one consumer while the ink has many.`}):d+B<r&&t.push({id:"link-hover-ordering",severity:"warning",tokens:["linkHover","link"],measured:u(r-d,1),message:`linkHover reads weaker than link on the same canvas (Lc ${u(d,1)} vs ${u(r,1)}) \u2014 a dimming hover feels disabled. Retune linkHover toward more contrast than the resting link.`})}const E=r=>Math.abs(T(y,r))>=Math.abs(T(P,r))?"dark":"light",s=e.tokens.find(r=>r.token==="actionBackground"),a=e.tokens.find(r=>r.token==="actionText"),i=[],l=[];let m=0;const h=a?.anchor?_(a.anchor.color):null,g=a?_(a.resolved):null,k=s?.anchor?_(s.anchor.color):null,b=s?_(s.resolved):null,O=g&&b?g.l>b.l?"light":"dark":null;s?.anchor&&k&&b&&O&&s.anchor.deltaE>=N&&E(k)!==O&&(i.push("actionBackground"),m=Math.max(m,s.anchor.deltaE),l.push(`"${s.anchor.reference}" wants ${E(k)} ink but actionBackground renders carrying a ${O} label (\u0394E ${u(s.anchor.deltaE,4)})`)),a?.anchor&&a.pairing!=="derived"&&h&&g&&k&&b&&a.anchor.deltaE>=N&&k.l!==h.l&&Math.sign(k.l-h.l)!==Math.sign(b.l-g.l)&&(i.push("actionText"),m=Math.max(m,a.anchor.deltaE),l.push(`the declared pair puts "${s.anchor.reference}" ${k.l>h.l?"above":"below"} "${a.anchor.reference}" in lightness and the rendered pair puts it ${b.l>g.l?"above":"below"} (\u0394E ${u(a.anchor.deltaE,4)})`)),i.length>0&&t.push({id:"action-anchor-inversion",severity:"warning",tokens:i,measured:u(m,4),message:`The filled action renders the opposite way round from the swatches it names: ${l.join("; ")}. `+(s?.explicit===void 0?"A filled action's ramp stop is chosen by the engine near its mid-band target, so seating the ramp cannot move it the way it moves every other anchored token. Retune by pinning semanticMappings.actionBackground to a stop that carries the swatch":"semanticMappings.actionBackground is already pinned, so the engine did not place this \u2014 retune the stop that pin names")+" \u2014 and check the pair still clears its tier there, because a swatch this far off the band often cannot hold a label at its own lightness."});const A=e.tokens.filter(r=>r.apca!==void 0&&r.apca.toLc<Math.min(r.apca.targetLc,M)).sort((r,d)=>Math.min(d.apca.targetLc,M)-d.apca.toLc-(Math.min(r.apca.targetLc,M)-r.apca.toLc)||(r.token<d.token?-1:r.token>d.token?1:0));if(A.length>0){const r=A[0].apca,d=A.map(R=>`${R.token} reaches Lc ${u(R.apca.toLc,1)} on ${R.apca.against} (floor ${Math.min(R.apca.targetLc,M)})`),w=_(r.againstColor),U=w!==null&&Math.max(Math.abs(T(y,w)),Math.abs(T(P,w)))>=Math.min(r.targetLc,M);t.push({id:"apca-target-unmet",severity:"warning",tokens:A.map(R=>R.token),measured:u(Math.min(r.targetLc,M)-r.toLc,1),message:`${d.join(", ")} \u2014 enforcement moved the ink as far as this ramp allows and the pair still falls short. `+(U?"The opposite ink pole does clear the floor on that ground, so the emitted ink is on the weaker side rather than the ground being impossible \u2014 check the pairing before retuning the ground.":"No ink reaches the floor on that ground, so retune the ground each pair reads against toward a lightness that can carry it.")})}return t}const S=":root",Ae=I({scheme:{kind:"scheme",compared:!0},seedColor:{kind:"string",compared:!0},anchors:{kind:"anchor-record",compared:!0},tokens:{kind:"token-array",compared:!0},adjustedTokens:{kind:"semantic-array",compared:!0},surface:{kind:"string",compared:!0},lints:{kind:"lint-array",compared:!0}}),we=I({surfaces:{kind:"string-array",compared:!0},dataPalette:{kind:"data-palette-record",compared:!0},paletteLints:{kind:"palette-lint-record",compared:!0},light:{kind:"report-array",compared:!0},dark:{kind:"report-array",compared:!0}});function Oe(e,t){const o=j(Y,e),n=j(G,t),c=[S,...[...new Set([...Object.keys(o.contexts),...Object.keys(n.contexts)])].sort()],p=a=>{const i={};for(let l=0;l<C.length;l+=1){const m=C[l],h=`--esp-color-series-${l+1}`,g=a[h];if(g===void 0)throw new Error(`Theme fit report did not emit required property ${h}.`);i[m]=g}return i},v=a=>q(a).map(i=>{const l=u(i.distance,4);return{id:"data-palette-cvd-collision",severity:"warning",series:i.series,simulation:i.simulation,measured:l,threshold:i.threshold,message:K(i)}}),L=(a,i)=>{const l=H(a,i);if(Object.keys(l.properties).length===0)throw new Error(`themeFitReportSuite: ${i} root emitted no properties for seed "${a.seedColor}".`);const m=[{...l.report,surface:S,lints:V(l.report)}];for(const h of c){if(h===S||!(h in a.contexts))continue;const k=H(a,i,{context:h}).report;m.push({...k,surface:h,lints:V(k)})}return{dataPalette:p(l.properties),reports:m}},f=L(o,"light"),E=L(n,"dark"),s={light:f.dataPalette,dark:E.dataPalette};return{surfaces:c,dataPalette:s,paletteLints:{light:v(s.light),dark:v(s.dark)},light:f.reports,dark:E.reports}}function u(e,t){const o=Math.pow(10,t);return Math.round(e*o)/o}function z(e,t){return t===void 0?e:`${e}.${t}`}function ce(e){const t={};for(const[o,n]of Object.entries(e)){if(typeof n=="string"){t[o]=n;continue}for(const[c,p]of Object.entries(n))t[c==="color"?o:z(o,c)]=p}return t}function ie(e){const t=_(e.resolved),o=e.requested===void 0?e.resolved:$(e.requested),n={token:e.token,property:e.property,source:e.source,lightness:e.lightness,requested:o,resolved:e.resolved,adjusted:o!==e.resolved,sourceResolved:e.sourceResolved},c=X(e.source);if(c&&e.sourceResolved&&(n.anchor={reference:z(c.name,c.slot),color:$(e.base),deltaE:u(x(e.base,t),4)}),e.contrast){const p=e.contrast.background;n.apca={from:o,to:e.resolved,targetLc:e.contrast.targetLc,against:e.contrast.against,againstColor:$(p),fromLc:u(Math.abs(T(_(o),p)),1),toLc:u(Math.abs(T(t,p)),1)}}return n}function de(e){if(!e.apca)return"";const{targetLc:t,against:o,fromLc:n,toLc:c}=e.apca;return e.adjusted?`Lc ${t} vs ${o} \u2014 pushed from Lc ${n} to Lc ${c}`:`Lc ${t} vs ${o} \u2014 met at Lc ${n}`}function le(e){return e.anchor?`${e.anchor.reference} \u0394E ${e.anchor.deltaE}`:e.sourceResolved?"":"unresolved \u2014 seed used"}export{S as ROOT_SURFACE,Ie as THEME_FIT_ANCHOR_FIELDS,be as THEME_FIT_APCA_FIELDS,Me as THEME_FIT_DATA_PALETTE_LINT_FIELDS,Ee as THEME_FIT_DATA_PALETTE_LINT_IDS,Te as THEME_FIT_EXPLICIT_VALUES,Re as THEME_FIT_LINT_FIELDS,fe as THEME_FIT_LINT_IDS,ve as THEME_FIT_LINT_SEVERITIES,_e as THEME_FIT_PAIRING_VALUES,Ae as THEME_FIT_REPORT_FIELDS,we as THEME_FIT_SUITE_FIELDS,Le as THEME_FIT_TOKEN_FIELDS,ge as printThemeFitReport,V as themeFitLints,ke as themeFitReport,Oe as themeFitReportSuite,ee as themeFitRows};
@@ -269,6 +269,32 @@ export declare const ROLE_NAMES: readonly RoleName[];
269
269
  * following `accent.text` exactly as before.
270
270
  */
271
271
  export declare const ROLE_TOKEN_PLAN: Readonly<Record<RoleName, Readonly<Record<string, ReadonlyArray<[SemanticColorName, LightnessKey]>>>>>;
272
+ /**
273
+ * Role slots whose ink lightness is derived from the surface that role
274
+ * actually painted, rather than taken from {@link ROLE_TOKEN_PLAN}.
275
+ *
276
+ * {@link resolvePairedInkStop} used to run only for a slot the theme
277
+ * left *undeclared*, so naming `action.ink` took the plan's `surface`
278
+ * stop verbatim. In a light ramp both rules answer `surface` and the
279
+ * difference never showed. In a dark ramp the plan's stop is the
280
+ * near-black page ground, which is the wrong side of a mid-dark action
281
+ * surface: enforcement had to walk the label the whole way across the
282
+ * ramp and stopped at the first value that cleared the target, emitting
283
+ * a weaker ink than the ramp could give. ADR-016 is explicit that the
284
+ * model's job is to hand enforcement the strongest available starting
285
+ * point on the correct side, so the derivation now runs for a declared
286
+ * slot too (ESP0223).
287
+ *
288
+ * The source still comes from the theme: naming `action.ink` chooses
289
+ * the family, exactly as ADR-015 says an anchor supplies hue and chroma
290
+ * while the ramp supplies lightness.
291
+ *
292
+ * Every entry here must also appear in {@link ROLE_PAIRED_INK}: the
293
+ * compile branch is gated on `paired` first, and
294
+ * {@link roleInkProvenance} walks the pairing table, so an entry in only
295
+ * this set would be silently inert in both.
296
+ */
297
+ export declare const ROLE_DERIVED_INK: ReadonlySet<string>;
272
298
  /**
273
299
  * Ink slots that derive from another role when the theme does not
274
300
  * declare them.
@@ -293,6 +319,8 @@ interface CompileRoleOptions {
293
319
  actionSurfaceContrast?: (source: MappingSource, stop: LightnessKey) => number;
294
320
  /** ΔE-OK between an action surface candidate and the theme's background. */
295
321
  actionSurfaceSeparation?: (source: MappingSource, stop: LightnessKey) => number;
322
+ /** Which ink pole reads better on a finished action surface at a lightness. */
323
+ actionSurfacePole?: (source: MappingSource, lightness: number) => "dark" | "light";
296
324
  }
297
325
  /**
298
326
  * Compile roles into semantic mappings.
@@ -304,6 +332,23 @@ interface CompileRoleOptions {
304
332
  * reach.
305
333
  */
306
334
  export declare function compileRoles(roles: ThemeRoles, lightness: LightnessMap, baseMappings: SemanticMappings, options?: CompileRoleOptions): Partial<SemanticMappings>;
335
+ /**
336
+ * Whether each role-paired ink token's *source* was named by the theme
337
+ * or paired off another role (ESP0223).
338
+ *
339
+ * The distinction matters to anything judging how far a token landed
340
+ * from the swatch it names. `action.ink` defaults to the canvas colour,
341
+ * so an undeclared ink IS the page ground by contract — it is re-lit to
342
+ * the opposite end of the ramp on purpose, and the large anchor ΔE that
343
+ * follows describes the contract working, not a theme missing its
344
+ * brand. A theme that names the slot has made a separate claim about
345
+ * the label's family, and the same ΔE then describes a claim that did
346
+ * not survive.
347
+ *
348
+ * A pure read of `roles`: the compile is not re-run, and a role the
349
+ * theme never declared reports nothing rather than guessing.
350
+ */
351
+ export declare function roleInkProvenance(roles: ThemeRoles): Partial<Record<SemanticColorName, "declared" | "derived">>;
307
352
  /** The complete, resolved Espalier theme. */
308
353
  export interface EspalierTheme {
309
354
  /**
@@ -665,6 +710,11 @@ export declare const DEFAULT_DARK_LIGHTNESS: Readonly<LightnessMap>;
665
710
  *
666
711
  * Each entry tells the color engine which variant supplies the
667
712
  * hue and which lightness key sets the perceived brightness.
713
+ *
714
+ * `actionBackground` carries the **light** scheme's stop; the dark
715
+ * theme overrides it through {@link buildDefaultSemanticMappings}. See
716
+ * {@link DEFAULT_ACTION_BACKGROUND_STOP} for why that one mapping
717
+ * cannot share a name across schemes.
668
718
  */
669
719
  export declare const DEFAULT_SEMANTIC_MAPPINGS: Readonly<SemanticMappings>;
670
720
  /** Built-in authored stack for body and UI text. */