@wafertools/wafermap 0.24.2 → 0.25.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/dist/packages/canvas-adapter/charts/barPanel.d.ts +5 -0
  3. package/dist/packages/canvas-adapter/charts/barPanel.js +1 -1
  4. package/dist/packages/canvas-adapter/charts/binCluster.d.ts +5 -0
  5. package/dist/packages/canvas-adapter/charts/binCluster.js +1 -1
  6. package/dist/packages/canvas-adapter/charts/boxplot.d.ts +5 -0
  7. package/dist/packages/canvas-adapter/charts/boxplot.js +1 -1
  8. package/dist/packages/canvas-adapter/charts/capability.d.ts +5 -0
  9. package/dist/packages/canvas-adapter/charts/capability.js +1 -1
  10. package/dist/packages/canvas-adapter/charts/chartShell.d.ts +24 -5
  11. package/dist/packages/canvas-adapter/charts/chartShell.js +1 -1
  12. package/dist/packages/canvas-adapter/charts/correlation.d.ts +5 -0
  13. package/dist/packages/canvas-adapter/charts/correlation.js +1 -1
  14. package/dist/packages/canvas-adapter/charts/histogram.d.ts +5 -0
  15. package/dist/packages/canvas-adapter/charts/histogram.js +1 -1
  16. package/dist/packages/canvas-adapter/charts/regionYieldDiagram.d.ts +5 -0
  17. package/dist/packages/canvas-adapter/charts/regionYieldDiagram.js +1 -1
  18. package/dist/packages/canvas-adapter/charts/scatter.d.ts +5 -0
  19. package/dist/packages/canvas-adapter/charts/scatter.js +1 -1
  20. package/dist/packages/canvas-adapter/dieList.d.ts +9 -0
  21. package/dist/packages/canvas-adapter/dieList.js +6 -6
  22. package/dist/packages/canvas-adapter/insightsTab.d.ts +5 -0
  23. package/dist/packages/canvas-adapter/insightsTab.js +1 -1
  24. package/dist/packages/canvas-adapter/metadataBadge.d.ts +4 -0
  25. package/dist/packages/canvas-adapter/metadataBadge.js +1 -1
  26. package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +14 -1
  27. package/dist/packages/canvas-adapter/renderWaferGallery.js +7 -7
  28. package/dist/packages/canvas-adapter/renderWaferMap.d.ts +22 -0
  29. package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
  30. package/dist/packages/canvas-adapter/summaryPanel.d.ts +1 -1
  31. package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
  32. package/dist/packages/canvas-adapter/toCanvas.d.ts +11 -0
  33. package/dist/packages/canvas-adapter/toCanvas.js +1 -1
  34. package/dist/packages/canvas-adapter/toolbar.d.ts +57 -8
  35. package/dist/packages/canvas-adapter/toolbar.js +2 -2
  36. package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
  37. package/dist/packages/canvas-adapter/userGuideHtml.js +1 -1
  38. package/dist/packages/canvas-adapter/version.d.ts +2 -2
  39. package/dist/packages/canvas-adapter/version.js +1 -1
  40. package/dist/packages/canvas-adapter/warnings.d.ts +1 -1
  41. package/dist/packages/canvas-adapter/warnings.js +1 -1
  42. package/dist/packages/renderer/buildView.d.ts +22 -0
  43. package/dist/packages/renderer/buildView.js +1 -1
  44. package/dist/packages/stats/renderSummaryReport.js +1 -1
  45. package/package.json +5 -5
