@wafertools/wafermap 0.24.3 → 0.26.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 (34) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/dist/packages/canvas-adapter/charts/capability.js +1 -1
  3. package/dist/packages/canvas-adapter/charts/chartShell.d.ts +16 -0
  4. package/dist/packages/canvas-adapter/charts/chartShell.js +1 -1
  5. package/dist/packages/canvas-adapter/charts/correlation.js +1 -1
  6. package/dist/packages/canvas-adapter/charts/histogram.js +1 -1
  7. package/dist/packages/canvas-adapter/charts/scatter.js +1 -1
  8. package/dist/packages/canvas-adapter/dieList.d.ts +17 -0
  9. package/dist/packages/canvas-adapter/dieList.js +7 -7
  10. package/dist/packages/canvas-adapter/icons.js +1 -1
  11. package/dist/packages/canvas-adapter/index.d.ts +4 -0
  12. package/dist/packages/canvas-adapter/index.js +1 -1
  13. package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +14 -1
  14. package/dist/packages/canvas-adapter/renderWaferGallery.js +6 -6
  15. package/dist/packages/canvas-adapter/renderWaferMap.d.ts +22 -0
  16. package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
  17. package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
  18. package/dist/packages/canvas-adapter/toCanvas.d.ts +11 -0
  19. package/dist/packages/canvas-adapter/toCanvas.js +1 -1
  20. package/dist/packages/canvas-adapter/toolbar.d.ts +108 -6
  21. package/dist/packages/canvas-adapter/toolbar.js +2 -2
  22. package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
  23. package/dist/packages/canvas-adapter/userGuideHtml.js +42 -4
  24. package/dist/packages/canvas-adapter/version.d.ts +2 -2
  25. package/dist/packages/canvas-adapter/version.js +1 -1
  26. package/dist/packages/renderer/buildView.d.ts +22 -0
  27. package/dist/packages/renderer/buildView.js +1 -1
  28. package/dist/packages/renderer/buildWaferMap.d.ts +5 -1
  29. package/dist/packages/renderer/buildWaferMap.js +1 -1
  30. package/dist/packages/stats/analyzeWaferLot.js +1 -1
  31. package/dist/packages/stats/renderSummaryReport.js +1 -1
  32. package/dist/packages/stats/reportHtml.d.ts +6 -4
  33. package/dist/packages/stats/types.d.ts +14 -1
  34. package/package.json +4 -4