@@ -1,3 +1,3 @@
1
- import{isParametricTest as xt}from"../renderer/buildWaferMap.js";import{buildRingRegions as K,buildQuadrantRegions as Z,buildRegionYieldData as bt}from"../stats/regions.js";import{computeFunctionalYield as vt}from"../stats/analyzeWaferMap.js";import{openHtmlReport as J}from"../stats/renderFindingsReport.js";import{renderSummaryReportHtml as yt,renderLotSummaryReportHtml as wt}from"../stats/renderSummaryReport.js";import{buildFindingsNarrative as Ct}from"../stats/findingsNarrative.js";import{filterFindings as St}from"../stats/filterFindings.js";import{buildFacetTable as kt,prettyKey as tt}from"../stats/facets.js";import{commonMetadata as et}from"../stats/facets.js";import{resolveMetadataColumns as Bt}from"../stats/metadataColumns.js";import{getColorScheme as $t}from"../renderer/colorSchemes.js";import{buildWarningsBanner as _,collectWarnings as nt}from"./warnings.js";export{_ as buildWarningsBanner};import{fmt as it,fmtAggregationMethod as Lt,plainBinTerms as P}from"../renderer/fmt.js";import{getUniqueTestNumbers as Dt}from"../renderer/buildView.js";import{quantile as Q}from"../stats/math.js";import{makeLabeledSelect as ot}from"./charts/chartShell.js";import{CLR as y,sevColor as G,openModal as at,saveTextFile as rt}from"./toolbar.js";import{buildDieListSection as Rt}from"./dieList.js";import{medianOfSorted as Ft}from"../core/utils.js";import{metadataDisplayValue as Tt}from"../core/metadata.js";const Et=y.panelBg,Y=`1px solid ${y.menuBorder}`,Nt="12px",W=y.label,z=y.value,st="10px";function l(t,i,e){const n=document.createElement(t);return i&&Object.assign(n.style,i),e!==void 0&&(n.textContent=e),n}function H(t){return l("div",{fontSize:st,fontWeight:"700",letterSpacing:"0.06em",textTransform:"uppercase",color:W,marginBottom:"6px"},t)}function lt(t,i=!0,e){const n=l("div"),o=document.createElement("button");o.type="button",Object.assign(o.style,{display:"flex",alignItems:"center",gap:"4px",width:"100%",background:"none",border:"none",cursor:"pointer",padding:"0 0 6px",textAlign:"left"});const d=l("span",{fontSize:"9px",color:W,transition:"transform 0.15s",transform:i?"rotate(90deg)":"rotate(0deg)",display:"inline-block",lineHeight:"1",marginRight:"1px"},"\u25B6"),f=l("span",{fontSize:st,fontWeight:"700",letterSpacing:"0.06em",textTransform:"uppercase",color:W,flex:"1"},t);if(o.setAttribute("aria-expanded",i?"true":"false"),o.appendChild(d),o.appendChild(f),e){const a=l("span",{fontSize:"9px",fontWeight:"700",background:y.warnBg,color:y.warnText,borderRadius:"10px",padding:"1px 5px"},e);o.appendChild(a)}const c=l("div");c.style.display=i?"block":"none";let s=i;return o.addEventListener("click",()=>{s=!s,c.style.display=s?"block":"none",d.style.transform=s?"rotate(90deg)":"rotate(0deg)",o.setAttribute("aria-expanded",s?"true":"false")}),n.appendChild(o),n.appendChild(c),{outer:n,content:c}}function dt(){return l("div",{height:"1px",background:y.separator,margin:`${Nt} 0`,flexShrink:"0"})}function U(t,i,e="#2a6fc0",n,o,d){const f=l("div",{marginBottom:"5px"}),c=l("div",{display:"flex",justifyContent:"space-between",fontSize:"11px",color:z,marginBottom:"2px"}),s=l("span",{},t),a=l("span",{fontWeight:"600"},`${i.toFixed(1)}%`);c.appendChild(s),c.appendChild(a);const p=n!==void 0?n:Math.min(100,Math.max(0,i)),v=d?"#94a3b8":e,h=l("div",{position:"relative",height:"9px",background:y.bgActive,borderRadius:"4px",overflow:"hidden"}),T=l("div",{height:"100%",width:`${p}%`,background:v,borderRadius:"4px",transition:"width 0.3s ease"});if(h.appendChild(T),o!==void 0){const N=l("div",{position:"absolute",top:"0",bottom:"0",left:`${o}%`,width:"1px",background:y.infoText,opacity:"0.5"});h.appendChild(N)}return f.appendChild(c),f.appendChild(h),f}function O(t,i,e){const n=l("div",{background:y.menuBg,border:Y,borderRadius:"6px",padding:"8px 10px",textAlign:"center",flex:"1"}),o=l("div",{fontSize:"20px",fontWeight:"700",color:z,lineHeight:"1.2"},t),d=l("div",{fontSize:"10px",color:W,marginTop:"2px"},i);return n.appendChild(o),n.appendChild(d),e&&n.appendChild(l("div",{fontSize:"9px",color:W,opacity:"0.85"},e)),n}function Mt(t,i){const e=l("div",{display:"flex",justifyContent:"space-between",fontSize:"11px",gap:"8px",marginBottom:"3px"}),n=l("span",{color:W,flexShrink:"0"},t),o=l("span",{color:z,textAlign:"right",fontWeight:"500",wordBreak:"break-all"},i);return e.appendChild(n),e.appendChild(o),e}function It(t,i){const e=l("div",{display:"flex",fontSize:"11px",gap:"6px",marginBottom:"3px"}),n=l("span",{color:W,flexShrink:"0"},`${t}:`),o=l("span",{color:z,fontWeight:"500",wordBreak:"break-all"},i);return e.appendChild(n),e.appendChild(o),e}export function metadataEntries(t){const i=[];for(const[e,n]of Object.entries(t)){const o=Tt(n);o!==void 0&&i.push([e,o])}return i}export function buildFacetSummaryChips(t,i=3){if(!t.length)return null;const e=l("div",{display:"flex",flexWrap:"wrap",alignItems:"baseline",gap:"4px 10px",fontSize:"11px"});for(const n of t){const o=l("span",{whiteSpace:"nowrap"}),d=l("span",{color:W},`${tt(n.key)}: `);o.appendChild(d);const f=n.values.slice(0,i),c=n.values.length-f.length;let s=f.map(a=>a.value).join(", ");c>0&&(s+=` +${c} more`),o.appendChild(document.createTextNode(s)),e.appendChild(o)}return e}export function buildMetadataStripRow(t,i,e){const n=kt(t,e),o=buildFacetSummaryChips(n);if(!o&&!i)return null;const d=l("div",{display:"flex",flexWrap:"wrap",alignItems:"baseline",gap:"4px 14px"});if(i){const f=l("span",{fontWeight:"500",whiteSpace:"nowrap"},i.aggrMethod?`${i.lotSize} wafers stacked \xB7 ${i.aggrMethod}`:`${i.lotSize} wafers stacked`);d.appendChild(f)}return o&&d.appendChild(o),d}const Wt={display:"flex",flexDirection:"column",gap:"6px",background:y.menuBg,border:`1px solid ${y.menuBorder}`,borderRadius:"6px",padding:"6px 10px",boxShadow:"0 1px 4px rgba(0,0,0,0.10)",fontSize:"12px",lineHeight:"1",boxSizing:"border-box",width:"100%",minWidth:"0"};export function buildMetadataStripBox(t,i,e){const n=buildMetadataStripRow(t,i,e);if(!n)return null;const o=l("div",Wt);return o.appendChild(n),o}export function buildCompactMetadataRows(t){const i=metadataEntries(t);if(!i.length)return null;const e=l("div");for(const[n,o]of i)e.appendChild(It(tt(n),o));return e}export function buildMetadataInfoSection(t,i){const e=buildMetadataStripRow(t,i,{facetableOnly:t.length>1});if(!e)return null;const n=l("div");return n.appendChild(H("Wafer Info")),n.appendChild(e),n}export function buildYieldSection(t,i,e=[1]){const n=l("div");n.appendChild(H("Summary"));const o=l("div",{display:"flex",gap:"6px",marginBottom:"8px"});if(o.appendChild(O(String(t.totalDies),"Total dies")),t.partialDies>0&&o.appendChild(O(String(t.partialDies),"Partial")),t.yieldPercent!==null){const d=e.length===1?`bin ${e[0]}`:`bins ${e.join(", ")}`;o.appendChild(O(`${t.yieldPercent.toFixed(1)}%`,"Yield",`pass: ${d}`))}return n.appendChild(o),t.edgeExcludedDies>0&&n.appendChild(Mt("Edge excluded (outer zone)",String(t.edgeExcludedDies))),n}export function buildBinSection(t,i,e,n,o){const d=new Map;if(o)for(const[p,v]of Object.entries(o))d.set(Number(p),v);else for(const p of t){if(p.partial||p.edgeExcluded)continue;const v=e==="hard"?p.hbin:p.sbin;v!=null&&d.set(v,(d.get(v)??0)+1)}if(!d.size)return null;const f=[...d.values()].reduce((p,v)=>p+v,0),c=i?new Map(i.map(p=>[p.bin,p])):null,s=[...d.entries()].sort((p,v)=>p[0]-v[0]),a=l("div");a.appendChild(H(e==="hard"?"Hard Bin Breakdown":"Soft Bin Breakdown"));for(const[p,v]of s){const h=c?.get(p),T=h?.name?`Bin ${p} \xB7 ${h.name}`:`Bin ${p}`,N=v/f*100,B=$t(n),E=(n==="custom"?h?.color:void 0)??B.forBin(p);a.appendChild(U(`${T} (${v})`,N,E))}return a}export function buildLotBinSection(t,i,e,n){return buildBinSection(t,i,e,n)}function j(t,i,e,n,o,d){const f=bt(t,i,e,n,o);if(!f.length)return null;const c=l("div");c.appendChild(H(d));for(const{label:s,n:a,yieldPercent:p}of f)c.appendChild(U(`${s} (N=${a})`,p));return c}export function buildRingSection(t,i,e,n){return j([t],[i],e,n,K,"Ring Yield")}export function buildQuadrantSection(t,i,e,n){return j([t],[i],e,n,Z,"Quadrant Yield")}export function buildLotRingSection(t,i,e,n){return j(t,i,e,n,K,"Ring Yield")}export function buildLotQuadrantSection(t,i,e,n){return j(t,i,e,n,Z,"Quadrant Yield")}export function buildLotOverviewSection(t){const i=l("div");i.appendChild(H("Lot Summary"));const e=l("div",{display:"flex",gap:"6px",marginBottom:"8px"});e.appendChild(O(String(t.stats.waferCount),"Wafers"));const n=t.perWafer.map(o=>o.summary.stats.yieldPercent).filter(o=>o!==null);if(n.length){const o=n.reduce((d,f)=>d+f,0)/n.length;e.appendChild(O(`${o.toFixed(1)}%`,"Mean wafer yield"))}return i.appendChild(e),i}export function buildPerWaferYieldSection(t,i,e){const n=t.perWafer.map(a=>({waferIndex:a.waferIndex,label:(i[a.waferIndex]?.label??`W${a.waferIndex+1}`).replace(/\s*·\s*\d+(\.\d+)?%$/,""),yieldPct:a.summary.stats.yieldPercent})).filter(a=>a.yieldPct!==null);if(!n.length)return null;const o=Math.min(...n.map(a=>a.yieldPct)),d=Math.max(...n.map(a=>a.yieldPct)),f=o===d?"":` (${o.toFixed(1)}\u2013${d.toFixed(1)}%)`,c=Ft([...n.map(a=>a.yieldPct)].sort((a,p)=>a-p)),s=l("div");s.appendChild(H("Wafer Yield"+f));for(const{waferIndex:a,label:p,yieldPct:v}of n){const h=U(p,v,void 0,void 0,c,v<c);e&&(h.style.cursor="pointer",h.style.borderRadius="4px",h.style.padding="2px 3px",h.style.marginLeft="-3px",h.style.marginRight="-3px",h.setAttribute("role","button"),h.setAttribute("aria-label",`${p}, ${v.toFixed(1)}% yield \u2014 view wafer`),h.tabIndex=0,h.addEventListener("mouseenter",()=>{h.style.background=y.bgHover}),h.addEventListener("mouseleave",()=>{h.style.background=""}),h.addEventListener("focus",()=>{h.style.background=y.bgHover}),h.addEventListener("blur",()=>{h.style.background=""}),h.addEventListener("click",()=>e(a)),h.addEventListener("keydown",T=>{(T.key==="Enter"||T.key===" ")&&(T.preventDefault(),e(a))})),s.appendChild(h)}return s}function zt(t){const i=[...t].sort((o,d)=>o-d),e=t.reduce((o,d)=>o+d,0)/t.length,n=t.reduce((o,d)=>o+(d-e)**2,0)/t.length;return{min:i[0],max:i[i.length-1],mean:e,count:t.length,stddev:Math.sqrt(n),median:Q(i,.5),q1:Q(i,.25),q3:Q(i,.75)}}function pt(t,i){if(!t)return[];const e=t.waferMetadata??(t.perWaferMetadata?et(t.perWaferMetadata.map(n=>({metadata:n}))):void 0);return!e||!Object.keys(e).length?[]:Bt({waferMetadata:e,waferPlacement:"csv",reservedLabels:i}).columns}export function csvField(t){return/[",\n]/.test(t)?`"${t.replace(/"/g,'""')}"`:t}export function buildTestSection(t,i,e,n,o,d){const f=t.filter(r=>!r.partial&&!r.edgeExcluded),c=new Map((n?.perTestStats??[]).map(r=>[r.testNumber,r])),s=new Map((n?.testSpecYield??[]).map(r=>[r.testNumber,r]));let a;if(i?.length?a=i.filter(xt).map(r=>({testNumber:r.testNumber,name:r.name,unit:r.unit,limitLow:r.limitLow,limitHigh:r.limitHigh})):a=Dt(f).map(m=>({testNumber:m,name:`Test ${m}`})),!a.length)return null;const p=a.filter(r=>f.some(m=>{const D=m.testValues?.[r.testNumber];return D!==void 0&&isFinite(D)}));if(!p.length)return null;const v=p.some(r=>r.limitLow!==void 0||r.limitHigh!==void 0),h=[];for(const r of p){const m=c.get(r.testNumber),D=m&&m.stddev!==void 0&&m.median!==void 0&&m.q1!==void 0&&m.q3!==void 0;let w;if(D){const b=m;w={testNumber:r.testNumber,min:b.min,max:b.max,mean:b.mean,count:b.count,stddev:b.stddev,median:b.median,q1:b.q1,q3:b.q3}}else{const b=f.map(F=>F.testValues?.[r.testNumber]).filter(F=>F!==void 0&&isFinite(F));if(!b.length)continue;w={testNumber:r.testNumber,...zt(b)}}let x=null,S=0;if(r.limitLow!==void 0||r.limitHigh!==void 0){const b=s.get(r.testNumber);if(b)({yieldPercent:x,totalDies:S}=b);else{const F=f.map(g=>g.testValues?.[r.testNumber]).filter(g=>g!==void 0&&isFinite(g)),I=F.filter(g=>r.limitLow!==void 0&&g<r.limitLow||r.limitHigh!==void 0&&g>r.limitHigh).length;S=F.length,x=F.length>0?(F.length-I)/F.length*100:null}}h.push({entry:r,stats:w,specYieldPct:x,specN:S})}if(!h.length)return null;const T=l("div"),N=l("div",{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:"6px"}),B=H(d?.populationLabel?`Test Values (${h.length}) \u2014 ${d.populationLabel}`:`Test Values (${h.length})`);B.style.marginBottom="0",N.appendChild(B);const E=document.createElement("button");E.type="button",E.textContent="Export CSV",Object.assign(E.style,{background:"none",border:Y,borderRadius:"4px",cursor:"pointer",fontSize:"10px",color:y.iconHover,padding:"2px 7px"}),E.addEventListener("click",()=>{const r=["Test","Unit","N","Min","Q1","Median","Mean","Q3","Max","StdDev"];v&&r.push("LSL","USL","Spec Yield %","Spec Yield N");const m=pt(d,r),w=[[...m.map(S=>S.label),...r].map(csvField).join(",")],x=S=>it(S,void 0,"engineering");for(const{entry:S,stats:b,specYieldPct:F,specN:I}of h){const g=[...m.map(M=>M.constant??""),S.name,S.unit??"",String(b.count),x(b.min),x(b.q1),x(b.median),x(b.mean),x(b.q3),x(b.max),x(b.stddev)];v&&g.push(S.limitLow!==void 0?x(S.limitLow):"",S.limitHigh!==void 0?x(S.limitHigh):"",F!==null?F.toFixed(1):"",S.limitLow!==void 0||S.limitHigh!==void 0?String(I):""),w.push(g.map(csvField).join(","))}rt(w.join(`
2
- `),"test-values.csv","text/csv",o)}),N.appendChild(E),T.appendChild(N);const R=l("table",{width:"100%",borderCollapse:"collapse",fontSize:"11px"}),L=document.createElement("thead"),u=document.createElement("tr"),$=["Test","N","Min","Q1","Median","Mean","Q3","Max","StdDev"];v&&$.push("LSL","USL","Spec yield");for(const r of $){const m=l("th",{textAlign:r==="Test"?"left":"right",fontWeight:"700",fontSize:"10px",letterSpacing:"0.03em",textTransform:"uppercase",color:W,padding:"3px 8px 5px",borderBottom:`1px solid ${y.menuBorder}`,whiteSpace:"nowrap"},r);u.appendChild(m)}L.appendChild(u),R.appendChild(L);const k=document.createElement("tbody");for(const{entry:r,stats:m,specYieldPct:D,specN:w}of h){const x=F=>it(F,r.unit,e),S=document.createElement("tr"),b=(F,I="right")=>{const g=l("td",{textAlign:I,padding:"4px 8px",borderBottom:`1px solid ${y.menuBorder}`,color:z,whiteSpace:"nowrap"},F);S.appendChild(g)};b(r.name,"left"),b(`${m.count}`),b(x(m.min)),b(x(m.q1)),b(x(m.median)),b(x(m.mean)),b(x(m.q3)),b(x(m.max)),b(x(m.stddev)),v&&(b(r.limitLow!==void 0?x(r.limitLow):"\u2014"),b(r.limitHigh!==void 0?x(r.limitHigh):"\u2014"),b(D!==null?`${D.toFixed(1)}% (N=${w})`:"\u2014")),k.appendChild(S)}R.appendChild(k);const C=l("div",{overflowX:"auto"});return C.appendChild(R),T.appendChild(C),T}export function buildFunctionalTestSection(t,i,e,n,o){const d=e??vt(t,i);if(!d?.length)return null;const f=l("div"),c=l("div",{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:"6px"}),s=H(o?.populationLabel?`Functional Tests (${d.length}) \u2014 ${o.populationLabel}`:`Functional Tests (${d.length})`);s.style.marginBottom="0",c.appendChild(s);const a=document.createElement("button");a.type="button",a.textContent="Export CSV",Object.assign(a.style,{background:"none",border:Y,borderRadius:"4px",cursor:"pointer",fontSize:"10px",color:y.iconHover,padding:"2px 7px"}),a.addEventListener("click",()=>{const B=["Test","N","Pass","Fail","Pass Rate %"],E=pt(o,B),R=[[...E.map(L=>L.label),...B].map(csvField).join(",")];for(const L of d)R.push([...E.map(u=>u.constant??""),L.label,String(L.totalDies),String(L.passDies),String(L.failDies),L.passRatePercent!==null?L.passRatePercent.toFixed(1):""].map(csvField).join(","));rt(R.join(`
3
- `),"functional-tests.csv","text/csv",n)}),c.appendChild(a),f.appendChild(c);const p=l("table",{width:"100%",borderCollapse:"collapse",fontSize:"11px"}),v=document.createElement("thead"),h=document.createElement("tr");for(const B of["Test","N","Pass","Fail","Pass rate"])h.appendChild(l("th",{textAlign:B==="Test"?"left":"right",fontWeight:"700",fontSize:"10px",letterSpacing:"0.03em",textTransform:"uppercase",color:W,padding:"3px 8px 5px",borderBottom:`1px solid ${y.menuBorder}`,whiteSpace:"nowrap"},B));v.appendChild(h),p.appendChild(v);const T=document.createElement("tbody");for(const B of d){const E=document.createElement("tr"),R=(L,u="right")=>{E.appendChild(l("td",{textAlign:u,padding:"4px 8px",borderBottom:`1px solid ${y.menuBorder}`,color:z,whiteSpace:"nowrap"},L))};R(B.label,"left"),R(String(B.totalDies)),R(String(B.passDies)),R(String(B.failDies)),R(B.passRatePercent!==null?`${B.passRatePercent.toFixed(1)}% (N=${B.totalDies})`:"\u2014"),T.appendChild(E)}p.appendChild(T);const N=l("div",{overflowX:"auto"});return N.appendChild(p),f.appendChild(N),f}export function buildLotFunctionalSection(t,i,e,n){const o=e?.length?{perWaferMetadata:e.map(d=>d.wafer??{}),populationLabel:`${e.length} wafer${e.length===1?"":"s"} pooled`}:void 0;if(e?.length&&e.every(d=>d.stats.functionalYield!==void 0)){const d=new Map;for(const c of e)for(const s of c.stats.functionalYield??[]){const a=d.get(s.testNumber)??{label:s.label,passDies:0,failDies:0,totalDies:0};a.passDies+=s.passDies,a.failDies+=s.failDies,a.totalDies+=s.totalDies,d.set(s.testNumber,a)}const f=[...d.entries()].map(([c,s])=>({testNumber:c,...s,passRatePercent:s.totalDies>0?s.passDies/s.totalDies*100:null}));if(f.length)return buildFunctionalTestSection(t,i,f,n,o)}return buildFunctionalTestSection(t,i,void 0,n,o)}const A={unusual:0,notable:1,info:2};function X(t){return l("span",{display:"inline-block",width:"7px",height:"7px",borderRadius:"50%",background:G(t),flexShrink:"0"})}function ct(t,i){let e=t.summary;return i&&(e.startsWith(`${i} has `)?e=e.slice(i.length+5):e.startsWith(`${i} `)&&(e=e.slice(i.length+1))),e=P(e),e.length?e[0].toUpperCase()+e.slice(1):e}export function buildFindingsSection(t,i,e,n,o=!1){if(!t.length)return null;const d=t.some(u=>u.severity==="unusual"||u.severity==="notable"),f=d?t.filter(u=>u.severity!=="info").length.toString():void 0,{outer:c,content:s}=o?(()=>{const u=l("div");return{outer:u,content:u}})():lt(`Findings (${t.length})`,d,f);function a(u){return u.reduce(($,k)=>A[k.severity]<A[$]?k.severity:$,"info")}function p(u){const $=new Map;for(const C of u){const r=`${C.comparison.family}\0${C.comparison.left}`,m=$.get(r)??[];m.push(C),$.set(r,m)}const k=[...$.entries()].map(([C,r])=>{const m=[...r].sort((D,w)=>A[D.severity]-A[w.severity]);return{key:C,findings:m,worst:a(m)}});return k.sort((C,r)=>A[C.worst]-A[r.worst]),k}function v(u,$){const C={ring:"Ring",quadrant:"Quadrant",sector:"Sector","reticle-position":"Reticle","test-site":"Test site",cluster:"Cluster","edge-arc":"Edge arc",wafer:"Wafer"}[u]??u;return $.toLowerCase().startsWith(C.toLowerCase())?$:`${C}: ${$}`}const h=t.filter(u=>u.comparison.family==="spatial-pattern"),T=new Set([...h.flatMap(u=>u.relatedIds??[]),...t.flatMap(u=>u.absorbedIds??[])]),N=t.filter(u=>u.comparison.family!=="spatial-pattern"&&!T.has(u.id)),B=p(N);function E(u,$=!1,k){const C=n===u.id,r=document.createElement("button");return r.type="button",r.dataset.wmapFinding=u.id,r.textContent=ct(u,k),r.title=u.summary,r.setAttribute("aria-current",C?"true":"false"),Object.assign(r.style,{border:`1px solid ${y.menuBorder}`,borderLeft:`3px solid ${G(u.severity)}`,background:C?y.bgActive:y.menuBg,borderRadius:"6px",padding:$?"6px 10px":"8px 10px",textAlign:"left",fontSize:$?"10px":"11px",fontWeight:C?"600":"400",color:y.iconHover,cursor:"pointer",width:"100%",marginBottom:"4px"}),r.addEventListener("click",()=>e(u,r)),r}const R=P(Ct(t)??"");if(R){const u=l("div",{background:y.bgActive,borderRadius:"6px",padding:"8px 10px",marginBottom:"8px",display:"flex",gap:"8px",alignItems:"flex-start"}),$=l("span",{flex:"1",fontSize:"12px",lineHeight:"1.5",color:y.iconHover},R);u.appendChild($);const k=l("button",{flexShrink:"0",border:"none",background:"none",fontSize:"10px",color:y.icon,cursor:"pointer",padding:"0",lineHeight:"1.5",whiteSpace:"nowrap"},"Detail \u25B8");k.type="button",k.addEventListener("click",()=>{const C=at({title:"Findings Summary",onClose:()=>{},anchor:k}),r=l("p",{fontSize:"16px",lineHeight:"1.6",color:y.iconHover,padding:"20px 24px 16px",margin:"0",borderBottom:`1px solid ${y.menuBorder}`,flexShrink:"0"},R);C.contentWrap.appendChild(r);const m=l("div",{overflowY:"auto",padding:"16px 24px",flex:"1"}),D=(w,x)=>{const S=l("div",{display:"flex",alignItems:"center",gap:"7px",fontSize:"13px",fontWeight:"600",color:z,marginTop:"10px",marginBottom:"4px"});return S.appendChild(X(w)),S.appendChild(l("span",{},x)),S};for(const w of h){m.appendChild(D(w.severity,w.comparison.left)),m.appendChild(l("div",{fontSize:"13px",color:y.iconHover,padding:"3px 0 3px 15px",marginBottom:"2px"},P(w.summary)));const x=t.filter(S=>w.relatedIds?.includes(S.id));for(const S of x)m.appendChild(l("div",{fontSize:"12px",color:y.icon,padding:"2px 0 2px 23px",marginBottom:"2px"},P(S.summary)))}for(const w of B){const[x,S]=w.key.split("\0");m.appendChild(D(w.worst,v(x,S)));for(const b of w.findings)m.appendChild(l("div",{fontSize:"13px",color:y.iconHover,padding:"3px 0 3px 15px",marginBottom:"2px"},ct(b,S)))}C.contentWrap.appendChild(m),Object.assign(C.contentWrap.style,{flexDirection:"column",overflow:"hidden"})}),u.appendChild(k),s.appendChild(u)}let L=!0;for(const u of h){L||s.appendChild(l("div",{height:"1px",background:y.separator,margin:"4px 0"})),L=!1;const $=t.filter(D=>u.relatedIds?.includes(D.id)),k=$.length>0,C=l("div",{position:"relative",marginBottom:k?"2px":"4px"}),r=n===u.id,m=document.createElement("button");if(m.type="button",m.dataset.wmapFinding=u.id,Object.assign(m.style,{border:`1px solid ${y.menuBorder}`,borderLeft:`3px solid ${G(u.severity)}`,background:r?y.bgActive:y.menuBg,borderRadius:"6px",padding:"8px 32px 8px 10px",textAlign:"left",fontSize:"11px",fontWeight:r?"600":"500",color:y.iconHover,cursor:"pointer",width:"100%"}),m.textContent=P(u.summary),m.title=u.summary,m.setAttribute("aria-current",r?"true":"false"),m.addEventListener("click",()=>e(u,m)),C.appendChild(m),k){const D=l("div",{display:"none",paddingLeft:"12px",marginBottom:"4px"});for(const S of $)D.appendChild(E(S,!0));C.appendChild(D);let w=!1;const x=l("button",{position:"absolute",top:"50%",right:"8px",transform:"translateY(-50%)",border:"none",background:"none",fontSize:"10px",color:y.icon,cursor:"pointer",padding:"2px 4px",lineHeight:"1"},"\u25B8");x.type="button",x.title="Show supporting findings",x.setAttribute("aria-label","Show supporting findings"),x.setAttribute("aria-expanded","false"),x.addEventListener("click",S=>{S.stopPropagation(),w=!w,D.style.display=w?"block":"none",x.textContent=w?"\u25BE":"\u25B8";const b=w?"Hide supporting findings":"Show supporting findings";x.title=b,x.setAttribute("aria-label",b),x.setAttribute("aria-expanded",String(w))}),C.appendChild(x)}s.appendChild(C)}for(const u of B){L||s.appendChild(l("div",{height:"1px",background:y.separator,margin:"4px 0"})),L=!1;const[$,k]=u.key.split("\0"),C=l("div",{display:"flex",alignItems:"center",gap:"6px",fontSize:"11px",fontWeight:"600",color:z,margin:"2px 0 4px"});C.appendChild(X(u.worst)),C.appendChild(l("span",{},v($,k))),s.appendChild(C);for(const r of u.findings)s.appendChild(E(r,!1,k))}return c}const Ht=[{value:"",label:"All kinds"},{value:"yield",label:"Yield"},{value:"hardBin",label:"Hard bin"},{value:"softBin",label:"Soft bin"},{value:"test",label:"Test value"},{value:"functionalTest",label:"Functional test"},{value:"spatialPattern",label:"Spatial pattern"}],At=[{value:"",label:"All regions"},{value:"ring",label:"Ring"},{value:"quadrant",label:"Quadrant"},{value:"reticle-position",label:"Reticle position"},{value:"test-site",label:"Test site"},{value:"wafer",label:"Wafer"},{value:"sector",label:"Sector"},{value:"cluster",label:"Cluster"},{value:"edge-arc",label:"Edge arc"},{value:"spatial-pattern",label:"Spatial pattern"}],Pt=["unusual","notable","info"],Yt={unusual:"Unusual",notable:"Notable",info:"Info"};function Ot(t,i,e){const n=l("div",{display:"flex",flexWrap:"wrap",alignItems:"center",gap:"10px",marginBottom:"10px",paddingBottom:"10px",borderBottom:`1px solid ${y.separator}`}),o={unusual:0,notable:0,info:0};for(const s of t)o[s.severity]++;const d=Pt.filter(s=>o[s]>0),f=new Set(i.severity===void 0?d:Array.isArray(i.severity)?i.severity:[i.severity]),c=l("div",{display:"flex",gap:"4px",flexWrap:"wrap"});for(const s of d){const a=document.createElement("button");a.type="button";const p=()=>{const v=f.has(s);a.setAttribute("aria-pressed",v?"true":"false"),Object.assign(a.style,{display:"inline-flex",alignItems:"center",gap:"5px",border:`1px solid ${y.menuBorder}`,borderRadius:"10px",background:v?y.bgActive:"none",opacity:v?"1":"0.5",color:y.text,fontSize:"11px",padding:"2px 8px",cursor:"pointer"})};a.appendChild(X(s)),a.appendChild(l("span",{},`${Yt[s]} ${o[s]}`)),a.title=f.has(s)?"Click to hide these findings":"Click to show these findings",p(),a.addEventListener("click",()=>{f.has(s)?f.delete(s):f.add(s),i.severity=f.size===d.length?void 0:[...f],e()}),c.appendChild(a)}return n.appendChild(c),n.appendChild(ot("Kind:",Ht,i.kind??"",s=>{i.kind=s||void 0,e()},{maxWidth:"130px"})),n.appendChild(ot("Region:",At,i.family??"",s=>{i.family=s||void 0,e()},{maxWidth:"150px"})),n}export function buildFindingsSectionWithFilter(t,i,e,n,o){if(!t.findings.length)return null;const d=t.findings.some(v=>v.severity==="unusual"||v.severity==="notable"),f=d?t.findings.filter(v=>v.severity!=="info").length.toString():void 0,{outer:c,content:s}=lt(`Findings (${t.findings.length})`,d,f);s.appendChild(Ot(t.findings,n,o));const a=St(t,n);if(!a.length)return s.appendChild(l("div",{color:W,fontSize:"11px",textAlign:"center",padding:"16px 8px"},"No findings match the current filter.")),c;const p=buildFindingsSection(a,t,i,e,!0);return p&&s.appendChild(p),c}export function buildLotTestSection(t,i,e,n,o){const d=n?.length?{perWaferMetadata:n.map(c=>c.wafer??{}),populationLabel:`${n.length} wafer${n.length===1?"":"s"} pooled`}:void 0;let f;if(n?.length&&n.every(c=>c.stats.perTestStats!==void 0)){const c=new Map;for(const s of n)for(const a of s.stats.perTestStats??[]){const p=c.get(a.testNumber);p?(p.n+=a.count,p.sum+=a.mean*a.count,p.min=Math.min(p.min,a.min),p.max=Math.max(p.max,a.max)):c.set(a.testNumber,{n:a.count,sum:a.mean*a.count,min:a.min,max:a.max})}f={perTestStats:[...c.entries()].map(([s,a])=>({testNumber:s,count:a.n,min:a.min,max:a.max,mean:a.sum/a.n}))}}if(n?.length&&n.every(c=>c.stats.testSpecYield!==void 0)){const c=new Map;for(const s of n)for(const a of s.stats.testSpecYield??[]){const p=c.get(a.testNumber);p?(p.passDies+=a.passDies,p.totalDies+=a.totalDies):c.set(a.testNumber,{passDies:a.passDies,totalDies:a.totalDies})}f={...f,testSpecYield:[...c.entries()].map(([s,a])=>({testNumber:s,totalDies:a.totalDies,yieldPercent:a.totalDies>0?a.passDies/a.totalDies*100:null}))}}return buildTestSection(t,i,e,f,o,d)}export function createSummaryPanelEl(t){const i=t==="top"||t==="bottom",e=l("div",{background:Et,border:Y,borderRadius:"8px",padding:"12px",overflowY:i?"hidden":"auto",overflowX:i?"auto":"hidden",flexShrink:"0",boxSizing:"border-box",fontFamily:"system-ui, sans-serif",fontSize:"12px",boxShadow:"0 1px 4px rgba(0,0,0,0.08)"});return i?(e.style.height="180px",e.style.width="100%"):(e.style.width="260px",e.style.maxHeight="100%"),e}export function wrapWithSummaryPanel(t,i,e){const n=e==="top"||e==="bottom",o=l("div",{display:"flex",flexDirection:n?"column":"row",gap:"8px",width:"100%",height:"100%",flex:"1 1 0",minHeight:"0",boxSizing:"border-box",alignItems:n?"flex-start":"stretch"});return t.style.flex="1 1 0",t.style.minWidth="0",t.style.minHeight="0",e==="right"||e==="bottom"?(o.appendChild(t),o.appendChild(i)):(o.appendChild(i),o.appendChild(t)),o}function ut(t){return l("div",{fontSize:"13px",fontWeight:"700",color:z,marginBottom:"10px"},t)}function q(t,i){const e=l("button",{background:"none",border:Y,borderRadius:"4px",cursor:"pointer",fontSize:"10px",color:y.iconHover,padding:"2px 7px"},t);return e.type="button",e.addEventListener("click",i),e}function ft(...t){const i=t.filter(n=>!!n);if(!i.length)return null;const e=l("div",{display:"flex",flexWrap:"wrap",gap:"6px",marginBottom:"10px"});for(const n of i)e.appendChild(n);return e}function mt(t,i,e,n,o,d,f,c,s){const a=at({title:"Die list",onClose:()=>{},anchor:t}),p=Rt(i,e,{...f,title:n,onSaveText:c,waferMetadata:o,metadataFields:d,extraColumn:s});p&&a.contentWrap.appendChild(p)}export function renderWaferSummaryContent(t,i){const e=t.scrollTop;t.innerHTML="";const{wafer:n,dies:o,yieldSummary:d,dataCoverage:f,hbinDefs:c,sbinDefs:s,testDefs:a,statsSummary:p,passBins:v=[1],ringCount:h=4,colorScheme:T,fallbackFormat:N,onFindingClick:B,activeFindingId:E=null,findingsFilter:R,onFindingsFilterChange:L,onSaveText:u,metadataFields:$,dieListOptions:k}=i;t.appendChild(ut("Wafer Summary"));const C=i.warnings??nt({statsSummary:p});C.length&&t.appendChild(_(C));const r=d&&f?q("Summary report",()=>{J(yt({wafer:n,dies:o,yieldSummary:d,dataCoverage:f,hbinDefs:c,sbinDefs:s,testDefs:a,statsSummary:p,passBins:v,ringCount:h}))}):null,m=(k?.enabled??!0)&&o.length?q("View die list",()=>{mt(t,o,a,`Die list \u2014 ${o.length} dies`,n.metadata,$,k,u)}):null,D=ft(r,m);D&&t.appendChild(D);const w=[],x=p?.stats.isLotStack?p.stats:void 0,S=x?.lotSize!==void 0?{lotSize:x.lotSize,aggrMethod:Lt(x.aggregationMethod)}:void 0;w.push(buildMetadataInfoSection([{metadata:n.metadata??void 0}],S)),d&&f&&w.push(buildYieldSection(d,f,v));const b=o.some(M=>M.hbin!=null),F=o.some(M=>M.sbin!=null);b?w.push(buildBinSection(o,c,"hard",T,p?.stats.hardBinCounts)):F&&w.push(buildBinSection(o,s,"soft",T,p?.stats.softBinCounts)),w.push(buildRingSection(o,n,h,v)),w.push(buildQuadrantSection(o,n,h,v));const I=n.metadata?{waferMetadata:n.metadata}:void 0;w.push(buildTestSection(o,a,N,p?.stats,u,I)),w.push(buildFunctionalTestSection(o,a,p?.stats.functionalYield,u,I)),p&&B&&R&&L&&w.push(buildFindingsSectionWithFilter(p,B,E,R,L));let g=!0;for(const M of w)M&&(g||t.appendChild(dt()),g=!1,t.appendChild(M));t.appendChild(l("div",{height:"12px",flexShrink:"0"})),t.scrollTop=e}export function renderLotSummaryContent(t,i){const e=t.scrollTop;t.innerHTML="";const{lotSummary:n,items:o,hbinDefs:d,sbinDefs:f,testDefs:c,passBins:s=[1],ringCount:a=4,colorScheme:p,fallbackFormat:v,onFindingClick:h,activeFindingId:T=null,onWaferClick:N,findingsFilter:B,onFindingsFilterChange:E,onSaveText:R,dieListOptions:L}=i;t.appendChild(ut(`Lot Summary \u2014 ${n.stats.waferCount} wafer${n.stats.waferCount===1?"":"s"}`));const u=i.warnings??nt({lotStatsSummary:n});u.length&&t.appendChild(_(u));const $=q("Summary report",()=>{J(wt({items:o.map((g,M)=>({label:g?.label??`W${M+1}`,wafer:g?.wafer,dies:g?.dies,statsSummary:g?.statsSummary})),hbinDefs:d,sbinDefs:f,testDefs:c,passBins:s,ringCount:a}))}),k=[],C=[],r=[],m=new WeakMap;for(let g=0;g<o.length;g++){const M=o[g];if(!M){C.push([]);continue}M.wafer&&k.push(M.wafer);const V=M.dies??[],ht=M.label??`W${g+1}`;for(const gt of V)m.set(gt,ht);C.push(V),r.push(...V)}const D=L?.enabled??!0?q("View die list",()=>{r.length&&mt(t,r,c,`Die list \u2014 ${r.length} dies across ${o.length} wafer${o.length===1?"":"s"}`,et(o.filter(g=>!!g).map(g=>({metadata:g.wafer?.metadata}))),o.find(g=>g?.metadataFields?.length)?.metadataFields,L,R,{label:"Wafer",get:g=>m.get(g)})}):null,w=ft($,D);w&&t.appendChild(w);const x=r.some(g=>g.hbin!=null),S=r.some(g=>g.sbin!=null),b=o.map(g=>g?.statsSummary).filter(g=>!!g),F=[buildLotOverviewSection(n),buildMetadataInfoSection(o.filter(g=>g!==null).map(g=>({metadata:g.wafer?.metadata}))),buildPerWaferYieldSection(n,o,N),x?buildLotBinSection(r,d,"hard",p):S?buildLotBinSection(r,f,"soft",p):null,buildLotRingSection(C,k,a,s),buildLotQuadrantSection(C,k,a,s),c?.length?buildLotTestSection(r,c,v,b,R):null,c?.length?buildLotFunctionalSection(r,c,b,R):null];h&&B&&E&&F.push(buildFindingsSectionWithFilter(n,h,T,B,E));let I=!0;for(const g of F)g&&(I||t.appendChild(dt()),I=!1,t.appendChild(g));t.appendChild(l("div",{height:"12px",flexShrink:"0"})),t.scrollTop=e}
1
+ import{isParametricTest as xt}from"../renderer/buildWaferMap.js";import{buildRingRegions as K,buildQuadrantRegions as Z,buildRegionYieldData as bt}from"../stats/regions.js";import{computeFunctionalYield as vt}from"../stats/analyzeWaferMap.js";import{openHtmlReport as J}from"../stats/renderFindingsReport.js";import{renderSummaryReportHtml as wt,renderLotSummaryReportHtml as yt}from"../stats/renderSummaryReport.js";import{buildFindingsNarrative as Ct}from"../stats/findingsNarrative.js";import{filterFindings as St}from"../stats/filterFindings.js";import{buildFacetTable as kt,prettyKey as tt}from"../stats/facets.js";import{commonMetadata as et}from"../stats/facets.js";import{resolveMetadataColumns as Bt}from"../stats/metadataColumns.js";import{getColorScheme as $t}from"../renderer/colorSchemes.js";import{buildWarningsBanner as _,collectWarnings as nt}from"./warnings.js";export{_ as buildWarningsBanner};import{fmt as it,fmtAggregationMethod as Lt,plainBinTerms as P}from"../renderer/fmt.js";import{getUniqueTestNumbers as Dt}from"../renderer/buildView.js";import{quantile as Q}from"../stats/math.js";import{makeLabeledSelect as ot}from"./charts/chartShell.js";import{CLR as v,sevColor as G,openModal as at,saveTextFile as rt}from"./toolbar.js";import{buildDieListSection as Rt}from"./dieList.js";import{medianOfSorted as Ft}from"../core/utils.js";import{metadataDisplayValue as Tt}from"../core/metadata.js";const Et=v.panelBg,Y=`1px solid ${v.menuBorder}`,Nt="12px",I=v.label,z=v.value,st="10px";function l(t,i,n,e=document){const o=e.createElement(t);return i&&Object.assign(o.style,i),n!==void 0&&(o.textContent=n),o}function H(t){return l("div",{fontSize:st,fontWeight:"700",letterSpacing:"0.06em",textTransform:"uppercase",color:I,marginBottom:"6px"},t)}function lt(t,i=!0,n){const e=l("div"),o=document.createElement("button");o.type="button",Object.assign(o.style,{display:"flex",alignItems:"center",gap:"4px",width:"100%",background:"none",border:"none",cursor:"pointer",padding:"0 0 6px",textAlign:"left"});const d=l("span",{fontSize:"9px",color:I,transition:"transform 0.15s",transform:i?"rotate(90deg)":"rotate(0deg)",display:"inline-block",lineHeight:"1",marginRight:"1px"},"\u25B6"),f=l("span",{fontSize:st,fontWeight:"700",letterSpacing:"0.06em",textTransform:"uppercase",color:I,flex:"1"},t);if(o.setAttribute("aria-expanded",i?"true":"false"),o.appendChild(d),o.appendChild(f),n){const a=l("span",{fontSize:"9px",fontWeight:"700",background:v.warnBg,color:v.warnText,borderRadius:"10px",padding:"1px 5px"},n);o.appendChild(a)}const c=l("div");c.style.display=i?"block":"none";let s=i;return o.addEventListener("click",()=>{s=!s,c.style.display=s?"block":"none",d.style.transform=s?"rotate(90deg)":"rotate(0deg)",o.setAttribute("aria-expanded",s?"true":"false")}),e.appendChild(o),e.appendChild(c),{outer:e,content:c}}function dt(){return l("div",{height:"1px",background:v.separator,margin:`${Nt} 0`,flexShrink:"0"})}function U(t,i,n=v.barFill,e,o,d){const f=l("div",{marginBottom:"5px"}),c=l("div",{display:"flex",justifyContent:"space-between",fontSize:"11px",color:z,marginBottom:"2px"}),s=l("span",{},t),a=l("span",{fontWeight:"600"},`${i.toFixed(1)}%`);c.appendChild(s),c.appendChild(a);const p=e!==void 0?e:Math.min(100,Math.max(0,i)),h=d?v.barFillMuted:n,g=l("div",{position:"relative",height:"9px",background:v.bgActive,borderRadius:"4px",overflow:"hidden"}),T=l("div",{height:"100%",width:`${p}%`,background:h,borderRadius:"4px",transition:"width 0.3s ease"});if(g.appendChild(T),o!==void 0){const N=l("div",{position:"absolute",top:"0",bottom:"0",left:`${o}%`,width:"1px",background:v.infoText,opacity:"0.5"});g.appendChild(N)}return f.appendChild(c),f.appendChild(g),f}function O(t,i,n){const e=l("div",{background:v.menuBg,border:Y,borderRadius:"6px",padding:"8px 10px",textAlign:"center",flex:"1"}),o=l("div",{fontSize:"20px",fontWeight:"700",color:z,lineHeight:"1.2"},t),d=l("div",{fontSize:"10px",color:I,marginTop:"2px"},i);return e.appendChild(o),e.appendChild(d),n&&e.appendChild(l("div",{fontSize:"9px",color:I,opacity:"0.85"},n)),e}function Mt(t,i){const n=l("div",{display:"flex",justifyContent:"space-between",fontSize:"11px",gap:"8px",marginBottom:"3px"}),e=l("span",{color:I,flexShrink:"0"},t),o=l("span",{color:z,textAlign:"right",fontWeight:"500",wordBreak:"break-all"},i);return n.appendChild(e),n.appendChild(o),n}function Wt(t,i){const n=l("div",{display:"flex",fontSize:"11px",gap:"6px",marginBottom:"3px"}),e=l("span",{color:I,flexShrink:"0"},`${t}:`),o=l("span",{color:z,fontWeight:"500",wordBreak:"break-all"},i);return n.appendChild(e),n.appendChild(o),n}export function metadataEntries(t){const i=[];for(const[n,e]of Object.entries(t)){const o=Tt(e);o!==void 0&&i.push([n,o])}return i}export function buildFacetSummaryChips(t,i=3){if(!t.length)return null;const n=l("div",{display:"flex",flexWrap:"wrap",alignItems:"baseline",gap:"4px 10px",fontSize:"11px"});for(const e of t){const o=l("span",{whiteSpace:"nowrap"}),d=l("span",{color:I},`${tt(e.key)}: `);o.appendChild(d);const f=e.values.slice(0,i),c=e.values.length-f.length;let s=f.map(a=>a.value).join(", ");c>0&&(s+=` +${c} more`),o.appendChild(document.createTextNode(s)),n.appendChild(o)}return n}export function buildMetadataStripRow(t,i,n){const e=kt(t,n),o=buildFacetSummaryChips(e);if(!o&&!i)return null;const d=l("div",{display:"flex",flexWrap:"wrap",alignItems:"baseline",gap:"4px 14px"});if(i){const f=l("span",{fontWeight:"500",whiteSpace:"nowrap"},i.aggrMethod?`${i.lotSize} wafers stacked \xB7 ${i.aggrMethod}`:`${i.lotSize} wafers stacked`);d.appendChild(f)}return o&&d.appendChild(o),d}const It={display:"flex",flexDirection:"column",gap:"6px",background:v.menuBg,border:`1px solid ${v.menuBorder}`,borderRadius:"6px",padding:"6px 10px",boxShadow:"0 1px 4px rgba(0,0,0,0.10)",fontSize:"12px",lineHeight:"1",boxSizing:"border-box",width:"100%",minWidth:"0"};export function buildMetadataStripBox(t,i,n){const e=buildMetadataStripRow(t,i,n);if(!e)return null;const o=l("div",It);return o.appendChild(e),o}export function buildCompactMetadataRows(t){const i=metadataEntries(t);if(!i.length)return null;const n=l("div");for(const[e,o]of i)n.appendChild(Wt(tt(e),o));return n}export function buildMetadataInfoSection(t,i){const n=buildMetadataStripRow(t,i,{facetableOnly:t.length>1});if(!n)return null;const e=l("div");return e.appendChild(H("Wafer Info")),e.appendChild(n),e}export function buildYieldSection(t,i,n=[1]){const e=l("div");e.appendChild(H("Summary"));const o=l("div",{display:"flex",gap:"6px",marginBottom:"8px"});if(o.appendChild(O(String(t.totalDies),"Total dies")),t.partialDies>0&&o.appendChild(O(String(t.partialDies),"Partial")),t.yieldPercent!==null){const d=n.length===1?`bin ${n[0]}`:`bins ${n.join(", ")}`;o.appendChild(O(`${t.yieldPercent.toFixed(1)}%`,"Yield",`pass: ${d}`))}return e.appendChild(o),t.edgeExcludedDies>0&&e.appendChild(Mt("Edge excluded (outer zone)",String(t.edgeExcludedDies))),e}export function buildBinSection(t,i,n,e,o){const d=new Map;if(o)for(const[p,h]of Object.entries(o))d.set(Number(p),h);else for(const p of t){if(p.partial||p.edgeExcluded)continue;const h=n==="hard"?p.hbin:p.sbin;h!=null&&d.set(h,(d.get(h)??0)+1)}if(!d.size)return null;const f=[...d.values()].reduce((p,h)=>p+h,0),c=i?new Map(i.map(p=>[p.bin,p])):null,s=[...d.entries()].sort((p,h)=>p[0]-h[0]),a=l("div");a.appendChild(H(n==="hard"?"Hard Bin Breakdown":"Soft Bin Breakdown"));for(const[p,h]of s){const g=c?.get(p),T=g?.name?`Bin ${p} \xB7 ${g.name}`:`Bin ${p}`,N=h/f*100,B=$t(e),E=(e==="custom"?g?.color:void 0)??B.forBin(p);a.appendChild(U(`${T} (${h})`,N,E))}return a}export function buildLotBinSection(t,i,n,e){return buildBinSection(t,i,n,e)}function j(t,i,n,e,o,d){const f=bt(t,i,n,e,o);if(!f.length)return null;const c=l("div");c.appendChild(H(d));for(const{label:s,n:a,yieldPercent:p}of f)c.appendChild(U(`${s} (N=${a})`,p));return c}export function buildRingSection(t,i,n,e){return j([t],[i],n,e,K,"Ring Yield")}export function buildQuadrantSection(t,i,n,e){return j([t],[i],n,e,Z,"Quadrant Yield")}export function buildLotRingSection(t,i,n,e){return j(t,i,n,e,K,"Ring Yield")}export function buildLotQuadrantSection(t,i,n,e){return j(t,i,n,e,Z,"Quadrant Yield")}export function buildLotOverviewSection(t){const i=l("div");i.appendChild(H("Lot Summary"));const n=l("div",{display:"flex",gap:"6px",marginBottom:"8px"});n.appendChild(O(String(t.stats.waferCount),"Wafers"));const e=t.perWafer.map(o=>o.summary.stats.yieldPercent).filter(o=>o!==null);if(e.length){const o=e.reduce((d,f)=>d+f,0)/e.length;n.appendChild(O(`${o.toFixed(1)}%`,"Mean wafer yield"))}return i.appendChild(n),i}export function buildPerWaferYieldSection(t,i,n){const e=t.perWafer.map(a=>({waferIndex:a.waferIndex,label:(i[a.waferIndex]?.label??`W${a.waferIndex+1}`).replace(/\s*·\s*\d+(\.\d+)?%$/,""),yieldPct:a.summary.stats.yieldPercent})).filter(a=>a.yieldPct!==null);if(!e.length)return null;const o=Math.min(...e.map(a=>a.yieldPct)),d=Math.max(...e.map(a=>a.yieldPct)),f=o===d?"":` (${o.toFixed(1)}\u2013${d.toFixed(1)}%)`,c=Ft([...e.map(a=>a.yieldPct)].sort((a,p)=>a-p)),s=l("div");s.appendChild(H("Wafer Yield"+f));for(const{waferIndex:a,label:p,yieldPct:h}of e){const g=U(p,h,void 0,void 0,c,h<c);n&&(g.style.cursor="pointer",g.style.borderRadius="4px",g.style.padding="2px 3px",g.style.marginLeft="-3px",g.style.marginRight="-3px",g.setAttribute("role","button"),g.setAttribute("aria-label",`${p}, ${h.toFixed(1)}% yield \u2014 view wafer`),g.tabIndex=0,g.addEventListener("mouseenter",()=>{g.style.background=v.bgHover}),g.addEventListener("mouseleave",()=>{g.style.background=""}),g.addEventListener("focus",()=>{g.style.background=v.bgHover}),g.addEventListener("blur",()=>{g.style.background=""}),g.addEventListener("click",()=>n(a)),g.addEventListener("keydown",T=>{(T.key==="Enter"||T.key===" ")&&(T.preventDefault(),n(a))})),s.appendChild(g)}return s}function zt(t){const i=[...t].sort((o,d)=>o-d),n=t.reduce((o,d)=>o+d,0)/t.length,e=t.reduce((o,d)=>o+(d-n)**2,0)/t.length;return{min:i[0],max:i[i.length-1],mean:n,count:t.length,stddev:Math.sqrt(e),median:Q(i,.5),q1:Q(i,.25),q3:Q(i,.75)}}function pt(t,i){if(!t)return[];const n=t.waferMetadata??(t.perWaferMetadata?et(t.perWaferMetadata.map(e=>({metadata:e}))):void 0);return!n||!Object.keys(n).length?[]:Bt({waferMetadata:n,waferPlacement:"csv",reservedLabels:i}).columns}export function csvField(t){return/[",\n]/.test(t)?`"${t.replace(/"/g,'""')}"`:t}export function buildTestSection(t,i,n,e,o,d){const f=t.filter(r=>!r.partial&&!r.edgeExcluded),c=new Map((e?.perTestStats??[]).map(r=>[r.testNumber,r])),s=new Map((e?.testSpecYield??[]).map(r=>[r.testNumber,r]));let a;if(i?.length?a=i.filter(xt).map(r=>({testNumber:r.testNumber,name:r.name,unit:r.unit,limitLow:r.limitLow,limitHigh:r.limitHigh})):a=Dt(f).map(m=>({testNumber:m,name:`Test ${m}`})),!a.length)return null;const p=a.filter(r=>f.some(m=>{const D=m.testValues?.[r.testNumber];return D!==void 0&&isFinite(D)}));if(!p.length)return null;const h=p.some(r=>r.limitLow!==void 0||r.limitHigh!==void 0),g=[];for(const r of p){const m=c.get(r.testNumber),D=m&&m.stddev!==void 0&&m.median!==void 0&&m.q1!==void 0&&m.q3!==void 0;let y;if(D){const b=m;y={testNumber:r.testNumber,min:b.min,max:b.max,mean:b.mean,count:b.count,stddev:b.stddev,median:b.median,q1:b.q1,q3:b.q3}}else{const b=f.map(F=>F.testValues?.[r.testNumber]).filter(F=>F!==void 0&&isFinite(F));if(!b.length)continue;y={testNumber:r.testNumber,...zt(b)}}let x=null,S=0;if(r.limitLow!==void 0||r.limitHigh!==void 0){const b=s.get(r.testNumber);if(b)({yieldPercent:x,totalDies:S}=b);else{const F=f.map(w=>w.testValues?.[r.testNumber]).filter(w=>w!==void 0&&isFinite(w)),W=F.filter(w=>r.limitLow!==void 0&&w<r.limitLow||r.limitHigh!==void 0&&w>r.limitHigh).length;S=F.length,x=F.length>0?(F.length-W)/F.length*100:null}}g.push({entry:r,stats:y,specYieldPct:x,specN:S})}if(!g.length)return null;const T=l("div"),N=l("div",{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:"6px"}),B=H(d?.populationLabel?`Test Values (${g.length}) \u2014 ${d.populationLabel}`:`Test Values (${g.length})`);B.style.marginBottom="0",N.appendChild(B);const E=document.createElement("button");E.type="button",E.textContent="Export CSV",Object.assign(E.style,{background:"none",border:Y,borderRadius:"4px",cursor:"pointer",fontSize:"10px",color:v.iconHover,padding:"2px 7px"}),E.addEventListener("click",()=>{const r=["Test","Unit","N","Min","Q1","Median","Mean","Q3","Max","StdDev"];h&&r.push("LSL","USL","Spec Yield %","Spec Yield N");const m=pt(d,r),y=[[...m.map(S=>S.label),...r].map(csvField).join(",")],x=S=>it(S,void 0,"engineering");for(const{entry:S,stats:b,specYieldPct:F,specN:W}of g){const w=[...m.map(M=>M.constant??""),S.name,S.unit??"",String(b.count),x(b.min),x(b.q1),x(b.median),x(b.mean),x(b.q3),x(b.max),x(b.stddev)];h&&w.push(S.limitLow!==void 0?x(S.limitLow):"",S.limitHigh!==void 0?x(S.limitHigh):"",F!==null?F.toFixed(1):"",S.limitLow!==void 0||S.limitHigh!==void 0?String(W):""),y.push(w.map(csvField).join(","))}rt(y.join(`
2
+ `),"test-values.csv","text/csv",o)}),N.appendChild(E),T.appendChild(N);const R=l("table",{width:"100%",borderCollapse:"collapse",fontSize:"11px"}),L=document.createElement("thead"),u=document.createElement("tr"),$=["Test","N","Min","Q1","Median","Mean","Q3","Max","StdDev"];h&&$.push("LSL","USL","Spec yield");for(const r of $){const m=l("th",{textAlign:r==="Test"?"left":"right",fontWeight:"700",fontSize:"10px",letterSpacing:"0.03em",textTransform:"uppercase",color:I,padding:"3px 8px 5px",borderBottom:`1px solid ${v.menuBorder}`,whiteSpace:"nowrap"},r);u.appendChild(m)}L.appendChild(u),R.appendChild(L);const k=document.createElement("tbody");for(const{entry:r,stats:m,specYieldPct:D,specN:y}of g){const x=F=>it(F,r.unit,n),S=document.createElement("tr"),b=(F,W="right")=>{const w=l("td",{textAlign:W,padding:"4px 8px",borderBottom:`1px solid ${v.menuBorder}`,color:z,whiteSpace:"nowrap"},F);S.appendChild(w)};b(r.name,"left"),b(`${m.count}`),b(x(m.min)),b(x(m.q1)),b(x(m.median)),b(x(m.mean)),b(x(m.q3)),b(x(m.max)),b(x(m.stddev)),h&&(b(r.limitLow!==void 0?x(r.limitLow):"\u2014"),b(r.limitHigh!==void 0?x(r.limitHigh):"\u2014"),b(D!==null?`${D.toFixed(1)}% (N=${y})`:"\u2014")),k.appendChild(S)}R.appendChild(k);const C=l("div",{overflowX:"auto"});return C.appendChild(R),T.appendChild(C),T}export function buildFunctionalTestSection(t,i,n,e,o){const d=n??vt(t,i);if(!d?.length)return null;const f=l("div"),c=l("div",{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:"6px"}),s=H(o?.populationLabel?`Functional Tests (${d.length}) \u2014 ${o.populationLabel}`:`Functional Tests (${d.length})`);s.style.marginBottom="0",c.appendChild(s);const a=document.createElement("button");a.type="button",a.textContent="Export CSV",Object.assign(a.style,{background:"none",border:Y,borderRadius:"4px",cursor:"pointer",fontSize:"10px",color:v.iconHover,padding:"2px 7px"}),a.addEventListener("click",()=>{const B=["Test","N","Pass","Fail","Pass Rate %"],E=pt(o,B),R=[[...E.map(L=>L.label),...B].map(csvField).join(",")];for(const L of d)R.push([...E.map(u=>u.constant??""),L.label,String(L.totalDies),String(L.passDies),String(L.failDies),L.passRatePercent!==null?L.passRatePercent.toFixed(1):""].map(csvField).join(","));rt(R.join(`
3
+ `),"functional-tests.csv","text/csv",e)}),c.appendChild(a),f.appendChild(c);const p=l("table",{width:"100%",borderCollapse:"collapse",fontSize:"11px"}),h=document.createElement("thead"),g=document.createElement("tr");for(const B of["Test","N","Pass","Fail","Pass rate"])g.appendChild(l("th",{textAlign:B==="Test"?"left":"right",fontWeight:"700",fontSize:"10px",letterSpacing:"0.03em",textTransform:"uppercase",color:I,padding:"3px 8px 5px",borderBottom:`1px solid ${v.menuBorder}`,whiteSpace:"nowrap"},B));h.appendChild(g),p.appendChild(h);const T=document.createElement("tbody");for(const B of d){const E=document.createElement("tr"),R=(L,u="right")=>{E.appendChild(l("td",{textAlign:u,padding:"4px 8px",borderBottom:`1px solid ${v.menuBorder}`,color:z,whiteSpace:"nowrap"},L))};R(B.label,"left"),R(String(B.totalDies)),R(String(B.passDies)),R(String(B.failDies)),R(B.passRatePercent!==null?`${B.passRatePercent.toFixed(1)}% (N=${B.totalDies})`:"\u2014"),T.appendChild(E)}p.appendChild(T);const N=l("div",{overflowX:"auto"});return N.appendChild(p),f.appendChild(N),f}export function buildLotFunctionalSection(t,i,n,e){const o=n?.length?{perWaferMetadata:n.map(d=>d.wafer??{}),populationLabel:`${n.length} wafer${n.length===1?"":"s"} pooled`}:void 0;if(n?.length&&n.every(d=>d.stats.functionalYield!==void 0)){const d=new Map;for(const c of n)for(const s of c.stats.functionalYield??[]){const a=d.get(s.testNumber)??{label:s.label,passDies:0,failDies:0,totalDies:0};a.passDies+=s.passDies,a.failDies+=s.failDies,a.totalDies+=s.totalDies,d.set(s.testNumber,a)}const f=[...d.entries()].map(([c,s])=>({testNumber:c,...s,passRatePercent:s.totalDies>0?s.passDies/s.totalDies*100:null}));if(f.length)return buildFunctionalTestSection(t,i,f,e,o)}return buildFunctionalTestSection(t,i,void 0,e,o)}const A={unusual:0,notable:1,info:2};function X(t){return l("span",{display:"inline-block",width:"7px",height:"7px",borderRadius:"50%",background:G(t),flexShrink:"0"})}function ct(t,i){let n=t.summary;return i&&(n.startsWith(`${i} has `)?n=n.slice(i.length+5):n.startsWith(`${i} `)&&(n=n.slice(i.length+1))),n=P(n),n.length?n[0].toUpperCase()+n.slice(1):n}export function buildFindingsSection(t,i,n,e,o=!1){if(!t.length)return null;const d=t.some(u=>u.severity==="unusual"||u.severity==="notable"),f=d?t.filter(u=>u.severity!=="info").length.toString():void 0,{outer:c,content:s}=o?(()=>{const u=l("div");return{outer:u,content:u}})():lt(`Findings (${t.length})`,d,f);function a(u){return u.reduce(($,k)=>A[k.severity]<A[$]?k.severity:$,"info")}function p(u){const $=new Map;for(const C of u){const r=`${C.comparison.family}\0${C.comparison.left}`,m=$.get(r)??[];m.push(C),$.set(r,m)}const k=[...$.entries()].map(([C,r])=>{const m=[...r].sort((D,y)=>A[D.severity]-A[y.severity]);return{key:C,findings:m,worst:a(m)}});return k.sort((C,r)=>A[C.worst]-A[r.worst]),k}function h(u,$){const C={ring:"Ring",quadrant:"Quadrant",sector:"Sector","reticle-position":"Reticle","test-site":"Test site",cluster:"Cluster","edge-arc":"Edge arc",wafer:"Wafer"}[u]??u;return $.toLowerCase().startsWith(C.toLowerCase())?$:`${C}: ${$}`}const g=t.filter(u=>u.comparison.family==="spatial-pattern"),T=new Set([...g.flatMap(u=>u.relatedIds??[]),...t.flatMap(u=>u.absorbedIds??[])]),N=t.filter(u=>u.comparison.family!=="spatial-pattern"&&!T.has(u.id)),B=p(N);function E(u,$=!1,k){const C=e===u.id,r=document.createElement("button");return r.type="button",r.dataset.wmapFinding=u.id,r.textContent=ct(u,k),r.title=u.summary,r.setAttribute("aria-current",C?"true":"false"),Object.assign(r.style,{border:`1px solid ${v.menuBorder}`,borderLeft:`3px solid ${G(u.severity)}`,background:C?v.bgActive:v.menuBg,borderRadius:"6px",padding:$?"6px 10px":"8px 10px",textAlign:"left",fontSize:$?"10px":"11px",fontWeight:C?"600":"400",color:v.iconHover,cursor:"pointer",width:"100%",marginBottom:"4px"}),r.addEventListener("click",()=>n(u,r)),r}const R=P(Ct(t)??"");if(R){const u=l("div",{background:v.bgActive,borderRadius:"6px",padding:"8px 10px",marginBottom:"8px",display:"flex",gap:"8px",alignItems:"flex-start"}),$=l("span",{flex:"1",fontSize:"12px",lineHeight:"1.5",color:v.iconHover},R);u.appendChild($);const k=l("button",{flexShrink:"0",border:"none",background:"none",fontSize:"10px",color:v.icon,cursor:"pointer",padding:"0",lineHeight:"1.5",whiteSpace:"nowrap"},"Detail \u25B8");k.type="button",k.addEventListener("click",()=>{const C=at({title:"Findings Summary",onClose:()=>{},anchor:k}),r=l("p",{fontSize:"16px",lineHeight:"1.6",color:v.iconHover,padding:"20px 24px 16px",margin:"0",borderBottom:`1px solid ${v.menuBorder}`,flexShrink:"0"},R);C.contentWrap.appendChild(r);const m=l("div",{overflowY:"auto",padding:"16px 24px",flex:"1"}),D=(y,x)=>{const S=l("div",{display:"flex",alignItems:"center",gap:"7px",fontSize:"13px",fontWeight:"600",color:z,marginTop:"10px",marginBottom:"4px"});return S.appendChild(X(y)),S.appendChild(l("span",{},x)),S};for(const y of g){m.appendChild(D(y.severity,y.comparison.left)),m.appendChild(l("div",{fontSize:"13px",color:v.iconHover,padding:"3px 0 3px 15px",marginBottom:"2px"},P(y.summary)));const x=t.filter(S=>y.relatedIds?.includes(S.id));for(const S of x)m.appendChild(l("div",{fontSize:"12px",color:v.icon,padding:"2px 0 2px 23px",marginBottom:"2px"},P(S.summary)))}for(const y of B){const[x,S]=y.key.split("\0");m.appendChild(D(y.worst,h(x,S)));for(const b of y.findings)m.appendChild(l("div",{fontSize:"13px",color:v.iconHover,padding:"3px 0 3px 15px",marginBottom:"2px"},ct(b,S)))}C.contentWrap.appendChild(m),Object.assign(C.contentWrap.style,{flexDirection:"column",overflow:"hidden"})}),u.appendChild(k),s.appendChild(u)}let L=!0;for(const u of g){L||s.appendChild(l("div",{height:"1px",background:v.separator,margin:"4px 0"})),L=!1;const $=t.filter(D=>u.relatedIds?.includes(D.id)),k=$.length>0,C=l("div",{position:"relative",marginBottom:k?"2px":"4px"}),r=e===u.id,m=document.createElement("button");if(m.type="button",m.dataset.wmapFinding=u.id,Object.assign(m.style,{border:`1px solid ${v.menuBorder}`,borderLeft:`3px solid ${G(u.severity)}`,background:r?v.bgActive:v.menuBg,borderRadius:"6px",padding:"8px 32px 8px 10px",textAlign:"left",fontSize:"11px",fontWeight:r?"600":"500",color:v.iconHover,cursor:"pointer",width:"100%"}),m.textContent=P(u.summary),m.title=u.summary,m.setAttribute("aria-current",r?"true":"false"),m.addEventListener("click",()=>n(u,m)),C.appendChild(m),k){const D=l("div",{display:"none",paddingLeft:"12px",marginBottom:"4px"});for(const S of $)D.appendChild(E(S,!0));C.appendChild(D);let y=!1;const x=l("button",{position:"absolute",top:"50%",right:"8px",transform:"translateY(-50%)",border:"none",background:"none",fontSize:"10px",color:v.icon,cursor:"pointer",padding:"2px 4px",lineHeight:"1"},"\u25B8");x.type="button",x.title="Show supporting findings",x.setAttribute("aria-label","Show supporting findings"),x.setAttribute("aria-expanded","false"),x.addEventListener("click",S=>{S.stopPropagation(),y=!y,D.style.display=y?"block":"none",x.textContent=y?"\u25BE":"\u25B8";const b=y?"Hide supporting findings":"Show supporting findings";x.title=b,x.setAttribute("aria-label",b),x.setAttribute("aria-expanded",String(y))}),C.appendChild(x)}s.appendChild(C)}for(const u of B){L||s.appendChild(l("div",{height:"1px",background:v.separator,margin:"4px 0"})),L=!1;const[$,k]=u.key.split("\0"),C=l("div",{display:"flex",alignItems:"center",gap:"6px",fontSize:"11px",fontWeight:"600",color:z,margin:"2px 0 4px"});C.appendChild(X(u.worst)),C.appendChild(l("span",{},h($,k))),s.appendChild(C);for(const r of u.findings)s.appendChild(E(r,!1,k))}return c}const Ht=[{value:"",label:"All kinds"},{value:"yield",label:"Yield"},{value:"hardBin",label:"Hard bin"},{value:"softBin",label:"Soft bin"},{value:"test",label:"Test value"},{value:"functionalTest",label:"Functional test"},{value:"spatialPattern",label:"Spatial pattern"}],At=[{value:"",label:"All regions"},{value:"ring",label:"Ring"},{value:"quadrant",label:"Quadrant"},{value:"reticle-position",label:"Reticle position"},{value:"test-site",label:"Test site"},{value:"wafer",label:"Wafer"},{value:"sector",label:"Sector"},{value:"cluster",label:"Cluster"},{value:"edge-arc",label:"Edge arc"},{value:"spatial-pattern",label:"Spatial pattern"}],Pt=["unusual","notable","info"],Yt={unusual:"Unusual",notable:"Notable",info:"Info"};function Ot(t,i,n){const e=l("div",{display:"flex",flexWrap:"wrap",alignItems:"center",gap:"10px",marginBottom:"10px",paddingBottom:"10px",borderBottom:`1px solid ${v.separator}`}),o={unusual:0,notable:0,info:0};for(const s of t)o[s.severity]++;const d=Pt.filter(s=>o[s]>0),f=new Set(i.severity===void 0?d:Array.isArray(i.severity)?i.severity:[i.severity]),c=l("div",{display:"flex",gap:"4px",flexWrap:"wrap"});for(const s of d){const a=document.createElement("button");a.type="button";const p=()=>{const h=f.has(s);a.setAttribute("aria-pressed",h?"true":"false"),Object.assign(a.style,{display:"inline-flex",alignItems:"center",gap:"5px",border:`1px solid ${v.menuBorder}`,borderRadius:"10px",background:h?v.bgActive:"none",opacity:h?"1":"0.5",color:v.text,fontSize:"11px",padding:"2px 8px",cursor:"pointer"})};a.appendChild(X(s)),a.appendChild(l("span",{},`${Yt[s]} ${o[s]}`)),a.title=f.has(s)?"Click to hide these findings":"Click to show these findings",p(),a.addEventListener("click",()=>{f.has(s)?f.delete(s):f.add(s),i.severity=f.size===d.length?void 0:[...f],n()}),c.appendChild(a)}return e.appendChild(c),e.appendChild(ot("Kind:",Ht,i.kind??"",s=>{i.kind=s||void 0,n()},{maxWidth:"130px"})),e.appendChild(ot("Region:",At,i.family??"",s=>{i.family=s||void 0,n()},{maxWidth:"150px"})),e}export function buildFindingsSectionWithFilter(t,i,n,e,o){if(!t.findings.length)return null;const d=t.findings.some(h=>h.severity==="unusual"||h.severity==="notable"),f=d?t.findings.filter(h=>h.severity!=="info").length.toString():void 0,{outer:c,content:s}=lt(`Findings (${t.findings.length})`,d,f);s.appendChild(Ot(t.findings,e,o));const a=St(t,e);if(!a.length)return s.appendChild(l("div",{color:I,fontSize:"11px",textAlign:"center",padding:"16px 8px"},"No findings match the current filter.")),c;const p=buildFindingsSection(a,t,i,n,!0);return p&&s.appendChild(p),c}export function buildLotTestSection(t,i,n,e,o){const d=e?.length?{perWaferMetadata:e.map(c=>c.wafer??{}),populationLabel:`${e.length} wafer${e.length===1?"":"s"} pooled`}:void 0;let f;if(e?.length&&e.every(c=>c.stats.perTestStats!==void 0)){const c=new Map;for(const s of e)for(const a of s.stats.perTestStats??[]){const p=c.get(a.testNumber);p?(p.n+=a.count,p.sum+=a.mean*a.count,p.min=Math.min(p.min,a.min),p.max=Math.max(p.max,a.max)):c.set(a.testNumber,{n:a.count,sum:a.mean*a.count,min:a.min,max:a.max})}f={perTestStats:[...c.entries()].map(([s,a])=>({testNumber:s,count:a.n,min:a.min,max:a.max,mean:a.sum/a.n}))}}if(e?.length&&e.every(c=>c.stats.testSpecYield!==void 0)){const c=new Map;for(const s of e)for(const a of s.stats.testSpecYield??[]){const p=c.get(a.testNumber);p?(p.passDies+=a.passDies,p.totalDies+=a.totalDies):c.set(a.testNumber,{passDies:a.passDies,totalDies:a.totalDies})}f={...f,testSpecYield:[...c.entries()].map(([s,a])=>({testNumber:s,totalDies:a.totalDies,yieldPercent:a.totalDies>0?a.passDies/a.totalDies*100:null}))}}return buildTestSection(t,i,n,f,o,d)}export function createSummaryPanelEl(t,i=document){const n=t==="top"||t==="bottom",e=l("div",{background:Et,border:Y,borderRadius:"6px",padding:"12px",overflowY:n?"hidden":"auto",overflowX:n?"auto":"hidden",flexShrink:"0",boxSizing:"border-box",fontFamily:"system-ui, sans-serif",fontSize:"12px",boxShadow:"0 1px 4px rgba(0,0,0,0.08)"},void 0,i);return n?(e.style.height="180px",e.style.width="100%"):(e.style.width="260px",e.style.maxHeight="100%"),e}export function wrapWithSummaryPanel(t,i,n){const e=n==="top"||n==="bottom",o=l("div",{display:"flex",flexDirection:e?"column":"row",gap:"8px",width:"100%",height:"100%",flex:"1 1 0",minHeight:"0",boxSizing:"border-box",alignItems:e?"flex-start":"stretch"});return t.style.flex="1 1 0",t.style.minWidth="0",t.style.minHeight="0",n==="right"||n==="bottom"?(o.appendChild(t),o.appendChild(i)):(o.appendChild(i),o.appendChild(t)),o}function ut(t){return l("div",{fontSize:"13px",fontWeight:"700",color:z,marginBottom:"10px"},t)}function q(t,i){const n=l("button",{background:"none",border:Y,borderRadius:"4px",cursor:"pointer",fontSize:"10px",color:v.iconHover,padding:"2px 7px"},t);return n.type="button",n.addEventListener("click",i),n}function ft(...t){const i=t.filter(e=>!!e);if(!i.length)return null;const n=l("div",{display:"flex",flexWrap:"wrap",gap:"6px",marginBottom:"10px"});for(const e of i)n.appendChild(e);return n}function mt(t,i,n,e,o,d,f,c,s){const a=t.ownerDocument,p=at({title:"Die list",onClose:()=>{},anchor:t,ownerDocument:a});p.contentWrap.style.padding="14px 16px";const h=Rt(i,n,{...f,title:e,onSaveText:c,waferMetadata:o,metadataFields:d,extraColumn:s,ownerDocument:a});h&&p.contentWrap.appendChild(h)}export function renderWaferSummaryContent(t,i){const n=t.scrollTop;t.innerHTML="";const{wafer:e,dies:o,yieldSummary:d,dataCoverage:f,hbinDefs:c,sbinDefs:s,testDefs:a,statsSummary:p,passBins:h=[1],ringCount:g=4,colorScheme:T,fallbackFormat:N,onFindingClick:B,activeFindingId:E=null,findingsFilter:R,onFindingsFilterChange:L,onSaveText:u,metadataFields:$,dieListOptions:k}=i;t.appendChild(ut("Wafer Summary"));const C=i.warnings??nt({statsSummary:p});C.length&&t.appendChild(_(C,t.ownerDocument));const r=d&&f?q("Summary report",()=>{J(wt({wafer:e,dies:o,yieldSummary:d,dataCoverage:f,hbinDefs:c,sbinDefs:s,testDefs:a,statsSummary:p,passBins:h,ringCount:g}))}):null,m=(k?.enabled??!0)&&o.length?q("View die list",()=>{mt(t,o,a,`Die list \u2014 ${o.length} dies`,e.metadata,$,k,u)}):null,D=ft(r,m);D&&t.appendChild(D);const y=[],x=p?.stats.isLotStack?p.stats:void 0,S=x?.lotSize!==void 0?{lotSize:x.lotSize,aggrMethod:Lt(x.aggregationMethod)}:void 0;y.push(buildMetadataInfoSection([{metadata:e.metadata??void 0}],S)),d&&f&&y.push(buildYieldSection(d,f,h));const b=o.some(M=>M.hbin!=null),F=o.some(M=>M.sbin!=null);b?y.push(buildBinSection(o,c,"hard",T,p?.stats.hardBinCounts)):F&&y.push(buildBinSection(o,s,"soft",T,p?.stats.softBinCounts)),y.push(buildRingSection(o,e,g,h)),y.push(buildQuadrantSection(o,e,g,h));const W=e.metadata?{waferMetadata:e.metadata}:void 0;y.push(buildTestSection(o,a,N,p?.stats,u,W)),y.push(buildFunctionalTestSection(o,a,p?.stats.functionalYield,u,W)),p&&B&&R&&L&&y.push(buildFindingsSectionWithFilter(p,B,E,R,L));let w=!0;for(const M of y)M&&(w||t.appendChild(dt()),w=!1,t.appendChild(M));t.appendChild(l("div",{height:"12px",flexShrink:"0"})),t.scrollTop=n}export function renderLotSummaryContent(t,i){const n=t.scrollTop;t.innerHTML="";const{lotSummary:e,items:o,hbinDefs:d,sbinDefs:f,testDefs:c,passBins:s=[1],ringCount:a=4,colorScheme:p,fallbackFormat:h,onFindingClick:g,activeFindingId:T=null,onWaferClick:N,findingsFilter:B,onFindingsFilterChange:E,onSaveText:R,dieListOptions:L}=i;t.appendChild(ut(`Lot Summary \u2014 ${e.stats.waferCount} wafer${e.stats.waferCount===1?"":"s"}`));const u=i.warnings??nt({lotStatsSummary:e});u.length&&t.appendChild(_(u,t.ownerDocument));const $=q("Summary report",()=>{J(yt({items:o.map((w,M)=>({label:w?.label??`W${M+1}`,wafer:w?.wafer,dies:w?.dies,statsSummary:w?.statsSummary})),hbinDefs:d,sbinDefs:f,testDefs:c,passBins:s,ringCount:a}))}),k=[],C=[],r=[],m=new WeakMap;for(let w=0;w<o.length;w++){const M=o[w];if(!M){C.push([]);continue}M.wafer&&k.push(M.wafer);const V=M.dies??[],ht=M.label??`W${w+1}`;for(const gt of V)m.set(gt,ht);C.push(V),r.push(...V)}const D=L?.enabled??!0?q("View die list",()=>{r.length&&mt(t,r,c,`Die list \u2014 ${r.length} dies across ${o.length} wafer${o.length===1?"":"s"}`,et(o.filter(w=>!!w).map(w=>({metadata:w.wafer?.metadata}))),o.find(w=>w?.metadataFields?.length)?.metadataFields,L,R,{label:"Wafer",get:w=>m.get(w)})}):null,y=ft($,D);y&&t.appendChild(y);const x=r.some(w=>w.hbin!=null),S=r.some(w=>w.sbin!=null),b=o.map(w=>w?.statsSummary).filter(w=>!!w),F=[buildLotOverviewSection(e),buildPerWaferYieldSection(e,o,N),x?buildLotBinSection(r,d,"hard",p):S?buildLotBinSection(r,f,"soft",p):null,buildLotRingSection(C,k,a,s),buildLotQuadrantSection(C,k,a,s),c?.length?buildLotTestSection(r,c,h,b,R):null,c?.length?buildLotFunctionalSection(r,c,b,R):null];g&&B&&E&&F.push(buildFindingsSectionWithFilter(e,g,T,B,E));let W=!0;for(const w of F)w&&(W||t.appendChild(dt()),W=!1,t.appendChild(w));t.appendChild(l("div",{height:"12px",flexShrink:"0"})),t.scrollTop=n}
@@ -5,6 +5,15 @@ export interface ToCanvasOptions {
5
5
  padding?: number;
6
6
  /** Draw a continuous colorbar (value modes) or bin legend (bin modes). Default true. */
7
7
  showColorbar?: boolean;
8
+ /**
9
+ * Draw a diagonal hatch on dies whose bin is not a pass bin (see
10
+ * `ViewRect.binFail`). Default false — opt in from the Colour scheme menu.
11
+ *
12
+ * Gives pass/fail a second channel that is not hue, so the map keeps working
13
+ * for a viewer with a colour-vision deficiency and when printed in
14
+ * greyscale. Composes with any colour scheme; it changes no colours itself.
15
+ */
16
+ markFailingDies?: boolean;
8
17
  /** Width in CSS pixels of the colorbar strip. Default 16. */
9
18
  colorbarWidth?: number;
10
19
  /** Canvas background colour. Default '#f5f5f5'. */
@@ -101,6 +110,8 @@ export declare const BIN_LEGEND_W = 110;
101
110
  export declare const BIN_LEGEND_W_COMPACT = 64;
102
111
  export declare const BIN_LEGEND_ADAPT_COMPACT = 280;
103
112
  export declare const BIN_LEGEND_ADAPT_FLOATING = 180;
113
+ export declare const BIN_LEGEND_MIN_CANVAS_W = 150;
114
+ export declare const BIN_LEGEND_MIN_CANVAS_H = 120;
104
115
  export declare function toCanvas(canvas: HTMLCanvasElement, view: View, options?: ToCanvasOptions): ToCanvasResult;
105
116
  export { fmt, fmtColorbarAxis } from '../renderer/fmt.js';
106
117
  //# sourceMappingURL=toCanvas.d.ts.map
@@ -1 +1 @@
1
- import{findTestDef as ue,buildMapTitle as pe}from"../renderer/buildView.js";import{affineInvert as Fe,affineVector as xe}from"../core/transforms.js";import{compareNatural as Oe}from"../core/utils.js";import{getColorScheme as ye}from"../renderer/colorSchemes.js";import{SPEC_PASS_FILL as Me,SPEC_FAIL_LOW as Ie,SPEC_FAIL_HIGH as be,contrastTextColor as Ne}from"../renderer/colorMap.js";import{fmt as Se,fmtColorbarAxis as Ee}from"../renderer/fmt.js";import{resolveCanvasTheme as Ye}from"./canvasTheme.js";function Te(h,e,F,p,c){h.moveTo(e,F+c*p),h.lineTo(e-p*.9,F-c*p*.6),h.lineTo(e+p*.9,F-c*p*.6),h.closePath()}const Xe=new Set(["value","stackedValues","stackedBins","stackedSoftBins"]),we=new Set(["hardBin","softBin","metadata"]),v="10px system-ui, sans-serif",He="600 12px system-ui, sans-serif",$t="11px system-ui, sans-serif",$e="600 11px system-ui, sans-serif",Gt=128,Ge="10px system-ui, sans-serif",ke=4,E=17,w=11;export const BIN_LEGEND_W=110,BIN_LEGEND_W_COMPACT=64,BIN_LEGEND_ADAPT_COMPACT=280,BIN_LEGEND_ADAPT_FLOATING=180;const Ut=28,xt=5,Vt=8;export function toCanvas(h,e,F={}){const p=Ye(h.parentElement??h),{padding:c=16,showColorbar:U=!0,colorbarWidth:S=16,background:lt=p.background,legendPosition:gt="default",legendOffset:j,showAxes:mt=!1,diePitchMm:Tt,viewport:H,activeBin:Q,fallbackFormat:O,topClearance:Y=0,minRightReserve:kt,hbinDefs:Mt,sbinDefs:it,metadataFields:Lt,showTitle:tt=!0}=F,ut=U&&Xe.has(e.plotMode)&&e.passFailDisplay==="off",bt=U&&we.has(e.plotMode),pt=U&&e.plotMode==="value"&&(e.passFailDisplay==="spec"&&e.specCounts!==void 0||e.passFailDisplay==="test"&&e.passFailCounts!==void 0),at=bt||pt,Rt=bt&&e.plotMode!=="metadata"&&e.binCounts?[...e.binCounts.entries()].sort(([o],[a])=>o-a):[],At=bt&&e.plotMode==="metadata"&&e.metadataCounts?[...e.metadataColorMap?.keys()??[...e.metadataCounts.keys()].sort(Oe)].map(o=>[o,e.metadataCounts.get(o)??0]):[],J=[];if(pt&&e.passFailDisplay==="test"){const o=e.passFailCounts;J.push({key:-1,color:Me,label:"Pass",compactLabel:"Pass",count:o.pass,tooltip:`Pass \xB7 ${o.pass} dies`}),J.push({key:-2,color:be,label:"Fail",compactLabel:"Fail",count:o.fail,tooltip:`Fail \xB7 ${o.fail} dies`})}else if(pt){const o=e.specCounts,a=ue(e.testDefs,e.activeTest);J.push({key:-1,color:Me,label:"Pass",compactLabel:"Pass",count:o.pass,tooltip:`Pass \xB7 ${o.pass} dies`}),a?.limitHigh!==void 0&&J.push({key:-2,color:be,label:"Fail high",compactLabel:"Fail hi",count:o.failHigh,tooltip:`Fail high \xB7 ${o.failHigh} dies`}),a?.limitLow!==void 0&&J.push({key:-3,color:Ie,label:"Fail low",compactLabel:"Fail lo",count:o.failLow,tooltip:`Fail low \xB7 ${o.failLow} dies`})}const yt=pt?J.length:e.plotMode==="metadata"?At.length:Rt.length,$=(h.ownerDocument.defaultView??window).devicePixelRatio??1,I=Math.floor(h.clientWidth||h.width),V=Math.floor(h.clientHeight||h.height);if(I<=0||V<=0)return{hitTarget:{getDieAtPoint:()=>null},viewport:{originX:0,originY:0,ppm:1,snapDist:1},binLegendRows:[]};const K=gt!=="default"?gt:I<BIN_LEGEND_ADAPT_FLOATING?"floating":I<BIN_LEGEND_ADAPT_COMPACT?"compact":"default",y=K==="default"||K==="compact",G=K==="left",ct=K==="bottom",Ct=K==="top",Pt=K==="floating",Wt=K==="compact"||K==="floating"?BIN_LEGEND_W_COMPACT:BIN_LEGEND_W,oe=Math.floor((V-2*c)/E),ne=yt>oe?oe-1:yt,se=at&&ct?ne*E:0,zt=at&&Ct?ne*E:0,Kt=ut?S+28:at&&y?Wt:0,Jt=at&&G?Wt:0,Zt=mt?32:0,Bt=mt?36:0,Le=kt!=null?Math.max(Kt,kt):Kt,Re=I-2*c-Le-Bt-Jt,le=V-2*c-Zt-se-zt;h.width=Math.round(I*$),h.height=Math.round(V*$);const t=h.getContext("2d");t.scale($,$),t.fillStyle=lt,t.fillRect(0,0,I,V);const q=e.hoverPoints;if(!q.length)return{hitTarget:{getDieAtPoint:()=>null},viewport:{originX:0,originY:0,ppm:1,snapDist:1},binLegendRows:[]};const _t=e.rectangles[0],jt=_t?_t.width/2:0,Qt=_t?_t.height/2:0,Dt=e.dieBounds,ie=(Dt?.minX??0)-jt,Ae=(Dt?.maxX??0)+jt,Ce=(Dt?.minY??0)-Qt,ae=(Dt?.maxY??0)+Qt,ce=Ae-ie,re=ae-Ce;let et,ot,k;if(H)({originX:et,originY:ot,ppm:k}=H);else{const a=e.notchDir,l=a&&a.y>.1?13:0,r=a&&a.y<-.1?13:0,f=a&&a.x<-.1?13:0,d=a&&a.x>.1?13:0,b=Re-f-d,i=le-Y-l-r;k=Math.min(b/ce,i/re),et=c+Bt+Jt+f+(b-ce*k)/2-ie*k,ot=c+Y+zt+l+(i-re*k)/2+ae*k}const Ft=H?.snapDist??Math.max(jt,Qt,1)*1.5,Ot=et+e.waferCenter.x*k,Pe=ot-e.waferCenter.y*k,vt=e.waferRadius*k,rt=o=>{const a=Math.abs(o-Pe);return a>=vt?0:Math.sqrt(vt*vt-a*a)},ft=(o,a,l,r,f,d,b=He,i="#333")=>{if(!o)return;t.save(),t.fillStyle=i,t.font=b,t.textAlign=r,t.textBaseline=f;const x=r==="right"?a-d:d-a;let s=o;if(x>8&&t.measureText(s).width>x){for(;s.length>1&&t.measureText(s+"\u2026").width>x;)s=s.slice(0,-1);s=s+"\u2026"}x>8&&t.fillText(s,a,l),t.restore()};if(t.save(),t.setTransform(k*$,0,0,-k*$,et*$,ot*$),e.rectangles.length>0){const o=new Map;for(const l of e.rectangles){const r=String(l.fill);let f=o.get(r);f||(f=[],o.set(r,f)),f.push(l)}for(const[l,r]of o){t.beginPath();for(const f of r)t.rect(f.x-f.width/2,f.y-f.height/2,f.width,f.height);t.fillStyle=l,t.fill()}t.strokeStyle="rgba(0,0,0,0.18)",t.lineWidth=.5/(k*$),t.beginPath();for(const l of e.rectangles)t.rect(l.x-l.width/2,l.y-l.height/2,l.width,l.height);t.stroke();const a=e.rectangles.filter(l=>l.specMark);if(a.length>0){const l=new Map;for(const f of a){const d=Ne(String(f.fill)),b=`${f.specMark}|${d}`;let i=l.get(b);i||(i=[],l.set(b,i)),i.push(f)}const r=2.5/(k*$);t.lineJoin="round";for(const[f,d]of l){const[b,i]=f.split("|"),x=i==="#000000"?"#ffffff":"#000000",s=b==="failHigh"?1:-1,u=Math.min(d[0].width,d[0].height),X=Math.max(2.5/(k*$),u*.3);t.beginPath();for(const T of d)Te(t,T.x,T.y,X,s);t.strokeStyle=x,t.lineWidth=r,t.stroke(),t.fillStyle=i,t.fill()}}}for(const o of e.overlays){t.beginPath();for(const a of o.points){for(let l=0;l<a.length;l++)l===0?t.moveTo(a[l].x,a[l].y):t.lineTo(a[l].x,a[l].y);o.closed&&t.closePath()}o.fill&&!o.fill.startsWith("rgba(0,0,0,0)")&&(t.fillStyle=o.fill,t.fill()),o.kind==="ring-boundary"||o.kind==="quadrant-boundary"||o.kind==="reticle"?(t.strokeStyle="rgba(0,0,0,0.45)",t.lineWidth=3/(k*$),t.stroke(),t.strokeStyle="rgba(255,255,255,0.8)",t.lineWidth=1/(k*$),t.stroke()):(t.strokeStyle=o.lineColor,t.lineWidth=o.lineWidth/(k*$),t.stroke())}if(t.restore(),e.notchDir){const o=et+e.waferCenter.x*k,a=ot-e.waferCenter.y*k,l=e.notchDir.x,r=-e.notchDir.y,f=e.waferRadius*k,d=4,b=9,i=5,x=o+l*(f+d),s=a+r*(f+d),u=x+l*b,X=s+r*b,T={x:-r,y:l},B=c+Bt+Jt,nt=I-c-Kt,z=c+Y+zt,dt=V-c-se-Zt;t.save(),t.beginPath(),t.rect(B,z,nt-B,dt-z),t.clip(),t.beginPath(),t.moveTo(x,s),t.lineTo(u+T.x*i,X+T.y*i),t.lineTo(u-T.x*i,X-T.y*i),t.closePath(),t.fillStyle=p.text,t.fill(),t.restore()}t.save(),t.textAlign="center",t.textBaseline="middle";let fe=0;if(e.rectangles.length>0){let o=1;for(const d of e.texts)d.role!=="indicator"&&d.text.length>o&&(o=d.text.length);const a=e.rectangles[0].width*k,l=e.rectangles[0].height*k,r=Math.min(a,l),f=a/o;fe=Math.max(8,Math.min(64,Math.round(Math.min(r*.55,f*1.8))))}for(const o of e.texts){const a=et+o.x*k,l=ot-o.y*k;t.font=`${o.role==="indicator"?o.fontSize:fe}px system-ui, sans-serif`,t.fillStyle=o.color,t.fillText(o.text,a,l)}if(t.restore(),mt&&Ue(t,I,V,et,ot,k,c,Zt,Bt,Tt,e.gridToScreen,e.rotation,p),ut){const o=ye(e.colorScheme),a=20,l=le-Y,r=Math.round(l*.75),f=c+Y+Math.round((l-r)/2),d=I-c-S-a,[b,i]=e.valueRange,x=i-b,s=e.plotMode==="stackedSoftBins"?it:Mt,{primary:u,secondary:X}=tt?pe(e,O,s):{primary:"",secondary:""};if(tt){const n=Math.max(f-6,c+Y+11),g=Ot+Math.max(rt(n),rt(n-12))+8;ft(u,I-c,n,"right","bottom",g)}let T=f+r+4;const B=n=>Ot+Math.max(rt(n),rt(n+12))+8,nt=e.logScale&&b>0?Math.log10(b):0,z=e.logScale&&i>0?Math.log10(i)-nt:1,dt=n=>e.logScale?(1-(Math.log10(n)-nt)/z)*r:(1-(n-b)/x)*r;for(let n=0;n<Gt;n++){const g=1-n/(Gt-1),_=f+n/Gt*r,W=r/Gt+1;t.fillStyle=o.forValue(g),t.fillRect(d,_,S,W)}t.strokeStyle="rgba(0,0,0,0.25)",t.lineWidth=.5,t.strokeRect(d,f,S,r),t.fillStyle=p.text,t.font=v,t.textAlign="left",t.strokeStyle="rgba(0,0,0,0.35)",t.lineWidth=.5;const L=3,N=36,st=14,Xt=e.logScale&&z>0?Ve(b,i,r,N,st,nt,z):e.allIntegerValues?(()=>{const n=Math.max(1,qt(x*N/r)),g=[],_=Math.ceil(b/n)*n;for(let W=_;W<=i+n*1e-6;W+=n){const D=(1-(W-b)/x)*r;D>st&&D<r-st&&g.push(Math.round(W))}return g})():(()=>{const n=x>0?qt(x*N/r):0,g=[];if(n>0){const _=Math.ceil(b/n)*n;for(let W=_;W<=i+n*1e-6;W+=n){const D=(1-(W-b)/x)*r;D>st&&D<r-st&&g.push(W)}}return g})(),A=ue(e.testDefs,e.activeTest),C=e.plotMode==="stackedBins"||e.plotMode==="stackedSoftBins",m=C?"Count":A?.name??(e.activeTest!=null?`Test ${e.activeTest}`:void 0),M=C?void 0:A?.unit,{tickFmt:R}=Ee(i,m,M,O),P=e.allIntegerValues?n=>String(Math.round(n)):R;t.textBaseline="middle";for(const n of Xt){const g=f+dt(n);t.beginPath(),t.moveTo(d+S,g),t.lineTo(d+S+L,g),t.stroke(),t.fillText(P(n),d+S+L+2,g)}if(t.beginPath(),t.moveTo(d+S,f),t.lineTo(d+S+L,f),t.stroke(),t.textBaseline="top",t.fillText(P(i),d+S+L+2,f),t.beginPath(),t.moveTo(d+S,f+r),t.lineTo(d+S+L,f+r),t.stroke(),t.textBaseline="bottom",t.fillText(P(b),d+S+L+2,f+r),!C&&A&&(A.limitLow!==void 0||A.limitHigh!==void 0)){const n=[],g=e.colorbarRangeMode==="spec";for(const[_,W]of[[A.limitLow,"LSL"],[A.limitHigh,"USL"]]){if(_===void 0||e.logScale&&_<=0)continue;const D=dt(_);!g&&(D<-st||D>r+st)||n.push({value:_,label:W,py:Math.max(0,Math.min(r,D))})}if(n.length>0){t.save(),t.font=v,t.textAlign="right",t.textBaseline="middle";for(const{py:_,label:W}of n){const D=f+_;g||(t.strokeStyle="rgba(255,255,255,0.85)",t.lineWidth=3,t.beginPath(),t.moveTo(d,D),t.lineTo(d+S,D),t.stroke(),t.strokeStyle="rgba(0,0,0,0.7)",t.lineWidth=1,t.beginPath(),t.moveTo(d,D),t.lineTo(d+S,D),t.stroke()),t.fillStyle=p.text,t.fillText(W,d-3,D);const wt=W==="USL"?-1:1,Ht=4,De=d-3-t.measureText(W).width-Ht-4;t.beginPath(),Te(t,De,D,Ht,wt),t.strokeStyle="#fff",t.lineWidth=2,t.lineJoin="round",t.stroke(),t.fillStyle=p.text,t.fill()}t.restore()}}if(!C){const n=e.logScale?"log\u2081\u2080":e.logScaleRequested?"linear \u2014 log n/a":null;n&&(t.save(),t.fillStyle=p.text,t.font=$e,t.textAlign="right",t.textBaseline="top",t.fillText(n,I-c,T),t.restore(),T+=15)}tt&&X&&ft(X,I-c,T,"right","top",B(T),$t,p.text)}const te=[];let Z;if(at){const o=ye(e.colorScheme),a=K==="compact",l=e.plotMode==="softBin"?it:Mt,r=l?new Map(l.map(s=>[s.bin,s])):null,f=s=>{const u=r?.get(s);return(e.colorScheme==="custom"?u?.color:void 0)??o.forBin(s)},d=(s,u)=>{const X=t.measureText(s).width,T=a?0:t.measureText(String(u)).width,B=w+xt+X+(T?Ut:0);return{labelWidth:X,countWidth:T,totalWidth:B}},b=Lt?.find(s=>s.key===e.activeMetadataKey),i=pt?J.map(s=>{const u=a?s.compactLabel:s.label;return{key:s.key,color:s.color,count:s.count,label:u,tooltipLabel:s.tooltip,...d(u,s.count)}}):e.plotMode==="metadata"?At.map(([s,u])=>{const T=b?.values?.find(z=>z.value===s)?.label??s,B=a?s:T,nt=e.metadataColorMap?.get(s)??"#d6d9dd";return{key:s,color:nt,count:u,label:B,tooltipLabel:`${T} \xB7 ${u} dies`,...d(B,u)}}):Rt.map(([s,u])=>{const X=r?.get(s),T=X?.name?`${s} \xB7 ${X.name}`:`Bin ${s}`,B=a?String(s):T;return{key:s,color:f(s),count:u,label:B,tooltipLabel:`${T} \xB7 ${u} dies`,...d(B,u)}}),x=I-c*2;if(i.length>0){const s=Math.max(1,Math.floor((V-2*c)/E));let u=i.length,X=1,T=u*E,B=[],nt=u,z=0;const dt=ct||Ct;if(dt){const A=Math.max(...i.map(m=>m.totalWidth)),C=Math.max(1,Math.min(i.length,Math.floor((x+8)/(A+8))));for(let m=C;m>=1;m--){const M=Math.ceil(i.length/m);if(M>s)continue;const R=new Array(m).fill(0);for(let n=0;n<i.length;n++){const g=n%m;R[g]=Math.max(R[g],i[n].totalWidth)}if(R.reduce((n,g)=>n+g,0)+(m-1)*8<=x||m===1){X=m,B=R,nt=M,u=M,T=M*E;break}}}else u=Math.min(i.length,s),T=u*E,i.length>s&&(z=i.length-(s-1)),B=[Wt];let L,N;if(Pt){const A=Math.max(...i.map(n=>w+xt+t.measureText(n.label).width+Ut)),C=Math.ceil(A);B=[C];const m=C+Vt*2,M=T+Vt*2,R=j?.x??0,P=j?.y??0;L=Math.min(Math.max(I-c-m+R,c),I-c-m),N=Math.min(Math.max(V-c-M+P,c),V-c-M),Z={x:L,y:N,w:m,h:M},t.save(),t.fillStyle="rgba(255,255,255,0.92)",t.strokeStyle="rgba(0,0,0,0.1)",t.lineWidth=1,t.fillRect(L,N,m,M),t.strokeRect(L,N,m,M),t.restore(),L+=Vt,N+=Vt}else ct?(L=c,N=V-c-T):Ct?(L=c,N=c+Y):G?(L=c+4,N=c+Math.round((V-2*c-T)/2)):(L=I-c-Wt+4,N=c+Math.round((V-2*c-T)/2));if(tt){const A=e.plotMode==="softBin"?it:Mt,{primary:C,secondary:m}=pe(e,O,A),M=6,R=g=>Ot-Math.max(rt(g),rt(g-12))-8,P=g=>y?I-c:R(g),n=Pt?Z.y+Z.h:N+T;if(Ct){const g=n+M;ft(C,L,g,"left","top",Ot-Math.max(rt(g),rt(g+12))-8),m&&ft(m,L,g+16,"left","top",R(g+16),$t,p.text)}else if(Pt)ft(C,Z.x,Z.y-M,"left","bottom",Z.x+Z.w),m&&ft(m,Z.x,n+M,"left","top",Z.x+Z.w,$t,p.text);else{const g=Math.max(N-M,c+Y+12);if(ft(C,L,g,"left","bottom",P(g)),m){const _=n+M;ft(m,L,_,"left","top",P(_),$t,p.text)}}}t.save(),t.font=v;const st=dt?8:0,Xt=(A,C)=>{if(t.measureText(A).width<=C)return A;let m=0,M=A.length;const R="\u2026",P=t.measureText(R).width;for(;m<M;){const n=m+M+1>>1;t.measureText(A.slice(0,n)).width+P<=C?m=n:M=n-1}return m===0?R:A.slice(0,m)+R};if(dt){let A=0;for(let C=0;C<u;C++){let m=L;for(let M=0;M<X;M++){const R=i[A++];if(!R)break;const P=R.key===Q,n=m,g=m+w+xt,_=N+C*E+E/2,W=N+C*E+Math.round((E-w)/2),D=B[M]-w-xt-Ut;t.fillStyle=R.color,t.fillRect(n,W,w,w),t.strokeStyle=P?p.accent:"rgba(0,0,0,0.25)",t.lineWidth=P?2:.75,t.strokeRect(n,W,w,w),t.fillStyle=P?p.accent:p.text,t.font=P?`bold ${v}`:v,t.textAlign="left",t.textBaseline="middle",t.fillText(Xt(R.label,D),g,_),t.fillStyle=p.textMuted,t.font=v,t.textAlign="right",t.fillText(String(R.count),m+B[M]-2,_),te.push({bin:R.key,x:m,y:N+C*E,w:B[M],h:E,label:R.tooltipLabel}),m+=B[M]+st}}}else{const A=B[0],C=K!=="compact",m=A-w-xt-(C?Ut:0),M=L+A-2;let R=i;z>0&&(R=i.slice(0,s-1));let P=N;for(const n of R){const g=n.key===Q,_=L,W=L+w+xt,D=P+E/2,wt=P+Math.round((E-w)/2),Ht=n.label;t.fillStyle=n.color,t.fillRect(_,wt,w,w),t.strokeStyle=g?p.accent:"rgba(0,0,0,0.25)",t.lineWidth=g?2:.75,t.strokeRect(_,wt,w,w),t.fillStyle=g?p.accent:p.text,t.font=g?`bold ${v}`:v,t.textAlign="left",t.textBaseline="middle",t.fillText(Xt(Ht,m),W,D),(C||Pt)&&(t.fillStyle=p.textMuted,t.font=v,t.textAlign="right",t.fillText(String(n.count),M,D)),te.push({bin:n.key,x:L,y:P,w:A,h:E,label:n.tooltipLabel}),P+=E}z>0&&(t.fillStyle=p.textMuted,t.font=v,t.textAlign="left",t.textBaseline="middle",t.fillText(`+ ${z} more`,L+w+xt,P+E/2))}}i.length>0&&t.restore()}const We={originX:et,originY:ot,ppm:k,snapDist:Ft},he=e.rectangles[0]?.width??1,de=e.rectangles[0]?.height??1,It=he*1.5,Nt=de*1.5,ht=e.dieBounds,Et=ht?ht.minX:q.length?Math.min(...q.map(o=>o.x)):0,Yt=ht?ht.minY:q.length?Math.min(...q.map(o=>o.y)):0,Be=ht?ht.maxX:q.length?Math.max(...q.map(o=>o.x)):1,_e=ht?ht.maxY:q.length?Math.max(...q.map(o=>o.y)):1,St=Math.max(1,Math.ceil((Be-Et)/It)+1),ee=Math.max(1,Math.ceil((_e-Yt)/Nt)+1),ge=Array.from({length:St*ee},()=>[]);for(let o=0;o<q.length;o++){const a=Math.floor((q[o].x-Et)/It),l=Math.floor((q[o].y-Yt)/Nt),r=Math.max(0,Math.min(St-1,a)),f=Math.max(0,Math.min(ee-1,l));ge[f*St+r].push(o)}function me(o,a,l){const r=Math.max(0,Math.floor((o-l-Et)/It)),f=Math.min(St-1,Math.floor((o+l-Et)/It)),d=Math.max(0,Math.floor((a-l-Yt)/Nt)),b=Math.min(ee-1,Math.floor((a+l-Yt)/Nt)),i=[];for(let x=d;x<=b;x++)for(let s=r;s<=f;s++)for(const u of ge[x*St+s])i.push(u);return i}return{hitTarget:{getDieAtPoint(o,a){const l=(o-et)/k,r=(ot-a)/k,f=Math.max(he,de);for(const i of me(l,r,f)){const x=e.rectangles[i];if(x&&Math.abs(l-x.x)<=x.width/2&&Math.abs(r-x.y)<=x.height/2)return e.dies[i]??null}let d=null,b=Ft*Ft;for(const i of me(l,r,Ft)){const x=q[i].x-l,s=q[i].y-r,u=x*x+s*s;u<b&&(b=u,d=e.dies[i]??null)}return d}},viewport:We,binLegendRows:te,legendBox:Z}}function Ue(h,e,F,p,c,U,S,lt,gt,j,mt,Tt,H){h.save(),h.font=Ge,h.fillStyle=H.text,h.strokeStyle=H.axisLine,h.lineWidth=.5;const Q=F-lt+4,O=S+gt-4,Y=Fe(mt),kt=xe(Y,1,0),Mt=xe(Y,0,1),it=1e-9;function Lt(y){return j?Math.abs(y.y)<it&&Math.abs(y.x)>it?{pitch:j.x,sign:Math.sign(y.x)}:Math.abs(y.x)<it&&Math.abs(y.y)>it?{pitch:j.y,sign:Math.sign(y.y)}:null:null}const tt=Lt(kt),ut=Lt(Mt);function bt(y){return tt?Math.round(y*tt.sign/tt.pitch):y}function pt(y){return ut?Math.round(y*ut.sign/ut.pitch):y}function at(y){if(!y)return qt(50/U);const G=50/(y*U);return Math.max(1,Math.round(G))*y}const Rt=tt?.pitch,At=ut?.pitch,J=at(Rt),yt=at(At);h.textAlign="center",h.textBaseline="top";const $=Math.ceil((S-p)/U/J)*J,I=(e-S-p)/U;for(let y=$;y<=I;y+=J){const G=p+y*U;if(G<S||G>e-S)continue;h.beginPath(),h.moveTo(G,Q-ke),h.lineTo(G,Q),h.stroke();const ct=j?String(bt(y)):Se(y);h.fillText(ct,G,Q+2)}h.textAlign="right",h.textBaseline="middle";const V=Math.ceil((c-(F-S))/U/yt)*yt,K=(c-S)/U;for(let y=V;y<=K;y+=yt){const G=c-y*U;if(G<S||G>F-S)continue;h.beginPath(),h.moveTo(O,G),h.lineTo(O+ke,G),h.stroke();const ct=j?String(pt(y)):Se(y);h.fillText(ct,O-2,G)}h.restore()}function qt(h){const e=Math.pow(10,Math.floor(Math.log10(h))),F=h/e;return(F<1.5?1:F<3.5?2:F<7.5?5:10)*e}function Ve(h,e,F,p,c,U,S){const lt=[],gt=S;if(gt<1){const H=qt((e-h)*p/F);if(H>0){const Q=Math.ceil(h/H)*H;for(let O=Q;O<=e+H*1e-6;O+=H){if(O<=h||O>=e)continue;const Y=(1-(Math.log10(O)-U)/S)*F;Y>c&&Y<F-c&&lt.push(O)}}return lt}const j=gt<3?[1,2,5]:[1],mt=Math.floor(Math.log10(h)),Tt=Math.ceil(Math.log10(e));for(let H=mt;H<=Tt;H++)for(const Q of j){const O=Q*Math.pow(10,H);if(O<=h||O>=e)continue;const Y=(1-(Math.log10(O)-U)/S)*F;Y>c&&Y<F-c&&lt.push(O)}return lt}export{fmt,fmtColorbarAxis}from"../renderer/fmt.js";
1
+ import{findTestDef as xe,buildMapTitle as ye}from"../renderer/buildView.js";import{affineInvert as Oe,affineVector as Me}from"../core/transforms.js";import{compareNatural as Ee}from"../core/utils.js";import{getColorScheme as be}from"../renderer/colorSchemes.js";import{SPEC_PASS_FILL as Te,SPEC_FAIL_LOW as Ye,SPEC_FAIL_HIGH as Se,contrastTextColor as ke}from"../renderer/colorMap.js";import{fmt as Le,fmtColorbarAxis as we}from"../renderer/fmt.js";import{resolveCanvasTheme as Xe}from"./canvasTheme.js";function Ae(g,e,N,M,h){g.moveTo(e,N+h*M),g.lineTo(e-M*.9,N-h*M*.6),g.lineTo(e+M*.9,N-h*M*.6),g.closePath()}const He=new Set(["value","stackedValues","stackedBins","stackedSoftBins"]),$e=new Set(["hardBin","softBin","metadata"]),v="10px system-ui, sans-serif",Ge="600 12px system-ui, sans-serif",Gt="11px system-ui, sans-serif",Ve="600 11px system-ui, sans-serif",Vt=128,Ue="10px system-ui, sans-serif",Re=4,Y=17,X=11;export const BIN_LEGEND_W=110,BIN_LEGEND_W_COMPACT=64,BIN_LEGEND_ADAPT_COMPACT=280,BIN_LEGEND_ADAPT_FLOATING=180,BIN_LEGEND_MIN_CANVAS_W=150,BIN_LEGEND_MIN_CANVAS_H=120;const Ut=28,gt=5,mt=8,qe=15;export function toCanvas(g,e,N={}){const M=Xe(g.parentElement??g),{padding:h=16,showColorbar:$=!0,markFailingDies:G=!1,colorbarWidth:_=16,background:ut=M.background,legendPosition:Z="default",legendOffset:pt,showAxes:xt=!1,diePitchMm:V,viewport:K,activeBin:E,fallbackFormat:z,topClearance:j=0,minRightReserve:kt,hbinDefs:at,sbinDefs:yt,metadataFields:Mt,showTitle:Q=!0}=N,I=Math.floor(g.clientWidth||g.width),H=Math.floor(g.clientHeight||g.height),bt=I>=BIN_LEGEND_MIN_CANVAS_W&&H>=BIN_LEGEND_MIN_CANVAS_H,Lt=$&&bt&&He.has(e.plotMode)&&e.passFailDisplay==="off",Tt=$&&bt&&$e.has(e.plotMode),tt=$&&bt&&e.plotMode==="value"&&(e.passFailDisplay==="spec"&&e.specCounts!==void 0||e.passFailDisplay==="test"&&e.passFailCounts!==void 0),et=Tt||tt,At=Tt&&e.plotMode!=="metadata"&&e.binCounts?[...e.binCounts.entries()].sort(([o],[r])=>o-r):[],Rt=Tt&&e.plotMode==="metadata"&&e.metadataCounts?[...e.metadataColorMap?.keys()??[...e.metadataCounts.keys()].sort(Ee)].map(o=>[o,e.metadataCounts.get(o)??0]):[],ot=[];if(tt&&e.passFailDisplay==="test"){const o=e.passFailCounts;ot.push({key:-1,color:Te,label:"Pass",compactLabel:"Pass",count:o.pass,tooltip:`Pass \xB7 ${o.pass} dies`}),ot.push({key:-2,color:Se,label:"Fail",compactLabel:"Fail",count:o.fail,tooltip:`Fail \xB7 ${o.fail} dies`})}else if(tt){const o=e.specCounts,r=xe(e.testDefs,e.activeTest);ot.push({key:-1,color:Te,label:"Pass",compactLabel:"Pass",count:o.pass,tooltip:`Pass \xB7 ${o.pass} dies`}),r?.limitHigh!==void 0&&ot.push({key:-2,color:Se,label:"Fail high",compactLabel:"Fail hi",count:o.failHigh,tooltip:`Fail high \xB7 ${o.failHigh} dies`}),r?.limitLow!==void 0&&ot.push({key:-3,color:Ye,label:"Fail low",compactLabel:"Fail lo",count:o.failLow,tooltip:`Fail low \xB7 ${o.failLow} dies`})}const Ct=tt?ot.length:e.plotMode==="metadata"?Rt.length:At.length,m=(g.ownerDocument.defaultView??window).devicePixelRatio??1;if(I<=0||H<=0)return{hitTarget:{getDieAtPoint:()=>null},viewport:{originX:0,originY:0,ppm:1,snapDist:1},binLegendRows:[]};const C=Z!=="default"?Z:I<BIN_LEGEND_ADAPT_FLOATING?"floating":I<BIN_LEGEND_ADAPT_COMPACT?"compact":"default",dt=C==="default"||C==="compact",ne=C==="left",zt=C==="bottom",_t=C==="top",Pt=C==="floating",Wt=C==="compact"||C==="floating"?BIN_LEGEND_W_COMPACT:BIN_LEGEND_W,se=Math.floor((H-2*h)/Y),le=Ct>se?se-1:Ct,ie=et&&zt?le*Y:0,Kt=et&&_t?le*Y:0,Jt=Lt?_+28:et&&dt?Wt:0,Zt=et&&ne?Wt:0,jt=xt?32:0,Bt=xt?36:0,Ce=kt!=null?Math.max(Jt,kt):Jt,_e=I-2*h-Ce-Bt-Zt,ae=H-2*h-jt-ie-Kt;g.width=Math.round(I*m),g.height=Math.round(H*m);const t=g.getContext("2d");t.scale(m,m),t.fillStyle=ut,t.fillRect(0,0,I,H);const U=e.hoverPoints;if(!U.length)return{hitTarget:{getDieAtPoint:()=>null},viewport:{originX:0,originY:0,ppm:1,snapDist:1},binLegendRows:[]};const Dt=e.rectangles[0],Qt=Dt?Dt.width/2:0,vt=Dt?Dt.height/2:0,Ft=e.dieBounds,ce=(Ft?.minX??0)-Qt,Pe=(Ft?.maxX??0)+Qt,We=(Ft?.minY??0)-vt,re=(Ft?.maxY??0)+vt,fe=Pe-ce,he=re-We;let nt,st,b;if(K)({originX:nt,originY:st,ppm:b}=K);else{const r=e.notchDir,l=r&&r.y>.1?13:0,a=r&&r.y<-.1?13:0,c=r&&r.x<-.1?13:0,f=r&&r.x>.1?13:0,d=_e-c-f,i=ae-j-l-a;b=Math.min(d/fe,i/he),nt=h+Bt+Zt+c+(d-fe*b)/2-ce*b,st=h+j+Kt+l+(i-he*b)/2+re*b}const It=K?.snapDist??Math.max(Qt,vt,1)*1.5,Nt=nt+e.waferCenter.x*b,Be=st-e.waferCenter.y*b,te=e.waferRadius*b,ct=o=>{const r=Math.abs(o-Be);return r>=te?0:Math.sqrt(te*te-r*r)},rt=(o,r,l,a,c,f,d=Ge,i=M.text)=>{if(!o)return;t.save(),t.fillStyle=i,t.font=d,t.textAlign=a,t.textBaseline=c;const p=a==="right"?r-f:f-r;let s=o;if(p>8&&t.measureText(s).width>p){for(;s.length>1&&t.measureText(s+"\u2026").width>p;)s=s.slice(0,-1);s=s+"\u2026"}p>8&&t.fillText(s,r,l),t.restore()};if(t.save(),t.setTransform(b*m,0,0,-b*m,nt*m,st*m),e.rectangles.length>0){const o=new Map;for(const l of e.rectangles){const a=String(l.fill);let c=o.get(a);c||(c=[],o.set(a,c)),c.push(l)}for(const[l,a]of o){t.beginPath();for(const c of a)t.rect(c.x-c.width/2,c.y-c.height/2,c.width,c.height);t.fillStyle=l,t.fill()}t.strokeStyle="rgba(0,0,0,0.18)",t.lineWidth=.5/(b*m),t.beginPath();for(const l of e.rectangles)t.rect(l.x-l.width/2,l.y-l.height/2,l.width,l.height);if(t.stroke(),G){const l=e.rectangles.filter(a=>a.binFail);if(l.length>0){const a=new Map;for(const c of l){const f=ke(String(c.fill));let d=a.get(f);d||(d=[],a.set(f,d)),d.push(c)}t.save(),t.lineCap="butt";for(const[c,f]of a){t.strokeStyle=c,t.globalAlpha=.55,t.lineWidth=Math.max(.6/(b*m),Math.min(f[0].width,f[0].height)*.1);for(const d of f){const i=d.width/2,p=d.height/2,s=Math.max(1.6/(b*m),Math.min(d.width,d.height)/3);t.save(),t.beginPath(),t.rect(d.x-i,d.y-p,d.width,d.height),t.clip(),t.beginPath();for(let x=-(i+p);x<=i+p;x+=s)t.moveTo(d.x-i+x,d.y-p),t.lineTo(d.x-i+x+d.height,d.y+p);t.stroke(),t.restore()}}t.restore()}}const r=e.rectangles.filter(l=>l.specMark);if(r.length>0){const l=new Map;for(const c of r){const f=ke(String(c.fill)),d=`${c.specMark}|${f}`;let i=l.get(d);i||(i=[],l.set(d,i)),i.push(c)}const a=2.5/(b*m);t.lineJoin="round";for(const[c,f]of l){const[d,i]=c.split("|"),p=i==="#000000"?"#ffffff":"#000000",s=d==="failHigh"?1:-1,x=Math.min(f[0].width,f[0].height),w=Math.max(2.5/(b*m),x*.3);t.beginPath();for(const k of f)Ae(t,k.x,k.y,w,s);t.strokeStyle=p,t.lineWidth=a,t.stroke(),t.fillStyle=i,t.fill()}}}for(const o of e.overlays){t.beginPath();for(const r of o.points){for(let l=0;l<r.length;l++)l===0?t.moveTo(r[l].x,r[l].y):t.lineTo(r[l].x,r[l].y);o.closed&&t.closePath()}o.fill&&!o.fill.startsWith("rgba(0,0,0,0)")&&(t.fillStyle=o.fill,t.fill()),o.kind==="ring-boundary"||o.kind==="quadrant-boundary"||o.kind==="reticle"?(t.strokeStyle="rgba(0,0,0,0.45)",t.lineWidth=3/(b*m),t.stroke(),t.strokeStyle="rgba(255,255,255,0.8)",t.lineWidth=1/(b*m),t.stroke()):(t.strokeStyle=o.lineColor,t.lineWidth=o.lineWidth/(b*m),t.stroke())}if(t.restore(),e.notchDir){const o=nt+e.waferCenter.x*b,r=st-e.waferCenter.y*b,l=e.notchDir.x,a=-e.notchDir.y,c=e.waferRadius*b,f=4,d=9,i=5,p=o+l*(c+f),s=r+a*(c+f),x=p+l*d,w=s+a*d,k={x:-a,y:l},B=h+Bt+Zt,lt=I-h-Jt,q=h+j+Kt,ht=H-h-ie-jt;t.save(),t.beginPath(),t.rect(B,q,lt-B,ht-q),t.clip(),t.beginPath(),t.moveTo(p,s),t.lineTo(x+k.x*i,w+k.y*i),t.lineTo(x-k.x*i,w-k.y*i),t.closePath(),t.fillStyle=M.text,t.fill(),t.restore()}t.save(),t.textAlign="center",t.textBaseline="middle";let de=0;if(e.rectangles.length>0){let o=1;for(const f of e.texts)f.role!=="indicator"&&f.text.length>o&&(o=f.text.length);const r=e.rectangles[0].width*b,l=e.rectangles[0].height*b,a=Math.min(r,l),c=r/o;de=Math.max(8,Math.min(64,Math.round(Math.min(a*.55,c*1.8))))}for(const o of e.texts){const r=nt+o.x*b,l=st-o.y*b;t.font=`${o.role==="indicator"?o.fontSize:de}px system-ui, sans-serif`,t.fillStyle=o.color,t.fillText(o.text,r,l)}if(t.restore(),xt&&ze(t,I,H,nt,st,b,h,jt,Bt,V,e.gridToScreen,e.rotation,M),Lt){const o=be(e.colorScheme),r=20,l=ae-j,a=Math.round(l*.75),c=h+j+Math.round((l-a)/2),f=I-h-_-r,[d,i]=e.valueRange,p=i-d,s=e.plotMode==="stackedSoftBins"?yt:at,{primary:x,secondary:w}=Q?ye(e,z,s):{primary:"",secondary:""};if(Q){const n=Math.max(c-6,h+j+11),u=Nt+Math.max(ct(n),ct(n-12))+8;rt(x,I-h,n,"right","bottom",u)}let k=c+a+4;const B=n=>Nt+Math.max(ct(n),ct(n+12))+8,lt=e.logScale&&d>0?Math.log10(d):0,q=e.logScale&&i>0?Math.log10(i)-lt:1,ht=n=>e.logScale?(1-(Math.log10(n)-lt)/q)*a:(1-(n-d)/p)*a;for(let n=0;n<Vt;n++){const u=1-n/(Vt-1),D=c+n/Vt*a,W=a/Vt+1;t.fillStyle=o.forValue(u),t.fillRect(f,D,_,W)}t.strokeStyle="rgba(0,0,0,0.25)",t.lineWidth=.5,t.strokeRect(f,c,_,a),t.fillStyle=M.text,t.font=v,t.textAlign="left",t.strokeStyle="rgba(0,0,0,0.35)",t.lineWidth=.5;const L=3,O=36,it=14,Xt=e.logScale&&q>0?Ke(d,i,a,O,it,lt,q):e.allIntegerValues?(()=>{const n=Math.max(1,qt(p*O/a)),u=[],D=Math.ceil(d/n)*n;for(let W=D;W<=i+n*1e-6;W+=n){const F=(1-(W-d)/p)*a;F>it&&F<a-it&&u.push(Math.round(W))}return u})():(()=>{const n=p>0?qt(p*O/a):0,u=[];if(n>0){const D=Math.ceil(d/n)*n;for(let W=D;W<=i+n*1e-6;W+=n){const F=(1-(W-d)/p)*a;F>it&&F<a-it&&u.push(W)}}return u})(),A=xe(e.testDefs,e.activeTest),R=e.plotMode==="stackedBins"||e.plotMode==="stackedSoftBins",y=R?"Count":A?.name??(e.activeTest!=null?`Test ${e.activeTest}`:void 0),T=R?void 0:A?.unit,{tickFmt:S}=we(i,y,T,z),P=e.allIntegerValues?n=>String(Math.round(n)):S;t.textBaseline="middle";for(const n of Xt){const u=c+ht(n);t.beginPath(),t.moveTo(f+_,u),t.lineTo(f+_+L,u),t.stroke(),t.fillText(P(n),f+_+L+2,u)}if(t.beginPath(),t.moveTo(f+_,c),t.lineTo(f+_+L,c),t.stroke(),t.textBaseline="top",t.fillText(P(i),f+_+L+2,c),t.beginPath(),t.moveTo(f+_,c+a),t.lineTo(f+_+L,c+a),t.stroke(),t.textBaseline="bottom",t.fillText(P(d),f+_+L+2,c+a),!R&&A&&(A.limitLow!==void 0||A.limitHigh!==void 0)){const n=[],u=e.colorbarRangeMode==="spec";for(const[D,W]of[[A.limitLow,"LSL"],[A.limitHigh,"USL"]]){if(D===void 0||e.logScale&&D<=0)continue;const F=ht(D);!u&&(F<-it||F>a+it)||n.push({value:D,label:W,py:Math.max(0,Math.min(a,F))})}if(n.length>0){t.save(),t.font=v,t.textAlign="right",t.textBaseline="middle";for(const{py:D,label:W}of n){const F=c+D;u||(t.strokeStyle="rgba(255,255,255,0.85)",t.lineWidth=3,t.beginPath(),t.moveTo(f,F),t.lineTo(f+_,F),t.stroke(),t.strokeStyle="rgba(0,0,0,0.7)",t.lineWidth=1,t.beginPath(),t.moveTo(f,F),t.lineTo(f+_,F),t.stroke()),t.fillStyle=M.text,t.fillText(W,f-3,F);const Ht=W==="USL"?-1:1,$t=4,Ne=f-3-t.measureText(W).width-$t-4;t.beginPath(),Ae(t,Ne,F,$t,Ht),t.strokeStyle="#fff",t.lineWidth=2,t.lineJoin="round",t.stroke(),t.fillStyle=M.text,t.fill()}t.restore()}}if(!R){const n=e.logScale?"log\u2081\u2080":e.logScaleRequested?"linear \u2014 log n/a":null;n&&(t.save(),t.fillStyle=M.text,t.font=Ve,t.textAlign="right",t.textBaseline="top",t.fillText(n,I-h,k),t.restore(),k+=15)}Q&&w&&rt(w,I-h,k,"right","top",B(k),Gt,M.text)}const ee=[];let J;if(et){const o=be(e.colorScheme),r=C==="compact",l=e.plotMode==="softBin"?yt:at,a=l?new Map(l.map(s=>[s.bin,s])):null,c=s=>{const x=a?.get(s);return(e.colorScheme==="custom"?x?.color:void 0)??o.forBin(s)},f=(s,x)=>{const w=t.measureText(s).width,k=r?0:t.measureText(String(x)).width,B=X+gt+w+(k?Ut:0);return{labelWidth:w,countWidth:k,totalWidth:B}},d=Mt?.find(s=>s.key===e.activeMetadataKey),i=tt?ot.map(s=>{const x=r?s.compactLabel:s.label;return{key:s.key,color:s.color,count:s.count,label:x,tooltipLabel:s.tooltip,...f(x,s.count)}}):e.plotMode==="metadata"?Rt.map(([s,x])=>{const k=d?.values?.find(q=>q.value===s)?.label??s,B=r?s:k,lt=e.metadataColorMap?.get(s)??"#d6d9dd";return{key:s,color:lt,count:x,label:B,tooltipLabel:`${k} \xB7 ${x} dies`,...f(B,x)}}):At.map(([s,x])=>{const w=a?.get(s),k=w?.name?`${s} \xB7 ${w.name}`:`Bin ${s}`,B=r?String(s):k;return{key:s,color:c(s),count:x,label:B,tooltipLabel:`${k} \xB7 ${x} dies`,...f(B,x)}}),p=I-h*2;if(i.length>0){const s=Math.max(1,Math.floor((H-2*h)/Y));let x=i.length,w=1,k=x*Y,B=[],lt=x,q=0;const ht=zt||_t;if(ht){const A=Math.max(...i.map(y=>y.totalWidth)),R=Math.max(1,Math.min(i.length,Math.floor((p+8)/(A+8))));for(let y=R;y>=1;y--){const T=Math.ceil(i.length/y);if(T>s)continue;const S=new Array(y).fill(0);for(let n=0;n<i.length;n++){const u=n%y;S[u]=Math.max(S[u],i[n].totalWidth)}if(S.reduce((n,u)=>n+u,0)+(y-1)*8<=p||y===1){w=y,B=S,lt=T,x=T,k=T*Y;break}}}else x=Math.min(i.length,s),k=x*Y,i.length>s&&(q=i.length-(s-1)),B=[Wt];let L,O;if(Pt){const A=Math.max(...i.map(u=>X+gt+t.measureText(u.label).width+Ut)),R=Math.ceil(A);B=[R];const y=Q?qe:0,T=R+mt*2,S=k+y+mt*2,P=pt?.x??0,n=pt?.y??0;L=Math.min(Math.max(I-h-T+P,h),I-h-T),O=Math.min(Math.max(H-h-S+n,h),H-h-S),J={x:L,y:O,w:T,h:S},t.save(),t.globalAlpha=.92,t.fillStyle=M.surface,t.fillRect(L,O,T,S),t.globalAlpha=1,t.strokeStyle=M.axisLine,t.lineWidth=1,t.strokeRect(L,O,T,S),t.restore(),L+=mt,O+=mt+y}else zt?(L=h,O=H-h-k):_t?(L=h,O=h+j):ne?(L=h+4,O=h+Math.round((H-2*h-k)/2)):(L=I-h-Wt+4,O=h+Math.round((H-2*h-k)/2));if(Q){const A=e.plotMode==="softBin"?yt:at,{primary:R,secondary:y}=ye(e,z,A),T=6,S=u=>Nt-Math.max(ct(u),ct(u-12))-8,P=u=>dt?I-h:S(u),n=Pt?J.y+J.h:O+k;if(_t){const u=n+T;rt(R,L,u,"left","top",Nt-Math.max(ct(u),ct(u+12))-8),y&&rt(y,L,u+16,"left","top",S(u+16),Gt,M.text)}else if(Pt)rt(R,J.x+mt,J.y+mt,"left","top",J.x+J.w-mt),y&&rt(y,J.x,n+T,"left","top",J.x+J.w,Gt,M.text);else{const u=Math.max(O-T,h+j+12);if(rt(R,L,u,"left","bottom",P(u)),y){const D=n+T;rt(y,L,D,"left","top",P(D),Gt,M.text)}}}t.save(),t.font=v;const it=ht?8:0,Xt=(A,R)=>{if(t.measureText(A).width<=R)return A;let y=0,T=A.length;const S="\u2026",P=t.measureText(S).width;for(;y<T;){const n=y+T+1>>1;t.measureText(A.slice(0,n)).width+P<=R?y=n:T=n-1}return y===0?S:A.slice(0,y)+S};if(ht){let A=0;for(let R=0;R<x;R++){let y=L;for(let T=0;T<w;T++){const S=i[A++];if(!S)break;const P=S.key===E,n=y,u=y+X+gt,D=O+R*Y+Y/2,W=O+R*Y+Math.round((Y-X)/2),F=B[T]-X-gt-Ut;t.fillStyle=S.color,t.fillRect(n,W,X,X),t.strokeStyle=P?M.accent:"rgba(0,0,0,0.25)",t.lineWidth=P?2:.75,t.strokeRect(n,W,X,X),t.fillStyle=P?M.accent:M.text,t.font=P?`bold ${v}`:v,t.textAlign="left",t.textBaseline="middle",t.fillText(Xt(S.label,F),u,D),t.fillStyle=M.textMuted,t.font=v,t.textAlign="right",t.fillText(String(S.count),y+B[T]-2,D),ee.push({bin:S.key,x:y,y:O+R*Y,w:B[T],h:Y,label:S.tooltipLabel}),y+=B[T]+it}}}else{const A=B[0],R=C!=="compact",y=A-X-gt-(R?Ut:0),T=L+A-2;let S=i;q>0&&(S=i.slice(0,s-1));let P=O;for(const n of S){const u=n.key===E,D=L,W=L+X+gt,F=P+Y/2,Ht=P+Math.round((Y-X)/2),$t=n.label;t.fillStyle=n.color,t.fillRect(D,Ht,X,X),t.strokeStyle=u?M.accent:"rgba(0,0,0,0.25)",t.lineWidth=u?2:.75,t.strokeRect(D,Ht,X,X),t.fillStyle=u?M.accent:M.text,t.font=u?`bold ${v}`:v,t.textAlign="left",t.textBaseline="middle",t.fillText(Xt($t,y),W,F),(R||Pt)&&(t.fillStyle=M.textMuted,t.font=v,t.textAlign="right",t.fillText(String(n.count),T,F)),ee.push({bin:n.key,x:L,y:P,w:A,h:Y,label:n.tooltipLabel}),P+=Y}q>0&&(t.fillStyle=M.textMuted,t.font=v,t.textAlign="left",t.textBaseline="middle",t.fillText(`+ ${q} more`,L+X+gt,P+Y/2))}}i.length>0&&t.restore()}const De={originX:nt,originY:st,ppm:b,snapDist:It},ge=e.rectangles[0]?.width??1,me=e.rectangles[0]?.height??1,Ot=ge*1.5,Et=me*1.5,ft=e.dieBounds,Yt=ft?ft.minX:U.length?Math.min(...U.map(o=>o.x)):0,wt=ft?ft.minY:U.length?Math.min(...U.map(o=>o.y)):0,Fe=ft?ft.maxX:U.length?Math.max(...U.map(o=>o.x)):1,Ie=ft?ft.maxY:U.length?Math.max(...U.map(o=>o.y)):1,St=Math.max(1,Math.ceil((Fe-Yt)/Ot)+1),oe=Math.max(1,Math.ceil((Ie-wt)/Et)+1),ue=Array.from({length:St*oe},()=>[]);for(let o=0;o<U.length;o++){const r=Math.floor((U[o].x-Yt)/Ot),l=Math.floor((U[o].y-wt)/Et),a=Math.max(0,Math.min(St-1,r)),c=Math.max(0,Math.min(oe-1,l));ue[c*St+a].push(o)}function pe(o,r,l){const a=Math.max(0,Math.floor((o-l-Yt)/Ot)),c=Math.min(St-1,Math.floor((o+l-Yt)/Ot)),f=Math.max(0,Math.floor((r-l-wt)/Et)),d=Math.min(oe-1,Math.floor((r+l-wt)/Et)),i=[];for(let p=f;p<=d;p++)for(let s=a;s<=c;s++)for(const x of ue[p*St+s])i.push(x);return i}return{hitTarget:{getDieAtPoint(o,r){const l=(o-nt)/b,a=(st-r)/b,c=Math.max(ge,me);for(const i of pe(l,a,c)){const p=e.rectangles[i];if(p&&Math.abs(l-p.x)<=p.width/2&&Math.abs(a-p.y)<=p.height/2)return e.dies[i]??null}let f=null,d=It*It;for(const i of pe(l,a,It)){const p=U[i].x-l,s=U[i].y-a,x=p*p+s*s;x<d&&(d=x,f=e.dies[i]??null)}return f}},viewport:De,binLegendRows:ee,legendBox:J}}function ze(g,e,N,M,h,$,G,_,ut,Z,pt,xt,V){g.save(),g.font=Ue,g.fillStyle=V.text,g.strokeStyle=V.axisLine,g.lineWidth=.5;const K=N-_+4,E=G+ut-4,z=Oe(pt),j=Me(z,1,0),kt=Me(z,0,1),at=1e-9;function yt(m){return Z?Math.abs(m.y)<at&&Math.abs(m.x)>at?{pitch:Z.x,sign:Math.sign(m.x)}:Math.abs(m.x)<at&&Math.abs(m.y)>at?{pitch:Z.y,sign:Math.sign(m.y)}:null:null}const Mt=yt(j),Q=yt(kt);function I(m){return Mt?Math.round(m*Mt.sign/Mt.pitch):m}function H(m){return Q?Math.round(m*Q.sign/Q.pitch):m}function bt(m){if(!m)return qt(50/$);const C=50/(m*$);return Math.max(1,Math.round(C))*m}const Lt=Mt?.pitch,Tt=Q?.pitch,tt=bt(Lt),et=bt(Tt);g.textAlign="center",g.textBaseline="top";const At=Math.ceil((G-M)/$/tt)*tt,Rt=(e-G-M)/$;for(let m=At;m<=Rt;m+=tt){const C=M+m*$;if(C<G||C>e-G)continue;g.beginPath(),g.moveTo(C,K-Re),g.lineTo(C,K),g.stroke();const dt=Z?String(I(m)):Le(m);g.fillText(dt,C,K+2)}g.textAlign="right",g.textBaseline="middle";const ot=Math.ceil((h-(N-G))/$/et)*et,Ct=(h-G)/$;for(let m=ot;m<=Ct;m+=et){const C=h-m*$;if(C<G||C>N-G)continue;g.beginPath(),g.moveTo(E,C),g.lineTo(E+Re,C),g.stroke();const dt=Z?String(H(m)):Le(m);g.fillText(dt,E-2,C)}g.restore()}function qt(g){const e=Math.pow(10,Math.floor(Math.log10(g))),N=g/e;return(N<1.5?1:N<3.5?2:N<7.5?5:10)*e}function Ke(g,e,N,M,h,$,G){const _=[],ut=G;if(ut<1){const V=qt((e-g)*M/N);if(V>0){const K=Math.ceil(g/V)*V;for(let E=K;E<=e+V*1e-6;E+=V){if(E<=g||E>=e)continue;const z=(1-(Math.log10(E)-$)/G)*N;z>h&&z<N-h&&_.push(E)}}return _}const Z=ut<3?[1,2,5]:[1],pt=Math.floor(Math.log10(g)),xt=Math.ceil(Math.log10(e));for(let V=pt;V<=xt;V++)for(const K of Z){const E=K*Math.pow(10,V);if(E<=g||E>=e)continue;const z=(1-(Math.log10(E)-$)/G)*N;z>h&&z<N-h&&_.push(E)}return _}export{fmt,fmtColorbarAxis}from"../renderer/fmt.js";
@@ -3,7 +3,7 @@ import type { Die } from '../core/dies.js';
3
3
  import type { TestDef, MetadataFieldDef } from '../renderer/buildWaferMap.js';
4
4
  import type { StatsSeverity } from '../stats/types.js';
5
5
  export declare function logWmapVersionOnce(): void;
6
- export declare const WMAP_TOKEN_NAMES: readonly ["z", "icon", "icon-hover", "icon-active", "bg-hover", "bg-active", "separator", "surface", "border", "menu-hover", "menu-active", "panel-bg", "text-muted", "text", "text-strong", "warn-bg", "warn-border", "warn-text", "err-bg", "err-border", "err-text", "info-bg", "info-text", "selected", "finding-indicator", "canvas-bg"];
6
+ export declare const WMAP_TOKEN_NAMES: readonly ["z", "icon", "icon-hover", "icon-active", "bg-hover", "bg-active", "separator", "surface", "border", "menu-hover", "menu-active", "panel-bg", "text-muted", "text", "text-strong", "warn-bg", "warn-border", "warn-text", "err-bg", "err-border", "err-text", "info-bg", "info-text", "selected", "finding-indicator", "canvas-bg", "bar-fill", "bar-fill-muted"];
7
7
  export type WmapTokenName = (typeof WMAP_TOKEN_NAMES)[number];
8
8
  export declare const CLR: {
9
9
  icon: string;
@@ -30,6 +30,8 @@ export declare const CLR: {
30
30
  infoText: string;
31
31
  findingHighlight: string;
32
32
  findingIndicator: string;
33
+ barFill: string;
34
+ barFillMuted: string;
33
35
  };
34
36
  /**
35
37
  * Findings-severity → colour, shared by summaryPanel.ts (severity dots/borders
@@ -43,12 +45,19 @@ export declare const INLINE_TEST_LIMIT = 6;
43
45
  /** Above this many rows, a scrollable list (test cascade submenu, chart test picker) gets a filter box. */
44
46
  export declare const MENU_SEARCH_THRESHOLD = 8;
45
47
  export declare const DEFAULT_OVERLAY_Z = 6000;
46
- /** `z-index` value string for a base-level overlay (menus, modal backdrop). */
48
+ /** `z-index` value string for a base-level overlay (modal backdrop, a
49
+ * component's own persistent chrome — e.g. a per-card toolbar). */
47
50
  export declare const Z_BASE = "var(--wmap-z, 6000)";
48
- /** One above base — tooltips, submenus, the modal box above its backdrop. */
51
+ /** One above base — tooltips, the modal box above its backdrop. */
49
52
  export declare const Z_ABOVE = "calc(var(--wmap-z, 6000) + 1)";
50
- /** Two above base — controls that must sit above a maximized modal box. */
51
- export declare const Z_ABOVE2 = "calc(var(--wmap-z, 6000) + 2)";
53
+ /**
54
+ * Two above base — the dedicated layer every transient menu/dropdown/
55
+ * cascade-submenu in this library renders into. See `menuLayerFor` below for
56
+ * why this is a real DOM layer, not just a number to reference.
57
+ */
58
+ export declare const Z_MENU = "calc(var(--wmap-z, 6000) + 2)";
59
+ /** Three above base — controls that must sit above a maximized modal box. */
60
+ export declare const Z_ABOVE2 = "calc(var(--wmap-z, 6000) + 3)";
52
61
  /**
53
62
  * Apply a host-supplied `zIndex` to wmap's overlays by writing `--wmap-z` onto
54
63
  * `document.documentElement`. Returns a disposer that restores the previous
@@ -162,6 +171,7 @@ export declare function saveTextFile(text: string, filename: string, mimeType: s
162
171
  */
163
172
  export declare function nextFrame(fn: () => void, ownerWindow?: Window): void;
164
173
  export declare function overlayRootFor(anchor: Element): Element;
174
+ export declare function menuLayerFor(anchor: Element): HTMLElement;
165
175
  /** The shared tooltip element for `doc` (default: the host page's own
166
176
  * document), lazily created and appended to `doc.body`. */
167
177
  export declare function getTooltip(doc?: Document): HTMLDivElement;
@@ -265,7 +275,7 @@ export declare function metadataModeEntry(field: MetadataFieldDef): ModeEntry;
265
275
  * stopped from bubbling so typing (including Space/arrow keys) lands in the
266
276
  * input rather than triggering the host menu's own keyboard navigation.
267
277
  */
268
- export declare function makeMenuSearchBox(onFilter: (query: string) => void, placeholder?: string): HTMLInputElement;
278
+ export declare function makeMenuSearchBox(onFilter: (query: string) => void, placeholder?: string, ownerDocument?: Document): HTMLInputElement;
269
279
  /**
270
280
  * Build the plot-mode dropdown menu element.
271
281
  * Shared between renderWaferMap and renderWaferGallery.
@@ -305,7 +315,18 @@ export type CheckMenuRow = {
305
315
  } | {
306
316
  label: string;
307
317
  active: boolean;
318
+ /** Default true. Without `disabledHint` a disabled row is omitted entirely. */
308
319
  enabled?: boolean;
320
+ /**
321
+ * Reason this row is unavailable. Supplying it keeps the row visible but
322
+ * greyed, with the reason as its tooltip, instead of dropping it.
323
+ *
324
+ * Omitting a row is right when its subject doesn't exist (no reticle data
325
+ * ⇒ nothing to say about a reticle grid). It's wrong when the option is
326
+ * real but temporarily inapplicable — the row vanishing then reads as a
327
+ * missing feature, and the user has no way to learn the condition.
328
+ */
329
+ disabledHint?: string;
309
330
  onClick: (e: MouseEvent) => void;
310
331
  };
311
332
  /**
@@ -396,7 +417,18 @@ export declare function openReparentedModal(elements: HTMLElement[], opts?: Repa
396
417
  * hardBin/softBin restriction (which still changes the die colours, just from
397
418
  * a smaller scheme set).
398
419
  */
399
- export declare function makePaletteBtn(helpers: ToolbarHelpers, getPlotMode: () => PlotMode, getColorScheme: () => string, hasCustomColors: () => boolean, setColorScheme: (v: string) => void): {
420
+ /**
421
+ * Colour-scheme picker, plus the optional non-colour failure marking.
422
+ *
423
+ * `markFail` is supplied by hosts that support it. It sits in this menu rather
424
+ * than getting a button of its own because it answers the same question the
425
+ * schemes do — "how is this data encoded" — and keeping it here means one
426
+ * control rather than a new axis of settings.
427
+ */
428
+ export declare function makePaletteBtn(helpers: ToolbarHelpers, getPlotMode: () => PlotMode, getColorScheme: () => string, hasCustomColors: () => boolean, setColorScheme: (v: string) => void, markFail?: {
429
+ get: () => boolean;
430
+ set: (v: boolean) => void;
431
+ }): {
400
432
  btn: HTMLButtonElement;
401
433
  sync: () => void;
402
434
  };
@@ -430,10 +462,27 @@ export declare function makeLogScaleBtn(helpers: ToolbarHelpers, getOpts: () =>
430
462
  sync: () => void;
431
463
  };
432
464
  export type LegendPosition = 'default' | 'compact' | 'left' | 'top' | 'bottom' | 'floating';
465
+ /**
466
+ * Per-map legend control, shared by the single map and the gallery.
467
+ *
468
+ * `perMap` is supplied only by the gallery, which is the one context where a
469
+ * single lot-level legend can stand in for every card's — and where the width
470
+ * a per-card legend costs is multiplied by the card count. A single map has
471
+ * nothing to stand in for it, so it never gets the toggle.
472
+ *
473
+ * The menu mixes a position group with that toggle rather than being two
474
+ * controls, because "how does the legend appear" is one question to the user
475
+ * even though it is two options underneath.
476
+ */
433
477
  export declare function makeLegendStyleBtn(helpers: ToolbarHelpers, getOpts: () => {
434
478
  plotMode?: PlotMode;
435
479
  legendPosition?: LegendPosition;
436
- }, setLegendPosition: (v: LegendPosition) => void): {
480
+ }, setLegendPosition: (v: LegendPosition) => void, perMap?: {
481
+ get: () => boolean;
482
+ set: (v: boolean) => void;
483
+ /** Non-empty when the toggle cannot apply right now — shown as the reason. */
484
+ blockedReason: () => string | null;
485
+ }): {
437
486
  btn: HTMLButtonElement;
438
487
  sync: () => void;
439
488
  };