@@ -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 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 w,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=w.panelBg,Y=`1px solid ${w.menuBorder}`,Nt="12px",I=w.label,z=w.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:w.warnBg,color:w.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:w.separator,margin:`${Nt} 0`,flexShrink:"0"})}function U(t,i,n=w.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?w.barFillMuted:n,g=l("div",{position:"relative",height:"9px",background:w.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:w.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:w.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:w.menuBg,border:`1px solid ${w.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=w.bgHover}),g.addEventListener("mouseleave",()=>{g.style.background=""}),g.addEventListener("focus",()=>{g.style.background=w.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 v=m;y={testNumber:r.testNumber,min:v.min,max:v.max,mean:v.mean,count:v.count,stddev:v.stddev,median:v.median,q1:v.q1,q3:v.q3}}else{const v=f.map(F=>F.testValues?.[r.testNumber]).filter(F=>F!==void 0&&isFinite(F));if(!v.length)continue;y={testNumber:r.testNumber,...zt(v)}}let b=null,S=0;if(r.limitLow!==void 0||r.limitHigh!==void 0){const v=s.get(r.testNumber);if(v)({yieldPercent:b,totalDies:S}=v);else{const F=f.map(x=>x.testValues?.[r.testNumber]).filter(x=>x!==void 0&&isFinite(x)),W=F.filter(x=>r.limitLow!==void 0&&x<r.limitLow||r.limitHigh!==void 0&&x>r.limitHigh).length;S=F.length,b=F.length>0?(F.length-W)/F.length*100:null}}g.push({entry:r,stats:y,specYieldPct:b,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:w.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(",")],b=S=>it(S,void 0,"engineering");for(const{entry:S,stats:v,specYieldPct:F,specN:W}of g){const x=[...m.map(M=>M.constant??""),S.name,S.unit??"",String(v.count),b(v.min),b(v.q1),b(v.median),b(v.mean),b(v.q3),b(v.max),b(v.stddev)];h&&x.push(S.limitLow!==void 0?b(S.limitLow):"",S.limitHigh!==void 0?b(S.limitHigh):"",F!==null?F.toFixed(1):"",S.limitLow!==void 0||S.limitHigh!==void 0?String(W):""),y.push(x.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 ${w.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 b=F=>it(F,r.unit,n),S=document.createElement("tr"),v=(F,W="right")=>{const x=l("td",{textAlign:W,padding:"4px 8px",borderBottom:`1px solid ${w.menuBorder}`,color:z,whiteSpace:"nowrap"},F);S.appendChild(x)};v(r.name,"left"),v(`${m.count}`),v(b(m.min)),v(b(m.q1)),v(b(m.median)),v(b(m.mean)),v(b(m.q3)),v(b(m.max)),v(b(m.stddev)),h&&(v(r.limitLow!==void 0?b(r.limitLow):"\u2014"),v(r.limitHigh!==void 0?b(r.limitHigh):"\u2014"),v(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:w.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 ${w.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 ${w.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 ${w.menuBorder}`,borderLeft:`3px solid ${G(u.severity)}`,background:C?w.bgActive:w.menuBg,borderRadius:"6px",padding:$?"6px 10px":"8px 10px",textAlign:"left",fontSize:$?"10px":"11px",fontWeight:C?"600":"400",color:w.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:w.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:w.iconHover},R);u.appendChild($);const k=l("button",{flexShrink:"0",border:"none",background:"none",fontSize:"10px",color:w.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:w.iconHover,padding:"20px 24px 16px",margin:"0",borderBottom:`1px solid ${w.menuBorder}`,flexShrink:"0"},R);C.contentWrap.appendChild(r);const m=l("div",{overflowY:"auto",padding:"16px 24px",flex:"1"}),D=(y,b)=>{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",{},b)),S};for(const y of g){m.appendChild(D(y.severity,y.comparison.left)),m.appendChild(l("div",{fontSize:"13px",color:w.iconHover,padding:"3px 0 3px 15px",marginBottom:"2px"},P(y.summary)));const b=t.filter(S=>y.relatedIds?.includes(S.id));for(const S of b)m.appendChild(l("div",{fontSize:"12px",color:w.icon,padding:"2px 0 2px 23px",marginBottom:"2px"},P(S.summary)))}for(const y of B){const[b,S]=y.key.split("\0");m.appendChild(D(y.worst,h(b,S)));for(const v of y.findings)m.appendChild(l("div",{fontSize:"13px",color:w.iconHover,padding:"3px 0 3px 15px",marginBottom:"2px"},ct(v,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:w.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 ${w.menuBorder}`,borderLeft:`3px solid ${G(u.severity)}`,background:r?w.bgActive:w.menuBg,borderRadius:"6px",padding:"8px 32px 8px 10px",textAlign:"left",fontSize:"11px",fontWeight:r?"600":"500",color:w.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 b=l("button",{position:"absolute",top:"50%",right:"8px",transform:"translateY(-50%)",border:"none",background:"none",fontSize:"10px",color:w.icon,cursor:"pointer",padding:"2px 4px",lineHeight:"1"},"\u25B8");b.type="button",b.title="Show supporting findings",b.setAttribute("aria-label","Show supporting findings"),b.setAttribute("aria-expanded","false"),b.addEventListener("click",S=>{S.stopPropagation(),y=!y,D.style.display=y?"block":"none",b.textContent=y?"\u25BE":"\u25B8";const v=y?"Hide supporting findings":"Show supporting findings";b.title=v,b.setAttribute("aria-label",v),b.setAttribute("aria-expanded",String(y))}),C.appendChild(b)}s.appendChild(C)}for(const u of B){L||s.appendChild(l("div",{height:"1px",background:w.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 ${w.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 ${w.menuBorder}`,borderRadius:"10px",background:h?w.bgActive:"none",opacity:h?"1":"0.5",color:w.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:w.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=[],b=p?.stats.isLotStack?p.stats:void 0,S=b?.lotSize!==void 0?{lotSize:b.lotSize,aggrMethod:Lt(b.aggregationMethod)}:void 0;y.push(buildMetadataInfoSection([{metadata:e.metadata??void 0}],S)),d&&f&&y.push(buildYieldSection(d,f,h));const v=o.some(M=>M.hbin!=null),F=o.some(M=>M.sbin!=null);v?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 x=!0;for(const M of y)M&&(x||t.appendChild(dt()),x=!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((x,M)=>({label:x?.label??`W${M+1}`,wafer:x?.wafer,dies:x?.dies,statsSummary:x?.statsSummary})),hbinDefs:d,sbinDefs:f,testDefs:c,passBins:s,ringCount:a}))}),k=[],C=[],r=[],m=new WeakMap;for(let x=0;x<o.length;x++){const M=o[x];if(!M){C.push([]);continue}M.wafer&&k.push(M.wafer);const V=M.dies??[],ht=M.label??`W${x+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(x=>!!x).map(x=>({metadata:x.wafer?.metadata}))),o.find(x=>x?.metadataFields?.length)?.metadataFields,L,R,{label:"Wafer",get:x=>m.get(x)})}):null,y=ft($,D);y&&t.appendChild(y);const b=r.some(x=>x.hbin!=null),S=r.some(x=>x.sbin!=null),v=o.map(x=>x?.statsSummary).filter(x=>!!x),F=[buildLotOverviewSection(e),buildMetadataInfoSection(o.filter(x=>x!==null).map(x=>({metadata:x.wafer?.metadata}))),buildPerWaferYieldSection(e,o,N),b?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,v,R):null,c?.length?buildLotFunctionalSection(r,c,v,R):null];g&&B&&E&&F.push(buildFindingsSectionWithFilter(e,g,T,B,E));let W=!0;for(const x of F)x&&(W||t.appendChild(dt()),W=!1,t.appendChild(x));t.appendChild(l("div",{height:"12px",flexShrink:"0"})),t.scrollTop=n}
1
+ import{isParametricTest as bt}from"../renderer/buildWaferMap.js";import{buildRingRegions as J,buildQuadrantRegions as tt,buildRegionYieldData as wt}from"../stats/regions.js";import{computeFunctionalYield as vt}from"../stats/analyzeWaferMap.js";import{renderSummaryReportHtml as yt,renderLotSummaryReportHtml as Ct}from"../stats/renderSummaryReport.js";import{buildFindingsNarrative as St}from"../stats/findingsNarrative.js";import{filterFindings as kt}from"../stats/filterFindings.js";import{buildFacetTable as Bt,prettyKey as et}from"../stats/facets.js";import{commonMetadata as nt}from"../stats/facets.js";import{resolveMetadataColumns as Lt}from"../stats/metadataColumns.js";import{getColorScheme as $t}from"../renderer/colorSchemes.js";import{buildWarningsBanner as Q,collectWarnings as it}from"./warnings.js";export{Q as buildWarningsBanner};import{fmt as ot,fmtAggregationMethod as Dt,plainBinTerms as Y}from"../renderer/fmt.js";import{getUniqueTestNumbers as Rt}from"../renderer/buildView.js";import{quantile as G}from"../stats/math.js";import{makeLabeledSelect as at}from"./charts/chartShell.js";import{CLR as b,sevColor as U,openModal as rt,openReportModal as st,saveTextFile as lt}from"./toolbar.js";import{buildDieListSection as Ft}from"./dieList.js";import{medianOfSorted as Nt}from"../core/utils.js";import{metadataDisplayValue as Tt}from"../core/metadata.js";const Et=b.panelBg,O=`1px solid ${b.menuBorder}`,Mt="12px",I=b.label,H=b.value,dt="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 A(t){return l("div",{fontSize:dt,fontWeight:"700",letterSpacing:"0.06em",textTransform:"uppercase",color:I,marginBottom:"6px"},t)}function pt(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:dt,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:b.warnBg,color:b.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 ct(){return l("div",{height:"1px",background:b.separator,margin:`${Mt} 0`,flexShrink:"0"})}function X(t,i,n=b.barFill,e,o,d){const f=l("div",{marginBottom:"5px"}),c=l("div",{display:"flex",justifyContent:"space-between",fontSize:"11px",color:H,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)),g=d?b.barFillMuted:n,h=l("div",{position:"relative",height:"9px",background:b.bgActive,borderRadius:"4px",overflow:"hidden"}),D=l("div",{height:"100%",width:`${p}%`,background:g,borderRadius:"4px",transition:"width 0.3s ease"});if(h.appendChild(D),o!==void 0){const M=l("div",{position:"absolute",top:"0",bottom:"0",left:`${o}%`,width:"1px",background:b.infoText,opacity:"0.5"});h.appendChild(M)}return f.appendChild(c),f.appendChild(h),f}function j(t,i,n){const e=l("div",{background:b.menuBg,border:O,borderRadius:"6px",padding:"8px 10px",textAlign:"center",flex:"1"}),o=l("div",{fontSize:"20px",fontWeight:"700",color:H,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 Wt(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:H,textAlign:"right",fontWeight:"500",wordBreak:"break-all"},i);return n.appendChild(e),n.appendChild(o),n}function It(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:H,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},`${et(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=Bt(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 zt={display:"flex",flexDirection:"column",gap:"6px",background:b.menuBg,border:`1px solid ${b.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",zt);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(It(et(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(A("Wafer Info")),e.appendChild(n),e}export function buildYieldSection(t,i,n=[1]){const e=l("div");e.appendChild(A("Summary"));const o=l("div",{display:"flex",gap:"6px",marginBottom:"8px"});if(o.appendChild(j(String(t.totalDies),"Total dies")),t.partialDies>0&&o.appendChild(j(String(t.partialDies),"Partial")),t.yieldPercent!==null){const d=n.length===1?`bin ${n[0]}`:`bins ${n.join(", ")}`;o.appendChild(j(`${t.yieldPercent.toFixed(1)}%`,"Yield",`pass: ${d}`))}return e.appendChild(o),t.edgeExcludedDies>0&&e.appendChild(Wt("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,g]of Object.entries(o))d.set(Number(p),g);else for(const p of t){if(p.partial||p.edgeExcluded)continue;const g=n==="hard"?p.hbin:p.sbin;g!=null&&d.set(g,(d.get(g)??0)+1)}if(!d.size)return null;const f=[...d.values()].reduce((p,g)=>p+g,0),c=i?new Map(i.map(p=>[p.bin,p])):null,s=[...d.entries()].sort((p,g)=>p[0]-g[0]),a=l("div");a.appendChild(A(n==="hard"?"Hard Bin Breakdown":"Soft Bin Breakdown"));for(const[p,g]of s){const h=c?.get(p),D=h?.name?`Bin ${p} \xB7 ${h.name}`:`Bin ${p}`,M=g/f*100,B=$t(e),T=(e==="custom"?h?.color:void 0)??B.forBin(p);a.appendChild(X(`${D} (${g})`,M,T))}return a}export function buildLotBinSection(t,i,n,e){return buildBinSection(t,i,n,e)}function q(t,i,n,e,o,d){const f=wt(t,i,n,e,o);if(!f.length)return null;const c=l("div");c.appendChild(A(d));for(const{label:s,n:a,yieldPercent:p}of f)c.appendChild(X(`${s} (N=${a})`,p));return c}export function buildRingSection(t,i,n,e){return q([t],[i],n,e,J,"Ring Yield")}export function buildQuadrantSection(t,i,n,e){return q([t],[i],n,e,tt,"Quadrant Yield")}export function buildLotRingSection(t,i,n,e){return q(t,i,n,e,J,"Ring Yield")}export function buildLotQuadrantSection(t,i,n,e){return q(t,i,n,e,tt,"Quadrant Yield")}export function buildLotOverviewSection(t){const i=l("div");i.appendChild(A("Lot Summary"));const n=l("div",{display:"flex",gap:"6px",marginBottom:"8px"});n.appendChild(j(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(j(`${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=Nt([...e.map(a=>a.yieldPct)].sort((a,p)=>a-p)),s=l("div");s.appendChild(A("Wafer Yield"+f));for(const{waferIndex:a,label:p,yieldPct:g}of e){const h=X(p,g,void 0,void 0,c,g<c);n&&(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}, ${g.toFixed(1)}% yield \u2014 view wafer`),h.tabIndex=0,h.addEventListener("mouseenter",()=>{h.style.background=b.bgHover}),h.addEventListener("mouseleave",()=>{h.style.background=""}),h.addEventListener("focus",()=>{h.style.background=b.bgHover}),h.addEventListener("blur",()=>{h.style.background=""}),h.addEventListener("click",()=>n(a)),h.addEventListener("keydown",D=>{(D.key==="Enter"||D.key===" ")&&(D.preventDefault(),n(a))})),s.appendChild(h)}return s}function Ht(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:G(i,.5),q1:G(i,.25),q3:G(i,.75)}}function ut(t,i){if(!t)return[];const n=t.waferMetadata??(t.perWaferMetadata?nt(t.perWaferMetadata.map(e=>({metadata:e}))):void 0);return!n||!Object.keys(n).length?[]:Lt({waferMetadata:n,waferPlacement:"csv",reservedLabels:i}).columns}const At=/^[=+\-@]/;export function csvField(t){const i=At.test(t)&&Number.isNaN(Number(t))?`'${t}`:t;return/[",\n\r]/.test(i)?`"${i.replace(/"/g,'""')}"`:i}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(bt).map(r=>({testNumber:r.testNumber,name:r.name,unit:r.unit,limitLow:r.limitLow,limitHigh:r.limitHigh})):a=Rt(f).map(m=>({testNumber:m,name:`Test ${m}`})),!a.length)return null;const p=a.filter(r=>f.some(m=>{const $=m.testValues?.[r.testNumber];return $!==void 0&&isFinite($)}));if(!p.length)return null;const g=p.some(r=>r.limitLow!==void 0||r.limitHigh!==void 0),h=[];for(const r of p){const m=c.get(r.testNumber),$=m&&m.stddev!==void 0&&m.median!==void 0&&m.q1!==void 0&&m.q3!==void 0;let v;if($){const w=m;v={testNumber:r.testNumber,min:w.min,max:w.max,mean:w.mean,count:w.count,stddev:w.stddev,median:w.median,q1:w.q1,q3:w.q3}}else{const w=f.map(N=>N.testValues?.[r.testNumber]).filter(N=>N!==void 0&&isFinite(N));if(!w.length)continue;v={testNumber:r.testNumber,...Ht(w)}}let x=null,S=0;if(r.limitLow!==void 0||r.limitHigh!==void 0){const w=s.get(r.testNumber);if(w)({yieldPercent:x,totalDies:S}=w);else{const N=f.map(E=>E.testValues?.[r.testNumber]).filter(E=>E!==void 0&&isFinite(E)),W=N.filter(E=>r.limitLow!==void 0&&E<r.limitLow||r.limitHigh!==void 0&&E>r.limitHigh).length;S=N.length,x=N.length>0?(N.length-W)/N.length*100:null}}h.push({entry:r,stats:v,specYieldPct:x,specN:S})}if(!h.length)return null;const D=l("div"),M=l("div",{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:"6px"}),B=A(d?.populationLabel?`Test Values (${h.length}) \u2014 ${d.populationLabel}`:`Test Values (${h.length})`);B.style.marginBottom="0",M.appendChild(B);const T=document.createElement("button");T.type="button",T.textContent="Export CSV",Object.assign(T.style,{background:"none",border:O,borderRadius:"4px",cursor:"pointer",fontSize:"10px",color:b.iconHover,padding:"2px 7px"}),T.addEventListener("click",()=>{const r=["Test","Unit","N","Min","Q1","Median","Mean","Q3","Max","StdDev"];g&&r.push("LSL","USL","Spec Yield %","Spec Yield N");const m=ut(d,r),v=[[...m.map(S=>S.label),...r].map(csvField).join(",")],x=S=>ot(S,void 0,"engineering");for(const{entry:S,stats:w,specYieldPct:N,specN:W}of h){const E=[...m.map(y=>y.constant??""),S.name,S.unit??"",String(w.count),x(w.min),x(w.q1),x(w.median),x(w.mean),x(w.q3),x(w.max),x(w.stddev)];g&&E.push(S.limitLow!==void 0?x(S.limitLow):"",S.limitHigh!==void 0?x(S.limitHigh):"",N!==null?N.toFixed(1):"",S.limitLow!==void 0||S.limitHigh!==void 0?String(W):""),v.push(E.map(csvField).join(","))}lt(v.join(`
2
+ `),"test-values.csv","text/csv",o)}),M.appendChild(T),D.appendChild(M);const F=l("table",{width:"100%",borderCollapse:"collapse",fontSize:"11px"}),R=document.createElement("thead"),u=document.createElement("tr"),L=["Test","N","Min","Q1","Median","Mean","Q3","Max","StdDev"];g&&L.push("LSL","USL","Spec yield");for(const r of L){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 ${b.menuBorder}`,whiteSpace:"nowrap"},r);u.appendChild(m)}R.appendChild(u),F.appendChild(R);const k=document.createElement("tbody");for(const{entry:r,stats:m,specYieldPct:$,specN:v}of h){const x=N=>ot(N,r.unit,n),S=document.createElement("tr"),w=(N,W="right")=>{const E=l("td",{textAlign:W,padding:"4px 8px",borderBottom:`1px solid ${b.menuBorder}`,color:H,whiteSpace:"nowrap"},N);S.appendChild(E)};w(r.name,"left"),w(`${m.count}`),w(x(m.min)),w(x(m.q1)),w(x(m.median)),w(x(m.mean)),w(x(m.q3)),w(x(m.max)),w(x(m.stddev)),g&&(w(r.limitLow!==void 0?x(r.limitLow):"\u2014"),w(r.limitHigh!==void 0?x(r.limitHigh):"\u2014"),w($!==null?`${$.toFixed(1)}% (N=${v})`:"\u2014")),k.appendChild(S)}F.appendChild(k);const C=l("div",{overflowX:"auto"});return C.appendChild(F),D.appendChild(C),D}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=A(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:O,borderRadius:"4px",cursor:"pointer",fontSize:"10px",color:b.iconHover,padding:"2px 7px"}),a.addEventListener("click",()=>{const B=["Test","N","Pass","Fail","Pass Rate %"],T=ut(o,B),F=[[...T.map(R=>R.label),...B].map(csvField).join(",")];for(const R of d)F.push([...T.map(u=>u.constant??""),R.label,String(R.totalDies),String(R.passDies),String(R.failDies),R.passRatePercent!==null?R.passRatePercent.toFixed(1):""].map(csvField).join(","));lt(F.join(`
3
+ `),"functional-tests.csv","text/csv",e)}),c.appendChild(a),f.appendChild(c);const p=l("table",{width:"100%",borderCollapse:"collapse",fontSize:"11px"}),g=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:I,padding:"3px 8px 5px",borderBottom:`1px solid ${b.menuBorder}`,whiteSpace:"nowrap"},B));g.appendChild(h),p.appendChild(g);const D=document.createElement("tbody");for(const B of d){const T=document.createElement("tr"),F=(R,u="right")=>{T.appendChild(l("td",{textAlign:u,padding:"4px 8px",borderBottom:`1px solid ${b.menuBorder}`,color:H,whiteSpace:"nowrap"},R))};F(B.label,"left"),F(String(B.totalDies)),F(String(B.passDies)),F(String(B.failDies)),F(B.passRatePercent!==null?`${B.passRatePercent.toFixed(1)}% (N=${B.totalDies})`:"\u2014"),D.appendChild(T)}p.appendChild(D);const M=l("div",{overflowX:"auto"});return M.appendChild(p),f.appendChild(M),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 P={unusual:0,notable:1,info:2};function K(t){return l("span",{display:"inline-block",width:"7px",height:"7px",borderRadius:"50%",background:U(t),flexShrink:"0"})}function ft(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=Y(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}})():pt(`Findings (${t.length})`,d,f);function a(u){return u.reduce((L,k)=>P[k.severity]<P[L]?k.severity:L,"info")}function p(u){const L=new Map;for(const C of u){const r=`${C.comparison.family}\0${C.comparison.left}`,m=L.get(r)??[];m.push(C),L.set(r,m)}const k=[...L.entries()].map(([C,r])=>{const m=[...r].sort(($,v)=>P[$.severity]-P[v.severity]);return{key:C,findings:m,worst:a(m)}});return k.sort((C,r)=>P[C.worst]-P[r.worst]),k}function g(u,L){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 L.toLowerCase().startsWith(C.toLowerCase())?L:`${C}: ${L}`}const h=t.filter(u=>u.comparison.family==="spatial-pattern"),D=new Set([...h.flatMap(u=>u.relatedIds??[]),...t.flatMap(u=>u.absorbedIds??[])]),M=t.filter(u=>u.comparison.family!=="spatial-pattern"&&!D.has(u.id)),B=p(M);function T(u,L=!1,k){const C=e===u.id,r=document.createElement("button");return r.type="button",r.dataset.wmapFinding=u.id,r.textContent=ft(u,k),r.title=u.summary,r.setAttribute("aria-current",C?"true":"false"),Object.assign(r.style,{border:`1px solid ${b.menuBorder}`,borderLeft:`3px solid ${U(u.severity)}`,background:C?b.bgActive:b.menuBg,borderRadius:"6px",padding:L?"6px 10px":"8px 10px",textAlign:"left",fontSize:L?"10px":"11px",fontWeight:C?"600":"400",color:b.iconHover,cursor:"pointer",width:"100%",marginBottom:"4px"}),r.addEventListener("click",()=>n(u,r)),r}const F=Y(St(t)??"");if(F){const u=l("div",{background:b.bgActive,borderRadius:"6px",padding:"8px 10px",marginBottom:"8px",display:"flex",gap:"8px",alignItems:"flex-start"}),L=l("span",{flex:"1",fontSize:"12px",lineHeight:"1.5",color:b.iconHover},F);u.appendChild(L);const k=l("button",{flexShrink:"0",border:"none",background:"none",fontSize:"10px",color:b.icon,cursor:"pointer",padding:"0",lineHeight:"1.5",whiteSpace:"nowrap"},"Detail \u25B8");k.type="button",k.addEventListener("click",()=>{const C=rt({title:"Findings Summary",onClose:()=>{},anchor:k}),r=l("p",{fontSize:"16px",lineHeight:"1.6",color:b.iconHover,padding:"20px 24px 16px",margin:"0",borderBottom:`1px solid ${b.menuBorder}`,flexShrink:"0"},F);C.contentWrap.appendChild(r);const m=l("div",{overflowY:"auto",padding:"16px 24px",flex:"1"}),$=(v,x)=>{const S=l("div",{display:"flex",alignItems:"center",gap:"7px",fontSize:"13px",fontWeight:"600",color:H,marginTop:"10px",marginBottom:"4px"});return S.appendChild(K(v)),S.appendChild(l("span",{},x)),S};for(const v of h){m.appendChild($(v.severity,v.comparison.left)),m.appendChild(l("div",{fontSize:"13px",color:b.iconHover,padding:"3px 0 3px 15px",marginBottom:"2px"},Y(v.summary)));const x=t.filter(S=>v.relatedIds?.includes(S.id));for(const S of x)m.appendChild(l("div",{fontSize:"12px",color:b.icon,padding:"2px 0 2px 23px",marginBottom:"2px"},Y(S.summary)))}for(const v of B){const[x,S]=v.key.split("\0");m.appendChild($(v.worst,g(x,S)));for(const w of v.findings)m.appendChild(l("div",{fontSize:"13px",color:b.iconHover,padding:"3px 0 3px 15px",marginBottom:"2px"},ft(w,S)))}C.contentWrap.appendChild(m),Object.assign(C.contentWrap.style,{flexDirection:"column",overflow:"hidden"})}),u.appendChild(k),s.appendChild(u)}let R=!0;for(const u of h){R||s.appendChild(l("div",{height:"1px",background:b.separator,margin:"4px 0"})),R=!1;const L=t.filter($=>u.relatedIds?.includes($.id)),k=L.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 ${b.menuBorder}`,borderLeft:`3px solid ${U(u.severity)}`,background:r?b.bgActive:b.menuBg,borderRadius:"6px",padding:"8px 32px 8px 10px",textAlign:"left",fontSize:"11px",fontWeight:r?"600":"500",color:b.iconHover,cursor:"pointer",width:"100%"}),m.textContent=Y(u.summary),m.title=u.summary,m.setAttribute("aria-current",r?"true":"false"),m.addEventListener("click",()=>n(u,m)),C.appendChild(m),k){const $=l("div",{display:"none",paddingLeft:"12px",marginBottom:"4px"});for(const S of L)$.appendChild(T(S,!0));C.appendChild($);let v=!1;const x=l("button",{position:"absolute",top:"50%",right:"8px",transform:"translateY(-50%)",border:"none",background:"none",fontSize:"10px",color:b.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(),v=!v,$.style.display=v?"block":"none",x.textContent=v?"\u25BE":"\u25B8";const w=v?"Hide supporting findings":"Show supporting findings";x.title=w,x.setAttribute("aria-label",w),x.setAttribute("aria-expanded",String(v))}),C.appendChild(x)}s.appendChild(C)}for(const u of B){R||s.appendChild(l("div",{height:"1px",background:b.separator,margin:"4px 0"})),R=!1;const[L,k]=u.key.split("\0"),C=l("div",{display:"flex",alignItems:"center",gap:"6px",fontSize:"11px",fontWeight:"600",color:H,margin:"2px 0 4px"});C.appendChild(K(u.worst)),C.appendChild(l("span",{},g(L,k))),s.appendChild(C);for(const r of u.findings)s.appendChild(T(r,!1,k))}return c}const Pt=[{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"}],Yt=[{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"}],Ot=["unusual","notable","info"],jt={unusual:"Unusual",notable:"Notable",info:"Info"};function qt(t,i,n){const e=l("div",{display:"flex",flexWrap:"wrap",alignItems:"center",gap:"10px",marginBottom:"10px",paddingBottom:"10px",borderBottom:`1px solid ${b.separator}`}),o={unusual:0,notable:0,info:0};for(const s of t)o[s.severity]++;const d=Ot.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 g=f.has(s);a.setAttribute("aria-pressed",g?"true":"false"),Object.assign(a.style,{display:"inline-flex",alignItems:"center",gap:"5px",border:`1px solid ${b.menuBorder}`,borderRadius:"10px",background:g?b.bgActive:"none",opacity:g?"1":"0.5",color:b.text,fontSize:"11px",padding:"2px 8px",cursor:"pointer"})};a.appendChild(K(s)),a.appendChild(l("span",{},`${jt[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(at("Kind:",Pt,i.kind??"",s=>{i.kind=s||void 0,n()},{maxWidth:"130px"})),e.appendChild(at("Region:",Yt,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(g=>g.severity==="unusual"||g.severity==="notable"),f=d?t.findings.filter(g=>g.severity!=="info").length.toString():void 0,{outer:c,content:s}=pt(`Findings (${t.findings.length})`,d,f);s.appendChild(qt(t.findings,e,o));const a=kt(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:O,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 mt(t){return l("div",{fontSize:"13px",fontWeight:"700",color:H,marginBottom:"10px"},t)}function V(t,i){const n=l("button",{background:"none",border:O,borderRadius:"4px",cursor:"pointer",fontSize:"10px",color:b.iconHover,padding:"2px 7px"},t);return n.type="button",n.addEventListener("click",i),n}function ht(...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 gt(t,i,n,e,o,d,f,c,s,a,p){const g=t.ownerDocument,h=rt({title:"Die list",onClose:()=>{},anchor:t,ownerDocument:g});h.contentWrap.style.padding="14px 16px";const D=Ft(i,n,{...f,title:e,onSaveText:c,waferMetadata:o,metadataFields:d,extraColumn:s,getWafer:a,ringCount:p,ownerDocument:g});D&&h.contentWrap.appendChild(D)}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:g=[1],ringCount:h=4,colorScheme:D,fallbackFormat:M,onFindingClick:B,activeFindingId:T=null,findingsFilter:F,onFindingsFilterChange:R,onSaveText:u,metadataFields:L,dieListOptions:k}=i;t.appendChild(mt("Wafer Summary"));const C=i.warnings??it({statsSummary:p});C.length&&t.appendChild(Q(C,t.ownerDocument));const r=d&&f?V("Summary report",()=>{st(yt({wafer:e,dies:o,yieldSummary:d,dataCoverage:f,hbinDefs:c,sbinDefs:s,testDefs:a,statsSummary:p,passBins:g,ringCount:h}),{anchor:t})}):null,m=(k?.enabled??!0)&&o.length?V("View die list",()=>{gt(t,o,a,`Die list \u2014 ${o.length} dies`,e.metadata,L,k,u,void 0,()=>e,h)}):null,$=ht(r,m);$&&t.appendChild($);const v=[],x=p?.stats.isLotStack?p.stats:void 0,S=x?.lotSize!==void 0?{lotSize:x.lotSize,aggrMethod:Dt(x.aggregationMethod)}:void 0;v.push(buildMetadataInfoSection([{metadata:e.metadata??void 0}],S)),d&&f&&v.push(buildYieldSection(d,f,g));const w=o.some(y=>y.hbin!=null),N=o.some(y=>y.sbin!=null);w?v.push(buildBinSection(o,c,"hard",D,p?.stats.hardBinCounts)):N&&v.push(buildBinSection(o,s,"soft",D,p?.stats.softBinCounts)),v.push(buildRingSection(o,e,h,g)),v.push(buildQuadrantSection(o,e,h,g));const W=e.metadata?{waferMetadata:e.metadata}:void 0;v.push(buildTestSection(o,a,M,p?.stats,u,W)),v.push(buildFunctionalTestSection(o,a,p?.stats.functionalYield,u,W)),p&&B&&F&&R&&v.push(buildFindingsSectionWithFilter(p,B,T,F,R));let E=!0;for(const y of v)y&&(E||t.appendChild(ct()),E=!1,t.appendChild(y));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:g,onFindingClick:h,activeFindingId:D=null,onWaferClick:M,findingsFilter:B,onFindingsFilterChange:T,onSaveText:F,dieListOptions:R}=i;t.appendChild(mt(`Lot Summary \u2014 ${e.stats.waferCount} wafer${e.stats.waferCount===1?"":"s"}`));const u=i.warnings??it({lotStatsSummary:e});u.length&&t.appendChild(Q(u,t.ownerDocument));const L=V("Summary report",()=>{st(Ct({items:o.map((y,z)=>({label:y?.label??`W${z+1}`,wafer:y?.wafer,dies:y?.dies,statsSummary:y?.statsSummary})),hbinDefs:d,sbinDefs:f,testDefs:c,passBins:s,ringCount:a}),{anchor:t})}),k=[],C=[],r=[],m=new WeakMap,$=new WeakMap;for(let y=0;y<o.length;y++){const z=o[y];if(!z){C.push([]);continue}z.wafer&&k.push(z.wafer);const _=z.dies??[],xt=z.label??`W${y+1}`;for(const Z of _)m.set(Z,xt),z.wafer&&$.set(Z,z.wafer);C.push(_),r.push(..._)}const v=R?.enabled??!0?V("View die list",()=>{r.length&&gt(t,r,c,`Die list \u2014 ${r.length} dies across ${o.length} wafer${o.length===1?"":"s"}`,nt(o.filter(y=>!!y).map(y=>({metadata:y.wafer?.metadata}))),o.find(y=>y?.metadataFields?.length)?.metadataFields,R,F,{label:"Wafer",get:y=>m.get(y)},y=>$.get(y),a)}):null,x=ht(L,v);x&&t.appendChild(x);const S=r.some(y=>y.hbin!=null),w=r.some(y=>y.sbin!=null),N=o.map(y=>y?.statsSummary).filter(y=>!!y),W=[buildLotOverviewSection(e),buildPerWaferYieldSection(e,o,M),S?buildLotBinSection(r,d,"hard",p):w?buildLotBinSection(r,f,"soft",p):null,buildLotRingSection(C,k,a,s),buildLotQuadrantSection(C,k,a,s),c?.length?buildLotTestSection(r,c,g,N,F):null,c?.length?buildLotFunctionalSection(r,c,N,F):null];h&&B&&T&&W.push(buildFindingsSectionWithFilter(e,h,D,B,T));let E=!0;for(const y of W)y&&(E||t.appendChild(ct()),E=!1,t.appendChild(y));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";
@@ -45,12 +45,19 @@ export declare const INLINE_TEST_LIMIT = 6;
45
45
  /** Above this many rows, a scrollable list (test cascade submenu, chart test picker) gets a filter box. */
46
46
  export declare const MENU_SEARCH_THRESHOLD = 8;
47
47
  export declare const DEFAULT_OVERLAY_Z = 6000;
48
- /** `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). */
49
50
  export declare const Z_BASE = "var(--wmap-z, 6000)";
50
- /** One above base — tooltips, submenus, the modal box above its backdrop. */
51
+ /** One above base — tooltips, the modal box above its backdrop. */
51
52
  export declare const Z_ABOVE = "calc(var(--wmap-z, 6000) + 1)";
52
- /** Two above base — controls that must sit above a maximized modal box. */
53
- 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)";
54
61
  /**
55
62
  * Apply a host-supplied `zIndex` to wmap's overlays by writing `--wmap-z` onto
56
63
  * `document.documentElement`. Returns a disposer that restores the previous
@@ -164,6 +171,7 @@ export declare function saveTextFile(text: string, filename: string, mimeType: s
164
171
  */
165
172
  export declare function nextFrame(fn: () => void, ownerWindow?: Window): void;
166
173
  export declare function overlayRootFor(anchor: Element): Element;
174
+ export declare function menuLayerFor(anchor: Element): HTMLElement;
167
175
  /** The shared tooltip element for `doc` (default: the host page's own
168
176
  * document), lazily created and appended to `doc.body`. */
169
177
  export declare function getTooltip(doc?: Document): HTMLDivElement;
@@ -307,7 +315,18 @@ export type CheckMenuRow = {
307
315
  } | {
308
316
  label: string;
309
317
  active: boolean;
318
+ /** Default true. Without `disabledHint` a disabled row is omitted entirely. */
310
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;
311
330
  onClick: (e: MouseEvent) => void;
312
331
  };
313
332
  /**
@@ -355,6 +374,22 @@ export interface OverlayOptions {
355
374
  * overlay then falls back to `doc.body`, matching prior behaviour exactly.
356
375
  */
357
376
  anchor?: Element;
377
+ /**
378
+ * Adds a "Print / Save as PDF" header button that calls the host page's
379
+ * native `window.print()` — identical behaviour in a plain browser tab and
380
+ * inside a Tauri/Electron/WebView2 webview, since both just hand the
381
+ * rendered DOM to the OS print dialog. No host wiring required: this
382
+ * button is purely a discoverability affordance for the `@media print`
383
+ * stylesheet already injected below, which makes ANY open overlay print
384
+ * cleanly (just this box's content, not the rest of the page) whether the
385
+ * user finds Ctrl+P/Cmd+P themselves or clicks this button.
386
+ *
387
+ * `true` calls the overlay's own `window.print()`. Pass a function instead
388
+ * when the overlay's real content lives in a child document the sibling-
389
+ * hiding print stylesheet doesn't reach — e.g. `openReportModal`'s iframe,
390
+ * where printing must target `iframe.contentWindow.print()`.
391
+ */
392
+ printable?: boolean | (() => void);
358
393
  }
359
394
  export interface OverlayHandle {
360
395
  /** null in 'window' mode — there is no backdrop to block the page. */
@@ -398,7 +433,18 @@ export declare function openReparentedModal(elements: HTMLElement[], opts?: Repa
398
433
  * hardBin/softBin restriction (which still changes the die colours, just from
399
434
  * a smaller scheme set).
400
435
  */
401
- export declare function makePaletteBtn(helpers: ToolbarHelpers, getPlotMode: () => PlotMode, getColorScheme: () => string, hasCustomColors: () => boolean, setColorScheme: (v: string) => void): {
436
+ /**
437
+ * Colour-scheme picker, plus the optional non-colour failure marking.
438
+ *
439
+ * `markFail` is supplied by hosts that support it. It sits in this menu rather
440
+ * than getting a button of its own because it answers the same question the
441
+ * schemes do — "how is this data encoded" — and keeping it here means one
442
+ * control rather than a new axis of settings.
443
+ */
444
+ export declare function makePaletteBtn(helpers: ToolbarHelpers, getPlotMode: () => PlotMode, getColorScheme: () => string, hasCustomColors: () => boolean, setColorScheme: (v: string) => void, markFail?: {
445
+ get: () => boolean;
446
+ set: (v: boolean) => void;
447
+ }): {
402
448
  btn: HTMLButtonElement;
403
449
  sync: () => void;
404
450
  };
@@ -432,10 +478,27 @@ export declare function makeLogScaleBtn(helpers: ToolbarHelpers, getOpts: () =>
432
478
  sync: () => void;
433
479
  };
434
480
  export type LegendPosition = 'default' | 'compact' | 'left' | 'top' | 'bottom' | 'floating';
481
+ /**
482
+ * Per-map legend control, shared by the single map and the gallery.
483
+ *
484
+ * `perMap` is supplied only by the gallery, which is the one context where a
485
+ * single lot-level legend can stand in for every card's — and where the width
486
+ * a per-card legend costs is multiplied by the card count. A single map has
487
+ * nothing to stand in for it, so it never gets the toggle.
488
+ *
489
+ * The menu mixes a position group with that toggle rather than being two
490
+ * controls, because "how does the legend appear" is one question to the user
491
+ * even though it is two options underneath.
492
+ */
435
493
  export declare function makeLegendStyleBtn(helpers: ToolbarHelpers, getOpts: () => {
436
494
  plotMode?: PlotMode;
437
495
  legendPosition?: LegendPosition;
438
- }, setLegendPosition: (v: LegendPosition) => void): {
496
+ }, setLegendPosition: (v: LegendPosition) => void, perMap?: {
497
+ get: () => boolean;
498
+ set: (v: boolean) => void;
499
+ /** Non-empty when the toggle cannot apply right now — shown as the reason. */
500
+ blockedReason: () => string | null;
501
+ }): {
439
502
  btn: HTMLButtonElement;
440
503
  sync: () => void;
441
504
  };
@@ -497,5 +560,44 @@ export interface UserGuideExtension {
497
560
  * documentation.
498
561
  */
499
562
  export declare function openUserGuideWindow(api: GuideApi, html: string, extension?: UserGuideExtension, anchor?: Element): void;
563
+ /**
564
+ * Opens wmap's own guide window with no live `WaferMapController`/
565
+ * `GalleryController` required — `WaferMapController.openUserGuide()`/
566
+ * `GalleryController.openUserGuide()` are thin wrappers around this exact
567
+ * same call (see their own `openGuideWindow` closures in `renderWaferMap.ts`/
568
+ * `renderWaferGallery.ts`), built from the library's own top-level functions
569
+ * rather than anything derived from a specific render — so a host with
570
+ * nothing rendered yet (e.g. an empty-state "Help" menu, before any file is
571
+ * loaded) can reach the same guide, with the same `extension` content, the
572
+ * same way. Prefer the controller method when one exists — this is for the
573
+ * gap it can't cover.
574
+ *
575
+ * `renderWaferMap`/`renderWaferGallery` are dynamically imported (like
576
+ * `USER_GUIDE_HTML` below) since `toolbar.ts` is itself imported BY both of
577
+ * them — a static import here would cycle back.
578
+ */
579
+ export declare function openWaferMapGuide(extension?: UserGuideExtension, anchor?: Element): void;
580
+ /**
581
+ * Opens report HTML (from `renderSummaryReportHtml`/`renderLotSummaryReportHtml`)
582
+ * in an in-app modal — no host-registered `setReportOpener` required just to
583
+ * *view* a report. `setReportOpener`/`openHtmlReport` remain available as an
584
+ * "Open as full page" action inside this modal (its header's print button, plus
585
+ * a link in the toolbar strip below it), for a host that wants the report as a
586
+ * real separate page — to keep it open outside this modal, or as a fallback if
587
+ * in-app printing misbehaves on a given platform.
588
+ *
589
+ * The report HTML is a complete standalone document (its own `<style>`, its own
590
+ * `<!DOCTYPE html>`) — rendered via `<iframe srcdoc>` rather than parsed apart
591
+ * and inserted into the modal's own DOM (the way the guide's `buildGuideContent`
592
+ * does it). This keeps the report's stylesheet naturally isolated from the host
593
+ * page with no parsing, and lets `iframe.contentWindow.print()` scope printing
594
+ * to just the report — `openOverlay`'s sibling-hiding print stylesheet (built
595
+ * for content that lives directly in the host document, like the guide) isn't
596
+ * needed here and wouldn't reach into the iframe's own document anyway.
597
+ */
598
+ export declare function openReportModal(html: string, opts?: {
599
+ anchor?: Element;
600
+ ownerDocument?: Document;
601
+ }): OverlayHandle;
500
602
  export {};
501
603
  //# sourceMappingURL=toolbar.d.ts.map