@wafertools/wafermap 0.30.2 → 0.30.4

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 (88) hide show
  1. package/AGENTS.md +16 -5
  2. package/CHANGELOG.md +432 -0
  3. package/README.md +6 -3
  4. package/dist/packages/canvas-adapter/chartPopulation.d.ts +48 -0
  5. package/dist/packages/canvas-adapter/chartPopulation.js +1 -0
  6. package/dist/packages/canvas-adapter/charts/barPanel.d.ts +4 -1
  7. package/dist/packages/canvas-adapter/charts/barPanel.js +1 -1
  8. package/dist/packages/canvas-adapter/charts/boxplot.d.ts +4 -1
  9. package/dist/packages/canvas-adapter/charts/boxplot.js +1 -1
  10. package/dist/packages/canvas-adapter/charts/capability.js +1 -1
  11. package/dist/packages/canvas-adapter/charts/chartShell.d.ts +82 -8
  12. package/dist/packages/canvas-adapter/charts/chartShell.js +1 -1
  13. package/dist/packages/canvas-adapter/charts/correlation.js +2 -2
  14. package/dist/packages/canvas-adapter/charts/groupedBarPlot.js +1 -1
  15. package/dist/packages/canvas-adapter/charts/histogram.js +1 -1
  16. package/dist/packages/canvas-adapter/charts/regionYieldDiagram.js +1 -1
  17. package/dist/packages/canvas-adapter/charts/scatter.d.ts +5 -1
  18. package/dist/packages/canvas-adapter/charts/scatter.js +1 -1
  19. package/dist/packages/canvas-adapter/charts/sweep.d.ts +31 -0
  20. package/dist/packages/canvas-adapter/charts/sweep.js +1 -0
  21. package/dist/packages/canvas-adapter/charts/testPassRate.js +1 -1
  22. package/dist/packages/canvas-adapter/charts/trend.d.ts +3 -1
  23. package/dist/packages/canvas-adapter/charts/trend.js +1 -1
  24. package/dist/packages/canvas-adapter/dieList.js +2 -2
  25. package/dist/packages/canvas-adapter/drilldown.d.ts +23 -0
  26. package/dist/packages/canvas-adapter/drilldown.js +1 -0
  27. package/dist/packages/canvas-adapter/icons.js +1 -1
  28. package/dist/packages/canvas-adapter/insightsTab.d.ts +65 -3
  29. package/dist/packages/canvas-adapter/insightsTab.js +1 -1
  30. package/dist/packages/canvas-adapter/maplessSummary.js +1 -1
  31. package/dist/packages/canvas-adapter/renderWaferGallery.js +1 -1
  32. package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
  33. package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
  34. package/dist/packages/canvas-adapter/toCanvas.js +1 -1
  35. package/dist/packages/canvas-adapter/toolbar.d.ts +30 -2
  36. package/dist/packages/canvas-adapter/toolbar.js +2 -2
  37. package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
  38. package/dist/packages/canvas-adapter/userGuideHtml.js +162 -37
  39. package/dist/packages/canvas-adapter/version.d.ts +2 -2
  40. package/dist/packages/canvas-adapter/version.js +1 -1
  41. package/dist/packages/canvas-adapter/warnings.js +1 -1
  42. package/dist/packages/core/dies.d.ts +1 -1
  43. package/dist/packages/renderer/axisTicks.d.ts +32 -0
  44. package/dist/packages/renderer/axisTicks.js +1 -0
  45. package/dist/packages/renderer/binColors.js +1 -1
  46. package/dist/packages/renderer/buildView.d.ts +11 -37
  47. package/dist/packages/renderer/buildView.js +1 -1
  48. package/dist/packages/renderer/buildWaferMap.d.ts +97 -12
  49. package/dist/packages/renderer/buildWaferMap.js +1 -1
  50. package/dist/packages/renderer/deprecate.d.ts +13 -5
  51. package/dist/packages/renderer/deprecate.js +1 -1
  52. package/dist/packages/renderer/derivedTests/apply.d.ts +85 -0
  53. package/dist/packages/renderer/derivedTests/apply.js +1 -0
  54. package/dist/packages/renderer/derivedTests/evaluate.d.ts +48 -0
  55. package/dist/packages/renderer/derivedTests/evaluate.js +1 -0
  56. package/dist/packages/renderer/derivedTests/parser.d.ts +148 -0
  57. package/dist/packages/renderer/derivedTests/parser.js +2 -0
  58. package/dist/packages/renderer/fmt.d.ts +24 -2
  59. package/dist/packages/renderer/fmt.js +1 -1
  60. package/dist/packages/renderer/index.d.ts +1 -0
  61. package/dist/packages/renderer/index.js +1 -1
  62. package/dist/packages/renderer/spec.d.ts +66 -0
  63. package/dist/packages/renderer/spec.js +1 -0
  64. package/dist/packages/renderer/testLabel.d.ts +154 -0
  65. package/dist/packages/renderer/testLabel.js +1 -0
  66. package/dist/packages/stats/analyzeWaferMap.js +1 -1
  67. package/dist/packages/stats/capability.d.ts +8 -2
  68. package/dist/packages/stats/capability.js +1 -1
  69. package/dist/packages/stats/correlation.d.ts +4 -1
  70. package/dist/packages/stats/correlation.js +1 -1
  71. package/dist/packages/stats/deprecated.d.ts +4 -1
  72. package/dist/packages/stats/deprecated.js +1 -1
  73. package/dist/packages/stats/index.d.ts +2 -2
  74. package/dist/packages/stats/index.js +1 -1
  75. package/dist/packages/stats/mergeTestDefs.d.ts +1 -1
  76. package/dist/packages/stats/mergeTestDefs.js +1 -1
  77. package/dist/packages/stats/renderFindingsReport.js +8 -28
  78. package/dist/packages/stats/renderSummaryReport.js +19 -28
  79. package/dist/packages/stats/reportHtml.d.ts +27 -0
  80. package/dist/packages/stats/reportHtml.js +35 -14
  81. package/dist/packages/stats/sweep.d.ts +186 -0
  82. package/dist/packages/stats/sweep.js +1 -0
  83. package/dist/packages/stats/sweepXFromName.d.ts +27 -0
  84. package/dist/packages/stats/sweepXFromName.js +1 -0
  85. package/dist/packages/stats/testPassRate.js +1 -1
  86. package/dist/packages/stats/types.d.ts +37 -4
  87. package/llms.txt +3 -2
  88. package/package.json +1 -1
@@ -1,3 +1,3 @@
1
- import{waferDisplayLabel as ye}from"../core/waferLabel.js";import{itemPassBins as Ce,passBinsLabel as Je}from"../core/passBins.js";import{isParametricTest as et}from"../renderer/buildWaferMap.js";import{buildRingRegions as tt,buildQuadrantRegions as nt,buildRegionYieldData as ot}from"../stats/regions.js";import{computeFunctionalYield as it}from"../stats/analyzeWaferMap.js";import{renderWaferReportHtml as st,renderLotReportHtml as at}from"../stats/renderSummaryReport.js";import{buildFindingsNarrative as rt}from"../stats/findingsNarrative.js";import{filterFindings as lt}from"../stats/filterFindings.js";import{buildFacetTable as dt,prettyKey as ke}from"../stats/facets.js";import{commonMetadata as Le}from"../stats/facets.js";import{resolveMetadataColumns as ct}from"../stats/metadataColumns.js";import{resolveBinColors as Be,resolveBinColorsByWafer as pt}from"../renderer/binColors.js";import{NO_DATA_FILL as ut}from"../renderer/colorMap.js";import{describeWaferPopulation as Ne,populationLabel as ft}from"../stats/population.js";import{buildWarningsBanner as Se,collectWarnings as Te}from"./warnings.js";export{Se as buildWarningsBanner};import{fmt as Ee,fmtAggregationMethod as mt,plainBinTerms as le}from"../renderer/fmt.js";import{getUniqueTestNumbers as gt}from"../renderer/buildView.js";import{describeSorted as ht,quantile as Fe}from"../stats/math.js";import{pooledTestStatsSteps as bt}from"../stats/capability.js";import{sortBinsForDisplay as xt}from"../stats/binPareto.js";import{poolFunctionalYield as yt}from"../stats/testPassRate.js";import{makeLabeledSelect as De,makeSegmented as we}from"./charts/chartShell.js";import{SHADOW as Re,MOTION as Me,LEADING as oe,TRACKING as pe,wireControlHover as G,controlStyle as ue,SPACE as y,RADIUS as q,FONT as T,CLR as $,sevColor as fe,openModal as Ie,openReportModal as We,saveTextFile as Ae,wireTooltip as de}from"./toolbar.js";import{buildDieListSection as Ct}from"./dieList.js";export{csvField}from"../core/utils.js";import{csvField as me,drain as ge,maxOf as St,medianOfSorted as wt,minOf as vt}from"../core/utils.js";import{metadataDisplayValue as $t}from"../core/metadata.js";const kt=$.panelBg,Pe=`1px solid ${$.menuBorder}`,Lt="12px",_=$.label,ee=$.value,ze="10px";function l(e,t,o,n=document){const i=n.createElement(e);return t&&Object.assign(i.style,t),o!==void 0&&(i.textContent=o),i}function ae(e){return l("div",{fontSize:ze,fontWeight:"700",letterSpacing:pe,textTransform:"uppercase",color:_,marginBottom:y.sm},e)}const He=new WeakMap,Oe=["regionYield","testValues","functionalTests"];function he(e){if(!e)return{collapsed:new Set(Oe),regionMode:"ring",waferSort:"slot"};let t=He.get(e);return t||(t={collapsed:new Set(Oe),regionMode:"ring",waferSort:"slot"},He.set(e,t)),t}function ie(e,t=!0,o,n){const i=l("div"),c=he(n?.panel);n?.stateKey&&n.panel&&(t=!c.collapsed.has(n.stateKey));const s=document.createElement("button");s.type="button",Object.assign(s.style,{display:"flex",alignItems:"center",gap:y.xs,width:"100%",background:"none",border:"none",cursor:"pointer",padding:"0 0 6px",textAlign:"left"});const m=l("span",{fontSize:T.meta,color:_,transition:`transform ${Me.base}`,transform:t?"rotate(90deg)":"rotate(0deg)",display:"inline-block",lineHeight:oe.none,marginRight:"1px"},"\u25B6"),d=l("span",{fontSize:ze,fontWeight:"700",letterSpacing:pe,textTransform:"uppercase",color:_,flex:"1"},e);if(G(s,"bare"),s.setAttribute("aria-expanded",t?"true":"false"),s.appendChild(m),s.appendChild(d),o){const h=l("span",{fontSize:T.meta,fontWeight:"700",background:$.warnBg,color:$.warnText,borderRadius:q.pill,padding:"1px 5px"},o);s.appendChild(h)}const r=l("div");r.style.display=t?"block":"none";let a=t;if(s.addEventListener("click",()=>{a=!a,r.style.display=a?"block":"none",m.style.transform=a?"rotate(90deg)":"rotate(0deg)",s.setAttribute("aria-expanded",a?"true":"false"),n?.stateKey&&n.panel&&(a?c.collapsed.delete(n.stateKey):c.collapsed.add(n.stateKey))}),n?.control){const h=l("div",{display:"flex",alignItems:"center",gap:y.sm});s.style.width="auto",s.style.flex="1",h.appendChild(s);const u=()=>{r.innerHTML="",n.render?.(r)},w=n.control(u);w&&(w.style.marginBottom="6px",w.addEventListener("click",L=>L.stopPropagation()),h.appendChild(w)),i.appendChild(h)}else i.appendChild(s);return i.appendChild(r),n?.render?.(r),{outer:i,content:r}}function Ye(){return l("div",{height:"1px",background:$.separator,margin:`${Lt} 0`,flexShrink:"0"})}function ve(e,t,o=$.barFill,n,i){const c=l("div",{marginBottom:"5px"}),s=l("div",{display:"flex",justifyContent:"space-between",fontSize:T.body,color:ee,marginBottom:y.xxs}),m=l("span",{color:_},e),d=l("span",{fontWeight:"600"},`${t.toFixed(1)}%`);s.appendChild(m),s.appendChild(d);const r=n!==void 0?n:Math.min(100,Math.max(0,t)),a=o,h=l("div",{position:"relative",height:"9px",background:$.bgActive,borderRadius:q.control,overflow:"hidden"}),u=l("div",{height:"100%",width:`${r}%`,background:a,borderRadius:q.control,transition:`width ${Me.base}`});if(h.appendChild(u),i!==void 0){const w=l("div",{position:"absolute",top:"0",bottom:"0",left:`${i}%`,width:"2px",marginLeft:"-1px",background:$.value,opacity:"0.8"});h.appendChild(w)}return c.appendChild(s),c.appendChild(h),c}function _e(){const e=l("div",{display:"flex",gap:y.sm,marginBottom:y.md});return e.finish=()=>{const t=e.lastElementChild;t&&Object.assign(t.style,{borderRight:"none",paddingRight:"0"})},e}function ce(e,t,o){const n=l("div",{padding:`${y.xxs} ${y.md} ${y.xxs} 0`,borderRight:Pe,textAlign:"center",flex:"1"}),i=l("div",{fontSize:T.heading,fontWeight:"700",color:ee,lineHeight:oe.tight},e),c=l("div",{fontSize:T.body,color:_,marginTop:y.xxs},t);return n.appendChild(i),n.appendChild(c),o&&n.appendChild(l("div",{fontSize:T.body,color:_,opacity:"0.85"},o)),n}function je(e,t){const o=l("div",{display:"flex",justifyContent:"space-between",fontSize:T.body,gap:y.md,marginBottom:"3px"}),n=l("span",{color:_,flexShrink:"0"},e),i=l("span",{color:ee,textAlign:"right",fontWeight:"500",wordBreak:"break-all"},t);return o.appendChild(n),o.appendChild(i),o}function Bt(e,t){const o=l("div",{display:"flex",fontSize:T.body,gap:y.sm,marginBottom:"3px"}),n=l("span",{color:_,flexShrink:"0"},`${e}:`),i=l("span",{color:ee,fontWeight:"500",wordBreak:"break-all"},t);return o.appendChild(n),o.appendChild(i),o}export function metadataEntries(e){const t=[];for(const[o,n]of Object.entries(e)){const i=$t(n);i!==void 0&&t.push([o,i])}return t}const Nt=["lot","product","testprogram","split"],be=e=>{const t=Nt.indexOf(e.toLowerCase().replace(/[^a-z]/g,""));return t===-1?Number.MAX_SAFE_INTEGER:t};export function buildFacetSummaryChips(e,t=3){if(!e.length)return null;const o=l("div",{display:"flex",flexWrap:"wrap",alignItems:"baseline",gap:`${y.xs} ${y.lg}`}),n=[...e].sort((m,d)=>be(m.key)-be(d.key)),i=n.filter(m=>be(m.key)!==Number.MAX_SAFE_INTEGER),c=n.filter(m=>be(m.key)===Number.MAX_SAFE_INTEGER),s=m=>{const d=l("span",{whiteSpace:"nowrap"}),r=l("span",{color:_},`${ke(m.key)}: `);d.appendChild(r);const a=m.values.slice(0,t),h=m.values.length-a.length;let u=a.map(w=>w.value).join(", ");return h>0&&(u+=` +${h} more`),d.appendChild(document.createTextNode(u)),d};for(const m of i)o.appendChild(s(m));if(c.length){const m=l("span",{display:"none",flexWrap:"wrap",alignItems:"baseline",gap:`${y.xs} ${y.lg}`});for(const a of c)m.appendChild(s(a));const d=l("button",{background:"none",border:"none",padding:"0",cursor:"pointer",color:$.iconActive,fontSize:T.body,whiteSpace:"nowrap"});d.type="button",G(d,"bare");const r=a=>{m.style.display=a?"contents":"none",d.textContent=a?"less":`+${c.length} more`,d.setAttribute("aria-expanded",String(a))};r(!1),d.addEventListener("click",()=>r(d.getAttribute("aria-expanded")!=="true")),o.appendChild(m),o.appendChild(d)}return o}export function buildMetadataStripRow(e,t,o){const n=dt(e,o),i=buildFacetSummaryChips(n);if(!i&&!t)return null;const c=l("div",{display:"flex",flexWrap:"wrap",alignItems:"baseline",gap:"4px 14px"});if(t){const s=l("span",{fontWeight:"500",whiteSpace:"nowrap"},t.aggrMethod?`${t.lotSize} wafers stacked \xB7 ${t.aggrMethod}`:`${t.lotSize} wafers stacked`);c.appendChild(s)}return i&&c.appendChild(i),c}const Tt={display:"flex",flexDirection:"column",gap:y.sm,background:$.menuBg,border:`1px solid ${$.menuBorder}`,borderRadius:q.container,padding:`${y.sm} ${y.lg}`,boxShadow:Re.panel,fontSize:T.body,lineHeight:oe.none,boxSizing:"border-box",width:"100%",minWidth:"0"};export function buildMetadataStripBox(e,t,o){const n=buildMetadataStripRow(e,t,o);if(!n)return null;const i=l("div",Tt);return i.appendChild(n),i}export function buildCompactMetadataRows(e){const t=metadataEntries(e);if(!t.length)return null;const o=l("div");for(const[n,i]of t)o.appendChild(Bt(ke(n),i));return o}export function buildMetadataInfoSection(e,t){const o=buildMetadataStripRow(e,t,{facetableOnly:e.length>1});if(!o)return null;const n=l("div");return n.appendChild(ae("Wafer Info")),n.appendChild(o),n}export function buildYieldSection(e,t,o=[1]){const n=l("div");n.appendChild(ae("Summary"));const i=_e();if(i.appendChild(ce(String(e.totalDies),"Total dies")),e.partialDies>0&&i.appendChild(ce(String(e.partialDies),"Partial")),e.yieldPercent!==null){const c=Je([o]);i.appendChild(ce(`${e.yieldPercent.toFixed(1)}%`,"Yield",`pass: ${c}`))}return i.finish(),n.appendChild(i),e.edgeExcludedDies>0&&n.appendChild(je("Edge excluded (outer zone)",String(e.edgeExcludedDies))),n}export function buildBinSection(e,t,o,n,i,c){const s=new Map;if(i)for(const[r,a]of Object.entries(i))s.set(Number(r),a);else for(const r of e){if(r.partial||r.edgeExcluded)continue;const a=o==="hard"?r.hbin:r.sbin;a!=null&&s.set(a,(s.get(a)??0)+1)}if(!s.size)return null;const m=n??Be(e,{passBins:c,...o==="hard"?{hbinDefs:t}:{sbinDefs:t}}),d=l("div");d.appendChild(ae(Ve(o,[...s.values()].reduce((r,a)=>r+a,0))));for(const r of qe(s,t,m[o],m.pass[o]))d.appendChild(r);return d}function Ve(e,t){return`${e==="hard"?"Hard":"Soft"} Bin Breakdown \u2014 % of dies (N=${t})`}function qe(e,t,o,n){const i=[...e.values()].reduce((m,d)=>m+d,0),c=t?new Map(t.map(m=>[m.bin,m])):null;return xt(e.entries(),n).map(([m,d])=>{const r=c?.get(m),a=r?.name?`Bin ${m} \xB7 ${r.name}`:`Bin ${m}`,h=d/i*100,u=o.get(m)??ut;return ve(`${a} (${d})`,h,u)})}export function buildBinBreakdownSection(e){const{dies:t,hbinDefs:o,sbinDefs:n,hardCounts:i,softCounts:c,plotMode:s,passBins:m=[1],panel:d}=e,r=e.binColors??Be(t,{passBins:m,hbinDefs:o,sbinDefs:n}),a=g=>{const C=g==="hard"?i:c,b=new Map;if(C){for(const[k,v]of Object.entries(C))b.set(Number(k),v);return b}for(const k of t){if(k.partial||k.edgeExcluded)continue;const v=g==="hard"?k.hbin:k.sbin;v!=null&&b.set(v,(b.get(v)??0)+1)}return b},h=a("hard"),u=a("soft"),w=h.size>0,L=u.size>0;if(!w&&!L)return null;const R=he(d),P=s==="softBin"||s==="stackedSoftBins"?"soft":s==="hardBin"||s==="stackedBins"?"hard":void 0;let F=R.binMode??P??(w?"hard":"soft");F==="hard"&&!w&&(F="soft"),F==="soft"&&!L&&(F="hard");const A=F==="hard"?h:u,x=F==="hard"?o:n,f=[...A.values()].reduce((g,C)=>g+C,0),{outer:S}=ie(Ve(F,f),!0,void 0,{stateKey:"bins",panel:d,render:g=>{const C=F==="hard"?r.hard:r.soft;for(const b of qe(A,x,C,r.pass[F]))g.appendChild(b)},control:w&&L&&d?()=>we([["hard","Hard"],["soft","Soft"]],F,g=>{R.binMode=g;const C=buildBinBreakdownSection(e);C&&S.replaceWith(C)},d.ownerDocument,!0):void 0});return S}export function buildLotBinSection(e,t,o,n){return buildBinSection(e,t,o,n)}export function buildRegionYieldPanelSection(e){const{diesByWafer:t,allWafers:o,ringCount:n,passBins:i,panel:c}=e,s=he(c),m=a=>ot(t,o,n,i,a==="ring"?tt:nt);if(!m("ring").length&&!m("quadrant").length)return null;const d=s.regionMode,{outer:r}=ie(d==="ring"?"Ring Yield":"Quadrant Yield",!0,void 0,{stateKey:"regionYield",panel:c,render:a=>{const h=m(d);if(!h.length){a.appendChild(l("div",{fontSize:T.body,color:_},"No positioned dies for this breakdown."));return}for(const{label:u,n:w,yieldPercent:L}of h)a.appendChild(ve(`${u} (N=${w})`,L))},control:c?()=>we([["ring","Ring"],["quadrant","Quadrant"]],d,a=>{s.regionMode=a;const h=buildRegionYieldPanelSection(e);h&&r.replaceWith(h)},c.ownerDocument,!0):void 0});return r}export function buildLotOverviewSection(e,t=[]){const o=l("div"),n=Ne(e.perWafer.map(s=>s.summary.wafer));o.appendChild(ae(n.lotId!==void 0?"Lot Summary":"Summary"));const i=_e();i.appendChild(ce(String(e.stats.waferCount),"Wafers"));const c=e.perWafer.map(s=>s.summary.stats.yieldPercent).filter(s=>s!==null);if(c.length){const s=c.reduce((m,d)=>m+d,0)/c.length;i.appendChild(ce(`${s.toFixed(1)}%`,"Mean per-wafer yield"))}if(i.finish(),o.appendChild(i),t.length){const s=t.reduce((d,r)=>d+r.stats.analyzedDies,0),m=t.reduce((d,r)=>d+r.stats.excludedDies,0);(s||m)&&o.appendChild(je("Dies analysed",m?`${s.toLocaleString()} (${m.toLocaleString()} excluded)`:s.toLocaleString()))}return o}export function buildPerWaferYieldSection(e,t,o,n,i){const c=e.perWafer.map(f=>({waferIndex:f.waferIndex,label:ye(t[f.waferIndex],f.waferIndex).replace(/\s*·\s*\d+(\.\d+)?%$/,""),yieldPct:f.summary.stats.yieldPercent})).filter(f=>f.yieldPct!==null);if(!c.length)return null;const s=vt(c.map(f=>f.yieldPct)),m=St(c.map(f=>f.yieldPct)),d=s===m?"":` (${s.toFixed(1)}\u2013${m.toFixed(1)}%)`,r=[...c.map(f=>f.yieldPct)].sort((f,S)=>f-S),a=wt(r),h=Fe(r,.25),u=Fe(r,.75),w=c.length>=5?h-1.5*(u-h):-1/0,L=he(n),R=f=>{const S=L.waferSort==="yield"?[...c].sort((g,C)=>g.yieldPct-C.yieldPct):c;for(const{waferIndex:g,label:C,yieldPct:b}of S){const v=b<w?`${C} \xB7 low outlier`:C,E=ve(v,b,void 0,void 0,a),D=i?.(g);D?.total&&A(E,D),x(E,g,C,b,D),f.appendChild(E)}},P=` \xB7 median ${a.toFixed(1)}%`,{outer:F}=ie("Wafer Yield"+d+P,!0,void 0,{stateKey:"waferYield",panel:n,render:R,control:n?f=>we([["slot","Slot"],["yield","Yield"]],L.waferSort,S=>{L.waferSort=S,f()},n.ownerDocument,!0):void 0});return F;function A(f,S){const g=S.unusual?"unusual":S.notable?"notable":"info",C=l("span",{marginLeft:y.sm,flexShrink:"0",background:fe(g),color:"#fff",borderRadius:q.container,padding:"0 5px",fontSize:T.body,fontWeight:"600"},`${S.total}`);C.setAttribute("aria-hidden","true"),de(C,`${S.total} finding${S.total===1?"":"s"}`+(S.unusual?`, ${S.unusual} unusual`:"")+(S.notable?`, ${S.notable} notable`:"")),f.firstElementChild?.firstElementChild?.appendChild(C)}function x(f,S,g,C,b){o&&(f.style.cursor="pointer",f.style.borderRadius="4px",f.style.padding="2px 3px",f.style.marginLeft="-3px",f.style.marginRight="-3px",f.setAttribute("role","button"),f.setAttribute("aria-label",`${g}, ${C.toFixed(1)}% yield`+(b?.total?`, ${b.total} finding${b.total===1?"":"s"}${b.unusual?`, ${b.unusual} unusual`:""}`:"")+" \u2014 open wafer"),f.tabIndex=0,f.addEventListener("mouseenter",()=>{f.style.background=$.bgHover}),f.addEventListener("mouseleave",()=>{f.style.background=""}),f.addEventListener("focus",()=>{f.style.background=$.bgHover}),f.addEventListener("blur",()=>{f.style.background=""}),f.addEventListener("click",()=>o(S)),f.addEventListener("keydown",k=>{(k.key==="Enter"||k.key===" ")&&(k.preventDefault(),o(S))}))}}function Et(e){return ht([...e].sort((t,o)=>t-o))}function Ke(e,t){if(!e)return[];const o=e.waferMetadata??(e.perWaferMetadata?Le(e.perWaferMetadata.map(n=>({metadata:n}))):void 0);return!o||!Object.keys(o).length?[]:ct({waferMetadata:o,waferKeys:"identity",waferPlacement:"csv",reservedLabels:t}).columns}function Ge(e){return e?.populationLabel?[{key:"population",label:"Population",scope:"wafer",constant:e.populationLabel,get:()=>e.populationLabel,csvOnly:!0}]:[]}export function buildTestSection(...e){return ge(buildTestSectionSteps(...e))}export function*buildTestSectionSteps(e,t,o,n,i,c,s,m,d="full"){const r=e.filter(p=>!p.partial&&!p.edgeExcluded),a=new Map((n?.perTestStats??[]).map(p=>[p.testNumber,p])),h=new Map((n?.testSpecYield??[]).map(p=>[p.testNumber,p]));let u;if(t?.length?u=t.filter(et).map(p=>({testNumber:p.testNumber,name:p.name,unit:p.unit,limitLow:p.limitLow,limitHigh:p.limitHigh})):u=gt(r).map(W=>({testNumber:W,name:`Test ${W}`})),!u.length)return null;const w=u.filter(p=>r.some(W=>{const H=W.testValues?.[p.testNumber];return H!==void 0&&isFinite(H)}));if(!w.length)return null;const L=w.some(p=>p.limitLow!==void 0||p.limitHigh!==void 0),R=[],P=p=>!!p&&p.stddev!==void 0&&p.median!==void 0&&p.q1!==void 0&&p.q3!==void 0,F=p=>p.limitLow!==void 0||p.limitHigh!==void 0,A=!!(s?.length&&t?.length),f=(A||w.some(p=>!P(a.get(p.testNumber))||F(p)&&!h.has(p.testNumber)))&&t?.length?yield*bt(s??[{dies:e}],t):void 0;for(const p of w){yield;const W=a.get(p.testNumber),H=f?.specTally.get(p.testNumber),se=f?.stats.get(p.testNumber),Y=()=>r.map(I=>I.testValues?.[p.testNumber]).filter(I=>I!==void 0&&isFinite(I));let O;if(P(W)){const I=W;O={testNumber:p.testNumber,min:I.min,max:I.max,mean:I.mean,count:I.count,stddev:I.stddev,median:I.median,q1:I.q1,q3:I.q3}}else if(se)O={testNumber:p.testNumber,...se};else{const I=Y();if(!I.length)continue;O={testNumber:p.testNumber,...Et(I)}}let B=null,M=0;if(F(p)){const I=h.get(p.testNumber);if(I)({yieldPercent:B,totalDies:M}=I);else if(H)M=H.n,B=M>0?(M-H.fail)/M*100:null;else{const Z=Y();let ne=0;for(const J of Z)(p.limitLow!==void 0&&J<p.limitLow||p.limitHigh!==void 0&&J>p.limitHigh)&&ne++;M=Z.length,B=Z.length>0?(Z.length-ne)/Z.length*100:null}}R.push({entry:p,stats:O,specYieldPct:B,specN:M})}if(!R.length)return null;const S=new Map;for(const p of(A?f?.capability:void 0)??[])p.hasSpec&&S.set(p.testNumber,p.ppk);const g=S.size>0,b=new Set(R.map(p=>p.stats.count)).size===1?R[0].stats.count:null,k=l("div"),v=l("div",{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:y.sm}),E=b!==null?` \xB7 N=${b.toLocaleString()}`:"",D=c?.populationLabel?`Test Values (${R.length}) \u2014 ${c.populationLabel}${E}`:`Test Values (${R.length})${E}`,V=ae(D);V.style.marginBottom="0",v.appendChild(V);const z=document.createElement("button");z.type="button",z.textContent="Test values CSV",Object.assign(z.style,{...ue("outlined")}),G(z),z.addEventListener("click",()=>{const p=["Test","Unit","N","Min","Q1","Median","Mean","Q3","Max","StdDev"];g&&p.push("Ppk");const W=R.some(B=>B.specYieldPct!==null&&B.specN!==B.stats.count);L&&(p.push("LSL","USL","Spec Yield %"),W&&p.push("Spec Yield N"));const H=[...Ke(c,p),...Ge(c)],Y=[[...H.map(B=>B.label),...p].map(me).join(",")],O=B=>Ee(B,void 0,"engineering");for(const{entry:B,stats:M,specYieldPct:I,specN:Z}of R){const ne=[...H.map(J=>J.constant??""),B.name,B.unit??"",String(M.count),O(M.min),O(M.q1),O(M.median),O(M.mean),O(M.q3),O(M.max),O(M.stddev)];if(g){const J=S.get(B.testNumber);ne.push(J==null?"":J.toFixed(3))}L&&(ne.push(B.limitLow!==void 0?O(B.limitLow):"",B.limitHigh!==void 0?O(B.limitHigh):"",I!==null?I.toFixed(1):""),W&&ne.push(B.limitLow!==void 0||B.limitHigh!==void 0?String(Z):"")),Y.push(ne.map(me).join(","))}Ae(Y.join(`
2
- `),"test-values.csv","text/csv",i)}),v.appendChild(z),k.appendChild(v);const Q=l("table",{width:"100%",borderCollapse:"collapse",fontSize:T.body}),te=document.createElement("thead"),X=document.createElement("tr"),j=["Test"];b===null&&j.push("N"),d==="full"&&j.push("Min","Q1","Median"),j.push("Mean"),d==="full"&&j.push("Q3","Max","StdDev"),g&&j.push("Ppk"),d==="full"&&L&&j.push("LSL","USL"),L&&j.push("Spec yield");for(const p of j){const W=l("th",{textAlign:p==="Test"?"left":"right",fontWeight:"600",fontSize:T.meta,letterSpacing:pe,textTransform:"uppercase",color:_,padding:"3px 8px 5px",borderBottom:`1px solid ${$.menuBorder}`,whiteSpace:"nowrap"},p);X.appendChild(W)}te.appendChild(X),Q.appendChild(te);const U=document.createElement("tbody");for(const{entry:p,stats:W,specYieldPct:H,specN:se}of R){const Y=M=>Ee(M,p.unit,o),O=document.createElement("tr"),B=(M,I="right")=>{const Z=l("td",{textAlign:I,padding:`${y.xs} ${y.md}`,borderBottom:`1px solid ${$.menuBorder}`,color:$.text,whiteSpace:"nowrap"},M);O.appendChild(Z)};if(B(p.name,"left"),b===null&&B(`${W.count}`),d==="full"&&(B(Y(W.min)),B(Y(W.q1)),B(Y(W.median))),B(Y(W.mean)),d==="full"&&(B(Y(W.q3)),B(Y(W.max)),B(Y(W.stddev))),g){const M=S.get(p.testNumber);B(M==null?"\u2014":M.toFixed(2))}d==="full"&&L&&(B(p.limitLow!==void 0?Y(p.limitLow):"\u2014"),B(p.limitHigh!==void 0?Y(p.limitHigh):"\u2014")),L&&B(H!==null?b!==null&&se===b?`${H.toFixed(1)}%`:`${H.toFixed(1)}% (N=${se})`:"\u2014"),U.appendChild(O)}Q.appendChild(U);const N=l("div",{overflowX:"auto"});if(N.appendChild(Q),!m)return k.appendChild(N),k;v.remove();const{outer:K}=ie(D,!0,void 0,{stateKey:"testValues",panel:m,render:p=>p.appendChild(N),control:()=>z});return K}export function buildFunctionalTestSection(e,t,o,n,i,c){const s=o??it(e,t);if(!s?.length)return null;const d=new Set(s.map(g=>g.totalDies)).size===1?s[0].totalDies:null,r=d!==null?` \xB7 N=${d.toLocaleString()}`:"",a=l("div"),h=l("div",{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:y.sm}),u=i?.populationLabel?`Functional Tests (${s.length}) \u2014 ${i.populationLabel}${r}`:`Functional Tests (${s.length})${r}`,w=ae(u);w.style.marginBottom="0",h.appendChild(w);const L=document.createElement("button");L.type="button",L.textContent="Functional CSV",Object.assign(L.style,{...ue("outlined")}),G(L),L.addEventListener("click",()=>{const g=["Test","N","Pass","Fail","Pass Rate %"],C=[...Ke(i,g),...Ge(i)],b=[[...C.map(k=>k.label),...g].map(me).join(",")];for(const k of s)b.push([...C.map(v=>v.constant??""),k.label,String(k.totalDies),String(k.passDies),String(k.failDies),k.passRatePercent!==null?k.passRatePercent.toFixed(1):""].map(me).join(","));Ae(b.join(`
3
- `),"functional-tests.csv","text/csv",n)}),h.appendChild(L),a.appendChild(h);const R=l("table",{width:"100%",borderCollapse:"collapse",fontSize:T.body}),P=document.createElement("thead"),F=document.createElement("tr"),A=d===null?["Test","N","Pass","Fail","Pass rate"]:["Test","Pass","Fail","Pass rate"];for(const g of A)F.appendChild(l("th",{textAlign:g==="Test"?"left":"right",fontWeight:"700",fontSize:T.body,letterSpacing:pe,textTransform:"uppercase",color:_,padding:"3px 8px 5px",borderBottom:`1px solid ${$.menuBorder}`,whiteSpace:"nowrap"},g));P.appendChild(F),R.appendChild(P);const x=document.createElement("tbody");for(const g of s){const C=document.createElement("tr"),b=(k,v="right")=>{C.appendChild(l("td",{textAlign:v,padding:`${y.xs} ${y.md}`,borderBottom:`1px solid ${$.menuBorder}`,color:ee,whiteSpace:"nowrap"},k))};b(g.label,"left"),d===null&&b(String(g.totalDies)),b(String(g.passDies)),b(String(g.failDies)),b(g.passRatePercent!==null?`${g.passRatePercent.toFixed(1)}%`:"\u2014"),x.appendChild(C)}R.appendChild(x);const f=l("div",{overflowX:"auto"});if(f.appendChild(R),!c)return a.appendChild(f),a;h.remove();const{outer:S}=ie(u,!0,void 0,{stateKey:"functionalTests",panel:c,render:g=>g.appendChild(f),control:()=>L});return S}export function buildLotFunctionalSection(e,t,o,n,i){const c=o?.length?{perWaferMetadata:o.map(m=>m.wafer??{}),populationLabel:`${o.length} wafer${o.length===1?"":"s"} pooled`}:void 0,s=yt(o);return s?buildFunctionalTestSection(e,t,s,n,c,i):buildFunctionalTestSection(e,t,void 0,n,c,i)}const re={unusual:0,notable:1,info:2};function $e(e){return l("span",{display:"inline-block",width:"7px",height:"7px",borderRadius:q.pill,background:fe(e),flexShrink:"0"})}function Ue(e,t){let o=e.summary;return t&&(o.startsWith(`${t} has `)?o=o.slice(t.length+5):o.startsWith(`${t} `)&&(o=o.slice(t.length+1))),o=le(o),o.length?o[0].toUpperCase()+o.slice(1):o}export function buildFindingsSection(e,t,o,n,i=!1){if(!e.length)return null;const c=e.some(x=>x.severity==="unusual"||x.severity==="notable"),s=c?e.filter(x=>x.severity!=="info").length.toString():void 0,{outer:m,content:d}=i?(()=>{const x=l("div");return{outer:x,content:x}})():ie(`Findings (${e.length})`,c,s);function r(x){return x.reduce((f,S)=>re[S.severity]<re[f]?S.severity:f,"info")}function a(x){const f=new Map;for(const g of x){const C=`${g.comparison.family}\0${g.comparison.left}`,b=f.get(C)??[];b.push(g),f.set(C,b)}const S=[...f.entries()].map(([g,C])=>{const b=[...C].sort((k,v)=>re[k.severity]-re[v.severity]);return{key:g,findings:b,worst:r(b)}});return S.sort((g,C)=>re[g.worst]-re[C.worst]),S}function h(x,f){const g={ring:"Ring",quadrant:"Quadrant",sector:"Sector","reticle-position":"Reticle","test-site":"Test site",cluster:"Cluster","edge-arc":"Edge arc",wafer:"Wafer"}[x]??x;return f.toLowerCase().startsWith(g.toLowerCase())?f:`${g}: ${f}`}const u=e.filter(x=>x.comparison.family==="spatial-pattern"),w=new Set([...u.flatMap(x=>x.relatedIds??[]),...e.flatMap(x=>x.absorbedIds??[])]),L=e.filter(x=>x.comparison.family!=="spatial-pattern"&&!w.has(x.id)),R=a(L);function P(x,f=!1,S){const g=n===x.id,C=document.createElement("button");return C.type="button",C.dataset.wmapFinding=x.id,C.textContent=Ue(x,S),de(C,x.summary),C.setAttribute("aria-current",g?"true":"false"),Object.assign(C.style,{border:`1px solid ${$.menuBorder}`,borderLeft:`3px solid ${fe(x.severity)}`,background:g?$.bgActive:$.menuBg,borderRadius:q.container,padding:f?"6px 10px":"8px 10px",textAlign:"left",fontSize:f?"10px":"11px",fontWeight:g?"600":"400",color:$.text,cursor:"pointer",width:"100%",marginBottom:y.xs}),G(C,"bare"),C.addEventListener("click",()=>o(x,C)),C}const F=le(rt(e)??"");if(F){const x=l("div",{background:$.bgActive,borderRadius:q.container,padding:`${y.md} ${y.lg}`,marginBottom:y.md,display:"flex",gap:y.md,alignItems:"flex-start"}),f=l("span",{flex:"1",fontSize:T.body,lineHeight:oe.base,color:$.text},F);x.appendChild(f);const S=l("button",{flexShrink:"0",border:"none",background:"none",fontSize:T.body,color:$.icon,cursor:"pointer",padding:"0",lineHeight:oe.base,whiteSpace:"nowrap"},"Detail \u25B8");S.type="button",G(S,"bare"),S.addEventListener("click",()=>{const g=Ie({title:"Findings Summary",onClose:()=>{},anchor:S}),C=l("p",{fontSize:"16px",lineHeight:oe.base,color:$.text,padding:`${y.xxl} ${y.xxxl} ${y.xxl}`,margin:"0",borderBottom:`1px solid ${$.menuBorder}`,flexShrink:"0"},F);g.contentWrap.appendChild(C);const b=l("div",{overflowY:"auto",padding:`${y.xxl} ${y.xxxl}`,flex:"1"}),k=(v,E)=>{const D=l("div",{display:"flex",alignItems:"center",gap:"7px",fontSize:T.sub,fontWeight:"600",color:ee,marginTop:y.lg,marginBottom:y.xs});return D.appendChild($e(v)),D.appendChild(l("span",{},E)),D};for(const v of u){b.appendChild(k(v.severity,v.comparison.left)),b.appendChild(l("div",{fontSize:T.body,color:$.text,padding:"4px 0 4px 15px",marginBottom:y.xxs},le(v.summary)));const E=e.filter(D=>v.relatedIds?.includes(D.id));for(const D of E)b.appendChild(l("div",{fontSize:T.body,color:_,padding:"2px 0 2px 23px",marginBottom:y.xxs},le(D.summary)))}for(const v of R){const[E,D]=v.key.split("\0");b.appendChild(k(v.worst,h(E,D)));for(const V of v.findings)b.appendChild(l("div",{fontSize:T.body,color:$.text,padding:"4px 0 4px 15px",marginBottom:y.xxs},Ue(V,D)))}g.contentWrap.appendChild(b),Object.assign(g.contentWrap.style,{flexDirection:"column",overflow:"hidden"})}),x.appendChild(S),d.appendChild(x)}let A=!0;for(const x of u){A||d.appendChild(l("div",{height:"1px",background:$.separator,margin:"4px 0"})),A=!1;const f=e.filter(k=>x.relatedIds?.includes(k.id)),S=f.length>0,g=l("div",{position:"relative",marginBottom:S?"2px":"4px"}),C=n===x.id,b=document.createElement("button");if(b.type="button",b.dataset.wmapFinding=x.id,Object.assign(b.style,{border:`1px solid ${$.menuBorder}`,borderLeft:`3px solid ${fe(x.severity)}`,background:C?$.bgActive:$.menuBg,borderRadius:q.container,padding:"8px 32px 8px 10px",textAlign:"left",fontSize:T.body,fontWeight:C?"600":"500",color:$.text,cursor:"pointer",width:"100%"}),b.textContent=le(x.summary),de(b,x.summary),b.setAttribute("aria-current",C?"true":"false"),G(b,"bare"),b.addEventListener("click",()=>o(x,b)),g.appendChild(b),S){const k=l("div",{display:"none",paddingLeft:"12px",marginBottom:y.xs});for(const D of f)k.appendChild(P(D,!0));g.appendChild(k);let v=!1;const E=l("button",{position:"absolute",top:"50%",right:"8px",transform:"translateY(-50%)",border:"none",background:"none",fontSize:T.body,color:$.icon,cursor:"pointer",padding:`${y.xxs} ${y.xs}`,lineHeight:oe.none},"\u25B8");G(E,"bare"),E.type="button",de(E),E.setAttribute("aria-label","Show supporting findings"),E.setAttribute("aria-expanded","false"),E.addEventListener("click",D=>{D.stopPropagation(),v=!v,k.style.display=v?"block":"none",E.textContent=v?"\u25BE":"\u25B8";const V=v?"Hide supporting findings":"Show supporting findings";E.setAttribute("aria-label",V),E.setAttribute("aria-expanded",String(v))}),g.appendChild(E)}d.appendChild(g)}for(const x of R){A||d.appendChild(l("div",{height:"1px",background:$.separator,margin:"4px 0"})),A=!1;const[f,S]=x.key.split("\0"),g=l("div",{display:"flex",alignItems:"center",gap:y.sm,fontSize:T.body,fontWeight:"600",color:ee,margin:"2px 0 4px"});g.appendChild($e(x.worst)),g.appendChild(l("span",{},h(f,S))),d.appendChild(g);for(const C of x.findings)d.appendChild(P(C,!1,S))}return m}const Ft=[{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"}],Dt=[{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"}],Rt=["unusual","notable","info"],Mt={unusual:"Unusual",notable:"Notable",info:"Info"};function It(e,t,o){const n=l("div",{display:"flex",flexWrap:"wrap",alignItems:"center",gap:y.lg,marginBottom:y.lg,paddingBottom:"10px",borderBottom:`1px solid ${$.separator}`}),i={unusual:0,notable:0,info:0};for(const r of e)i[r.severity]++;const c=Rt.filter(r=>i[r]>0),s=new Set(t.severity===void 0?c:Array.isArray(t.severity)?t.severity:[t.severity]),m=l("div",{display:"flex",gap:y.xs,flexWrap:"wrap"});for(const r of c){const a=document.createElement("button");a.type="button";const h=()=>{const u=s.has(r);a.setAttribute("aria-pressed",u?"true":"false"),Object.assign(a.style,{display:"inline-flex",alignItems:"center",gap:"5px",border:`1px solid ${$.menuBorder}`,borderRadius:q.pill,background:u?$.bgActive:"none",opacity:u?"1":"0.5",color:$.text,fontSize:T.body,padding:`${y.xxs} ${y.md}`,cursor:"pointer"}),a.dataset.on=u?"true":"false"};a.appendChild($e(r)),a.appendChild(l("span",{},`${Mt[r]} ${i[r]}`)),de(a,()=>s.has(r)?"Click to hide these findings":"Click to show these findings"),h(),G(a,"bare"),a.addEventListener("click",()=>{s.has(r)?s.delete(r):s.add(r),t.severity=s.size===c.length?void 0:[...s],o()}),m.appendChild(a)}n.appendChild(m);const d=t.kind!==void 0||t.family!==void 0;return(e.length>=Wt||d)&&(n.appendChild(De("Kind:",Ft,t.kind??"",r=>{t.kind=r||void 0,o()},{maxWidth:"130px"})),n.appendChild(De("Region:",Dt,t.family??"",r=>{t.family=r||void 0,o()},{maxWidth:"150px"}))),n}const Wt=8;function At(e,t){const o=l("div",{display:"flex",flexDirection:"column",gap:y.xs,padding:y.md,marginBottom:y.md,background:$.bgHover,borderRadius:q.control},void 0,t);if(o.appendChild(l("div",{fontSize:T.body,color:$.value},e.message,t)),e.detail&&o.appendChild(l("div",{fontSize:T.meta,color:_},e.detail,t)),e.actionLabel&&e.onAction){const n=l("button",ue("outlined"),e.actionLabel,t);n.type="button",n.style.alignSelf="flex-start",n.style.marginTop=y.xs,G(n),n.addEventListener("click",e.onAction),o.appendChild(n)}return o}export function buildFindingsSectionWithFilter(e,t,o,n,i,c,s){if(!e.findings.length&&!s)return null;const m=e.findings.some(L=>L.severity==="unusual"||L.severity==="notable"),d=m?e.findings.filter(L=>L.severity!=="info").length.toString():void 0,r=c?.ownerDocument??document,{outer:a,content:h}=ie(`Findings (${e.findings.length})`,m||!!s,d,{stateKey:"findings",panel:c});if(s&&h.appendChild(At(s,r)),!e.findings.length)return a;h.appendChild(It(e.findings,n,i));const u=lt(e,n);if(!u.length)return h.appendChild(l("div",{color:_,fontSize:T.body,textAlign:"center",padding:`${y.xxl} ${y.md}`},"No findings match the current filter.")),a;const w=buildFindingsSection(u,e,t,o,!0);return w&&h.appendChild(w),a}export function buildLotTestSection(...e){return ge(buildLotTestSectionSteps(...e))}export function*buildLotTestSectionSteps(e,t,o,n,i,c,s,m="full"){const d=n?.length?{perWaferMetadata:n.map(a=>a.wafer??{}),populationLabel:`${n.length} wafer${n.length===1?"":"s"} pooled`}:void 0;let r;if(n?.length&&n.every(a=>a.stats.perTestStats!==void 0)){const a=new Map;for(const h of n)for(const u of h.stats.perTestStats??[]){const w=a.get(u.testNumber);w?(w.n+=u.count,w.sum+=u.mean*u.count,w.min=Math.min(w.min,u.min),w.max=Math.max(w.max,u.max)):a.set(u.testNumber,{n:u.count,sum:u.mean*u.count,min:u.min,max:u.max})}r={perTestStats:[...a.entries()].map(([h,u])=>({testNumber:h,count:u.n,min:u.min,max:u.max,mean:u.sum/u.n}))}}if(n?.length&&n.every(a=>a.stats.testSpecYield!==void 0)){const a=new Map;for(const h of n)for(const u of h.stats.testSpecYield??[]){const w=a.get(u.testNumber);w?(w.passDies+=u.passDies,w.totalDies+=u.totalDies):a.set(u.testNumber,{passDies:u.passDies,totalDies:u.totalDies})}r={...r,testSpecYield:[...a.entries()].map(([h,u])=>({testNumber:h,totalDies:u.totalDies,yieldPercent:u.totalDies>0?u.passDies/u.totalDies*100:null}))}}return yield*buildTestSectionSteps(e,t,o,r,i,d,c,s,m)}export function createSummaryPanelEl(e,t,o=document){const n=e==="top"||e==="bottom",i=l("div",{background:kt,color:$.text,fontVariantNumeric:"tabular-nums",border:Pe,borderRadius:q.container,padding:y.xl,overflowY:"auto",overflowX:n?"auto":"hidden",flexShrink:"0",boxSizing:"border-box",fontFamily:T.family,fontSize:T.sub,boxShadow:Re.panel},void 0,o);return e==="right"&&(i.style.marginRight=t),e==="left"&&(i.style.marginLeft=t),e==="bottom"&&(i.style.marginBottom=t),n?(i.style.height="180px",i.style.width="100%"):(i.style.width="300px",i.style.maxHeight="100%"),i}export function wrapWithSummaryPanel(e,t,o){const n=o==="top"||o==="bottom",i=l("div",{display:"flex",flexDirection:n?"column":"row",gap:y.md,width:"100%",height:"100%",flex:"1 1 0",minHeight:"0",boxSizing:"border-box",alignItems:n?"flex-start":"stretch"});return e.style.flex="1 1 0",e.style.minWidth="0",e.style.minHeight="0",o==="right"||o==="bottom"?(i.appendChild(e),i.appendChild(t)):(i.appendChild(t),i.appendChild(e)),i}function Qe(e){return l("div",{fontSize:T.heading,fontWeight:"600",color:ee,marginBottom:y.lg},e)}function xe(e,t){const o=l("button",ue("outlined"),e);return o.type="button",G(o),o.addEventListener("click",t),o}function Xe(...e){const t=e.filter(n=>!!n);if(!t.length)return null;const o=l("div",{display:"flex",flexWrap:"wrap",gap:y.sm,marginBottom:y.lg});for(const n of t)o.appendChild(n);return o}function Ze(e,t,o,n,i,c,s,m,d,r,a){const h=e.ownerDocument,u=Ie({title:"Die list",onClose:()=>{},anchor:e,ownerDocument:h});u.contentWrap.style.padding="14px 16px";const w=Ct(t,o,{...s,title:n,onSaveText:m,waferMetadata:i,metadataFields:c,extraColumn:d,getWafer:r,ringCount:a,ownerDocument:h});w&&u.contentWrap.appendChild(w)}export function renderWaferSummaryContent(...e){ge(renderWaferSummaryContentSteps(...e))}export function*renderWaferSummaryContentSteps(e,t){const o=e.scrollTop;e.innerHTML="";const{wafer:n,dies:i,yieldSummary:c,dataCoverage:s,hbinDefs:m,sbinDefs:d,testDefs:r,statsSummary:a,passBins:h=[1],ringCount:u=4,binColors:w,plotMode:L,fallbackFormat:R,onFindingClick:P,activeFindingId:F=null,findingsFilter:A,onFindingsFilterChange:x,findingsNotice:f,onSaveText:S,metadataFields:g,dieListOptions:C,metadataShownElsewhere:b}=t;e.appendChild(Qe("Wafer Summary"));const k=t.warnings??Te({statsSummary:a});k.length&&e.appendChild(Se(k,e.ownerDocument));const v=c&&s?xe("Summary report",()=>{We(st({wafer:n,dies:i,yield:c,dataCoverage:s,hbinDefs:m,sbinDefs:d,testDefs:r,passBins:h,ringCount:u},a),{anchor:e})}):null,E=(C?.enabled??!0)&&i.length?xe("View die list",()=>{Ze(e,i,r,`Die list \u2014 ${i.length} dies`,n.metadata,g,C,S,void 0,()=>n,u)}):null,D=Xe(v,E);D&&e.appendChild(D);let V=!0;const z=j=>{j&&(V||e.appendChild(Ye()),V=!1,e.appendChild(j))},Q=a?.stats.isLotStack?a.stats:void 0,te=Q?.lotSize!==void 0?{lotSize:Q.lotSize,aggrMethod:mt(Q.aggregationMethod)}:void 0;b||z(buildMetadataInfoSection([{metadata:n.metadata??void 0}],te)),c&&s&&z(buildYieldSection(c,s,h)),yield,a&&P&&A&&x&&(z(buildFindingsSectionWithFilter(a,P,F,A,x,e,f)),yield),z(buildBinBreakdownSection({dies:i,hbinDefs:m,sbinDefs:d,binColors:w,hardCounts:a?.stats.hardBinCounts,softCounts:a?.stats.softBinCounts,plotMode:L,passBins:h,panel:e})),yield,z(buildRegionYieldPanelSection({diesByWafer:[i],allWafers:[n],ringCount:u,passBins:h,panel:e})),yield;const X=n.metadata?{waferMetadata:n.metadata}:void 0;z(yield*buildTestSectionSteps(i,r,R,a?.stats,S,X,[{dies:i}],e,"compact")),yield,z(buildFunctionalTestSection(i,r,a?.stats.functionalYield,S,X,e)),e.appendChild(l("div",{height:"12px",flexShrink:"0"})),e.scrollTop=o}export function reportMapsFromItems(e,t,o){const n=[];return e.forEach((i,c)=>{!i?.wafer||!i.dies||n.push({...i,wafer:i.wafer,dies:i.dies,label:ye(i,c),passBins:[...Ce(i,t)],ringCount:i.ringCount??o})}),n}export function renderLotSummaryContent(...e){ge(renderLotSummaryContentSteps(...e))}export function*renderLotSummaryContentSteps(e,t){const o=e.scrollTop;e.innerHTML="";const{lotSummary:n,items:i,hbinDefs:c,sbinDefs:s,testDefs:m,passBins:d=[1],ringCount:r=4,binColors:a,plotMode:h,fallbackFormat:u,onFindingClick:w,activeFindingId:L=null,onWaferClick:R,findingsFilter:P,onFindingsFilterChange:F,findingsNotice:A,onSaveText:x,dieListOptions:f,findingsFor:S}=t;e.appendChild(Qe(`Summary \u2014 ${ft(Ne(n.perWafer.map(N=>N.summary.wafer)))}`));const g=t.warnings??Te({lotStatsSummary:n});g.length&&e.appendChild(Se(g,e.ownerDocument));const C=xe("Summary report",()=>{We(at(reportMapsFromItems(i,d,r)),{anchor:e})}),b=[],k=[],v=[],E=[],D=[],V=new WeakMap,z=new WeakMap;for(let N=0;N<i.length;N++){yield;const K=i[N];if(!K){k.push([]);continue}K.wafer&&(b.push(K.wafer),E.push(K.dies??[]),D.push(Ce(K,d)));const p=K.dies??[],W=ye(K,N);for(const H of p)V.set(H,W),K.wafer&&z.set(H,K.wafer);k.push(p);for(const H of p)v.push(H)}const Q=f?.enabled??!0?xe("View die list",()=>{v.length&&Ze(e,v,m,`Die list \u2014 ${v.length} dies across ${i.length} wafer${i.length===1?"":"s"}`,Le(i.filter(N=>!!N).map(N=>({metadata:N.wafer?.metadata}))),i.find(N=>N?.metadataFields?.length)?.metadataFields,f,x,{label:"Wafer",get:N=>V.get(N)},N=>z.get(N),r)}):null,te=Xe(C,Q);te&&e.appendChild(te);const X=i.map(N=>N?.statsSummary).filter(N=>!!N);let j=!0;const U=N=>{N&&(j||e.appendChild(Ye()),j=!1,e.appendChild(N))};U(buildLotOverviewSection(n,X)),yield,w&&P&&F&&(U(buildFindingsSectionWithFilter(n,w,L,P,F,e,A)),yield),U(buildPerWaferYieldSection(n,i,R,e,S)),yield,U(buildBinBreakdownSection({dies:v,hbinDefs:c,sbinDefs:s,plotMode:h,passBins:d,panel:e,binColors:a??pt(i.flatMap(N=>N?[{dies:N.dies??[],passBins:Ce(N,d)}]:[]),{hbinDefs:c,sbinDefs:s}).colors})),yield,U(buildRegionYieldPanelSection({diesByWafer:E,allWafers:b,ringCount:r,passBins:N=>D[N],panel:e})),yield,m?.length&&(U(yield*buildLotTestSectionSteps(v,m,u,X,x,k.map(N=>({dies:N})),e,"compact")),yield,U(buildLotFunctionalSection(v,m,X,x,e))),e.appendChild(l("div",{height:"12px",flexShrink:"0"})),e.scrollTop=o}
1
+ import{waferDisplayLabel as ve}from"../core/waferLabel.js";import{itemPassBins as Se,passBinsLabel as lt}from"../core/passBins.js";import{isParametricTest as dt}from"../renderer/buildWaferMap.js";import{testLabel as ct,markedTestLabel as pt,unmarkedLabel as ut,derivedFields as we,derivedKeyText as ft,derivedCsvCell as Ne,isDerivedTest as Te,DERIVED_CSV_HEADER as De}from"../renderer/testLabel.js";import{buildRingRegions as mt,buildQuadrantRegions as gt,buildRegionYieldData as ht}from"../stats/regions.js";import{computeFunctionalYield as bt}from"../stats/analyzeWaferMap.js";import{renderWaferReportHtml as xt,renderLotReportHtml as yt}from"../stats/renderSummaryReport.js";import{buildFindingsNarrative as Ct}from"../stats/findingsNarrative.js";import{formatFindingTooltip as vt}from"../stats/reportHtml.js";import{filterFindings as St}from"../stats/filterFindings.js";import{buildFacetTable as wt,prettyKey as Ee}from"../stats/facets.js";import{commonMetadata as Fe}from"../stats/facets.js";import{resolveMetadataColumns as $t}from"../stats/metadataColumns.js";import{resolveBinColors as Re,resolveBinColorsByWafer as kt}from"../renderer/binColors.js";import{NO_DATA_FILL as Lt}from"../renderer/colorMap.js";import{classifySpec as Bt,isOutOfSpec as Nt}from"../renderer/spec.js";import{describeWaferPopulation as Me,populationLabel as Tt}from"../stats/population.js";import{buildWarningsBanner as $e,collectWarnings as Ie}from"./warnings.js";export{$e as buildWarningsBanner};import{fmt as Ae,fmtAggregationMethod as Dt,plainBinTerms as de}from"../renderer/fmt.js";import{getUniqueTestNumbers as Et}from"../renderer/buildView.js";import{describeSorted as Ft,quantile as We}from"../stats/math.js";import{pooledTestStatsSteps as Rt}from"../stats/capability.js";import{sortBinsForDisplay as Mt}from"../stats/binPareto.js";import{poolFunctionalYield as It}from"../stats/testPassRate.js";import{makeLabeledSelect as Pe,makeSegmented as ke}from"./charts/chartShell.js";import{SHADOW as ze,MOTION as He,LEADING as te,TRACKING as fe,wireControlHover as G,controlStyle as me,SPACE as x,RADIUS as q,FONT as T,CLR as $,sevColor as ge,openModal as Oe,openReportModal as Ye,saveTextFile as _e,wireTooltip as ce}from"./toolbar.js";import{buildDieListSection as At}from"./dieList.js";export{csvField}from"../core/utils.js";import{csvField as he,drain as be,maxOf as Wt,medianOfSorted as Pt,minOf as zt}from"../core/utils.js";import{metadataDisplayValue as Ht}from"../core/metadata.js";const Ot=$.panelBg,je=`1px solid ${$.menuBorder}`,Yt="12px",Y=$.label,ne=$.value,Ke="10px";function l(e,t,i,n=document){const o=n.createElement(e);return t&&Object.assign(o.style,t),i!==void 0&&(o.textContent=i),o}function se(e){return l("div",{fontSize:Ke,fontWeight:"700",letterSpacing:fe,textTransform:"uppercase",color:Y,marginBottom:x.sm},e)}const Ve=new WeakMap,qe=["regionYield","testValues","functionalTests"];function xe(e){if(!e)return{collapsed:new Set(qe),regionMode:"ring",waferSort:"slot"};let t=Ve.get(e);return t||(t={collapsed:new Set(qe),regionMode:"ring",waferSort:"slot"},Ve.set(e,t)),t}function oe(e,t=!0,i,n){const o=l("div"),u=xe(n?.panel);n?.stateKey&&n.panel&&(t=!u.collapsed.has(n.stateKey));const s=document.createElement("button");s.type="button",Object.assign(s.style,{display:"flex",alignItems:"center",gap:x.xs,width:"100%",background:"none",border:"none",cursor:"pointer",padding:"0 0 6px",textAlign:"left"});const m=l("span",{fontSize:T.meta,color:Y,transition:`transform ${He.base}`,transform:t?"rotate(90deg)":"rotate(0deg)",display:"inline-block",lineHeight:te.none,marginRight:"1px"},"\u25B6"),p=l("span",{fontSize:Ke,fontWeight:"700",letterSpacing:fe,textTransform:"uppercase",color:Y,flex:"1"},e);if(G(s,"bare"),s.setAttribute("aria-expanded",t?"true":"false"),s.appendChild(m),s.appendChild(p),i){const b=l("span",{fontSize:T.meta,fontWeight:"700",background:$.warnBg,color:$.warnText,borderRadius:q.pill,padding:"1px 5px"},i);s.appendChild(b)}const r=l("div");r.style.display=t?"block":"none";let a=t;if(s.addEventListener("click",()=>{a=!a,r.style.display=a?"block":"none",m.style.transform=a?"rotate(90deg)":"rotate(0deg)",s.setAttribute("aria-expanded",a?"true":"false"),n?.stateKey&&n.panel&&(a?u.collapsed.delete(n.stateKey):u.collapsed.add(n.stateKey))}),n?.control){const b=l("div",{display:"flex",alignItems:"center",gap:x.sm});s.style.width="auto",s.style.flex="1",b.appendChild(s);const f=()=>{r.innerHTML="",n.render?.(r)},w=n.control(f);w&&(w.style.marginBottom="6px",w.addEventListener("click",k=>k.stopPropagation()),b.appendChild(w)),o.appendChild(b)}else o.appendChild(s);return o.appendChild(r),n?.render?.(r),{outer:o,content:r}}function Ge(){return l("div",{height:"1px",background:$.separator,margin:`${Yt} 0`,flexShrink:"0"})}function Le(e,t,i=$.barFill,n,o){const u=l("div",{marginBottom:"5px"}),s=l("div",{display:"flex",justifyContent:"space-between",fontSize:T.body,color:ne,marginBottom:x.xxs}),m=l("span",{color:Y},e),p=l("span",{fontWeight:"600"},`${t.toFixed(1)}%`);s.appendChild(m),s.appendChild(p);const r=n!==void 0?n:Math.min(100,Math.max(0,t)),a=i,b=l("div",{position:"relative",height:"9px",background:$.bgActive,borderRadius:q.control,overflow:"hidden"}),f=l("div",{height:"100%",width:`${r}%`,background:a,borderRadius:q.control,transition:`width ${He.base}`});if(b.appendChild(f),o!==void 0){const w=l("div",{position:"absolute",top:"0",bottom:"0",left:`${o}%`,width:"2px",marginLeft:"-1px",background:$.value,opacity:"0.8"});b.appendChild(w)}return u.appendChild(s),u.appendChild(b),u}function Ue(){const e=l("div",{display:"flex",gap:x.sm,marginBottom:x.md});return e.finish=()=>{const t=e.lastElementChild;t&&Object.assign(t.style,{borderRight:"none",paddingRight:"0"})},e}function pe(e,t,i){const n=l("div",{padding:`${x.xxs} ${x.md} ${x.xxs} 0`,borderRight:je,textAlign:"center",flex:"1"}),o=l("div",{fontSize:T.heading,fontWeight:"700",color:ne,lineHeight:te.tight},e),u=l("div",{fontSize:T.body,color:Y,marginTop:x.xxs},t);return n.appendChild(o),n.appendChild(u),i&&n.appendChild(l("div",{fontSize:T.body,color:Y,opacity:"0.85"},i)),n}function Qe(e,t){const i=l("div",{display:"flex",justifyContent:"space-between",fontSize:T.body,gap:x.md,marginBottom:"3px"}),n=l("span",{color:Y,flexShrink:"0"},e),o=l("span",{color:ne,textAlign:"right",fontWeight:"500",wordBreak:"break-all"},t);return i.appendChild(n),i.appendChild(o),i}function _t(e,t){const i=l("div",{display:"flex",fontSize:T.body,gap:x.sm,marginBottom:"3px"}),n=l("span",{color:Y,flexShrink:"0"},`${e}:`),o=l("span",{color:ne,fontWeight:"500",wordBreak:"break-all"},t);return i.appendChild(n),i.appendChild(o),i}export function metadataEntries(e){const t=[];for(const[i,n]of Object.entries(e)){const o=Ht(n);o!==void 0&&t.push([i,o])}return t}const jt=["lot","product","testprogram","split"],ye=e=>{const t=jt.indexOf(e.toLowerCase().replace(/[^a-z]/g,""));return t===-1?Number.MAX_SAFE_INTEGER:t};export function buildFacetSummaryChips(e,t=3){if(!e.length)return null;const i=l("div",{display:"flex",flexWrap:"wrap",alignItems:"baseline",gap:`${x.xs} ${x.lg}`}),n=[...e].sort((m,p)=>ye(m.key)-ye(p.key)),o=n.filter(m=>ye(m.key)!==Number.MAX_SAFE_INTEGER),u=n.filter(m=>ye(m.key)===Number.MAX_SAFE_INTEGER),s=m=>{const p=l("span",{whiteSpace:"nowrap"}),r=l("span",{color:Y},`${Ee(m.key)}: `);p.appendChild(r);const a=m.values.slice(0,t),b=m.values.length-a.length;let f=a.map(w=>w.value).join(", ");return b>0&&(f+=` +${b} more`),p.appendChild(document.createTextNode(f)),p};for(const m of o)i.appendChild(s(m));if(u.length){const m=l("span",{display:"none",flexWrap:"wrap",alignItems:"baseline",gap:`${x.xs} ${x.lg}`});for(const a of u)m.appendChild(s(a));const p=l("button",{background:"none",border:"none",padding:"0",cursor:"pointer",color:$.iconActive,fontSize:T.body,whiteSpace:"nowrap"});p.type="button",G(p,"bare");const r=a=>{m.style.display=a?"contents":"none",p.textContent=a?"less":`+${u.length} more`,p.setAttribute("aria-expanded",String(a))};r(!1),p.addEventListener("click",()=>r(p.getAttribute("aria-expanded")!=="true")),i.appendChild(m),i.appendChild(p)}return i}export function buildMetadataStripRow(e,t,i){const n=wt(e,i),o=buildFacetSummaryChips(n);if(!o&&!t)return null;const u=l("div",{display:"flex",flexWrap:"wrap",alignItems:"baseline",gap:"4px 14px"});if(t){const s=l("span",{fontWeight:"500",whiteSpace:"nowrap"},t.aggrMethod?`${t.lotSize} wafers stacked \xB7 ${t.aggrMethod}`:`${t.lotSize} wafers stacked`);u.appendChild(s)}return o&&u.appendChild(o),u}const Kt={display:"flex",flexDirection:"column",gap:x.sm,background:$.menuBg,border:`1px solid ${$.menuBorder}`,borderRadius:q.container,padding:`${x.sm} ${x.lg}`,boxShadow:ze.panel,fontSize:T.body,lineHeight:te.none,boxSizing:"border-box",width:"100%",minWidth:"0"};export function buildMetadataStripBox(e,t,i){const n=buildMetadataStripRow(e,t,i);if(!n)return null;const o=l("div",Kt);return o.appendChild(n),o}export function buildCompactMetadataRows(e){const t=metadataEntries(e);if(!t.length)return null;const i=l("div");for(const[n,o]of t)i.appendChild(_t(Ee(n),o));return i}export function buildMetadataInfoSection(e,t){const i=buildMetadataStripRow(e,t,{facetableOnly:e.length>1});if(!i)return null;const n=l("div");return n.appendChild(se("Wafer Info")),n.appendChild(i),n}export function buildYieldSection(e,t,i=[1]){const n=l("div");n.appendChild(se("Summary"));const o=Ue();if(o.appendChild(pe(String(e.totalDies),"Total dies")),e.partialDies>0&&o.appendChild(pe(String(e.partialDies),"Partial")),e.yieldPercent!==null){const u=lt([i]);o.appendChild(pe(`${e.yieldPercent.toFixed(1)}%`,"Yield",`pass: ${u}`))}return o.finish(),n.appendChild(o),e.edgeExcludedDies>0&&n.appendChild(Qe("Edge excluded (outer zone)",String(e.edgeExcludedDies))),n}export function buildBinSection(e,t,i,n,o,u){const s=new Map;if(o)for(const[r,a]of Object.entries(o))s.set(Number(r),a);else for(const r of e){if(r.partial||r.edgeExcluded)continue;const a=i==="hard"?r.hbin:r.sbin;a!=null&&s.set(a,(s.get(a)??0)+1)}if(!s.size)return null;const m=n??Re(e,{passBins:u,...i==="hard"?{hbinDefs:t}:{sbinDefs:t}}),p=l("div");p.appendChild(se(Xe(i,[...s.values()].reduce((r,a)=>r+a,0))));for(const r of Ze(s,t,m[i],m.pass[i]))p.appendChild(r);return p}function Xe(e,t){return`${e==="hard"?"Hard":"Soft"} Bin Breakdown \u2014 % of dies (N=${t})`}function Ze(e,t,i,n){const o=[...e.values()].reduce((m,p)=>m+p,0),u=t?new Map(t.map(m=>[m.bin,m])):null;return Mt(e.entries(),n).map(([m,p])=>{const r=u?.get(m),a=r?.name?`Bin ${m} \xB7 ${r.name}`:`Bin ${m}`,b=p/o*100,f=i.get(m)??Lt;return Le(`${a} (${p})`,b,f)})}export function buildBinBreakdownSection(e){const{dies:t,hbinDefs:i,sbinDefs:n,hardCounts:o,softCounts:u,plotMode:s,passBins:m=[1],panel:p}=e,r=e.binColors??Re(t,{passBins:m,hbinDefs:i,sbinDefs:n}),a=v=>{const g=v==="hard"?o:u,h=new Map;if(g){for(const[y,S]of Object.entries(g))h.set(Number(y),S);return h}for(const y of t){if(y.partial||y.edgeExcluded)continue;const S=v==="hard"?y.hbin:y.sbin;S!=null&&h.set(S,(h.get(S)??0)+1)}return h},b=a("hard"),f=a("soft"),w=b.size>0,k=f.size>0;if(!w&&!k)return null;const F=xe(p),H=s==="softBin"||s==="stackedSoftBins"?"soft":s==="hardBin"||s==="stackedBins"?"hard":void 0;let I=F.binMode??H??(w?"hard":"soft");I==="hard"&&!w&&(I="soft"),I==="soft"&&!k&&(I="hard");const O=I==="hard"?b:f,z=I==="hard"?i:n,C=[...O.values()].reduce((v,g)=>v+g,0),{outer:d}=oe(Xe(I,C),!0,void 0,{stateKey:"bins",panel:p,render:v=>{const g=I==="hard"?r.hard:r.soft;for(const h of Ze(O,z,g,r.pass[I]))v.appendChild(h)},control:w&&k&&p?()=>ke([["hard","Hard"],["soft","Soft"]],I,v=>{F.binMode=v;const g=buildBinBreakdownSection(e);g&&d.replaceWith(g)},p.ownerDocument,!0):void 0});return d}export function buildLotBinSection(e,t,i,n){return buildBinSection(e,t,i,n)}export function buildRegionYieldPanelSection(e){const{diesByWafer:t,allWafers:i,ringCount:n,passBins:o,panel:u}=e,s=xe(u),m=a=>ht(t,i,n,o,a==="ring"?mt:gt);if(!m("ring").length&&!m("quadrant").length)return null;const p=s.regionMode,{outer:r}=oe(p==="ring"?"Ring Yield":"Quadrant Yield",!0,void 0,{stateKey:"regionYield",panel:u,render:a=>{const b=m(p);if(!b.length){a.appendChild(l("div",{fontSize:T.body,color:Y},"No positioned dies for this breakdown."));return}for(const{label:f,n:w,yieldPercent:k}of b)a.appendChild(Le(`${f} (N=${w})`,k))},control:u?()=>ke([["ring","Ring"],["quadrant","Quadrant"]],p,a=>{s.regionMode=a;const b=buildRegionYieldPanelSection(e);b&&r.replaceWith(b)},u.ownerDocument,!0):void 0});return r}export function buildLotOverviewSection(e,t=[]){const i=l("div"),n=Me(e.perWafer.map(s=>s.summary.wafer));i.appendChild(se(n.lotId!==void 0?"Lot Summary":"Summary"));const o=Ue();o.appendChild(pe(String(e.stats.waferCount),"Wafers"));const u=e.perWafer.map(s=>s.summary.stats.yieldPercent).filter(s=>s!==null);if(u.length){const s=u.reduce((m,p)=>m+p,0)/u.length;o.appendChild(pe(`${s.toFixed(1)}%`,"Mean per-wafer yield"))}if(o.finish(),i.appendChild(o),t.length){const s=t.reduce((p,r)=>p+r.stats.analyzedDies,0),m=t.reduce((p,r)=>p+r.stats.excludedDies,0);(s||m)&&i.appendChild(Qe("Dies analysed",m?`${s.toLocaleString()} (${m.toLocaleString()} excluded)`:s.toLocaleString()))}return i}export function buildPerWaferYieldSection(e,t,i,n,o){const u=e.perWafer.map(C=>({waferIndex:C.waferIndex,label:ve(t[C.waferIndex],C.waferIndex).replace(/\s*·\s*\d+(\.\d+)?%$/,""),yieldPct:C.summary.stats.yieldPercent})).filter(C=>C.yieldPct!==null);if(!u.length)return null;const s=zt(u.map(C=>C.yieldPct)),m=Wt(u.map(C=>C.yieldPct)),p=s===m?"":` (${s.toFixed(1)}\u2013${m.toFixed(1)}%)`,r=[...u.map(C=>C.yieldPct)].sort((C,d)=>C-d),a=Pt(r),b=We(r,.25),f=We(r,.75),w=u.length>=5?b-1.5*(f-b):-1/0,k=xe(n),F=C=>{const d=k.waferSort==="yield"?[...u].sort((v,g)=>v.yieldPct-g.yieldPct):u;for(const{waferIndex:v,label:g,yieldPct:h}of d){const S=h<w?`${g} \xB7 low outlier`:g,L=Le(S,h,void 0,void 0,a),A=o?.(v);A?.total&&O(L,A),z(L,v,g,h,A),C.appendChild(L)}},H=` \xB7 median ${a.toFixed(1)}%`,{outer:I}=oe("Wafer Yield"+p+H,!0,void 0,{stateKey:"waferYield",panel:n,render:F,control:n?C=>ke([["slot","Slot"],["yield","Yield"]],k.waferSort,d=>{k.waferSort=d,C()},n.ownerDocument,!0):void 0});return I;function O(C,d){const v=d.unusual?"unusual":d.notable?"notable":"info",g=l("span",{marginLeft:x.sm,flexShrink:"0",background:ge(v),color:"#fff",borderRadius:q.container,padding:"0 5px",fontSize:T.body,fontWeight:"600"},`${d.total}`);g.setAttribute("aria-hidden","true"),ce(g,`${d.total} finding${d.total===1?"":"s"}`+(d.unusual?`, ${d.unusual} unusual`:"")+(d.notable?`, ${d.notable} notable`:"")),C.firstElementChild?.firstElementChild?.appendChild(g)}function z(C,d,v,g,h){i&&(C.style.cursor="pointer",C.style.borderRadius="4px",C.style.padding="2px 3px",C.style.marginLeft="-3px",C.style.marginRight="-3px",C.setAttribute("role","button"),C.setAttribute("aria-label",`${v}, ${g.toFixed(1)}% yield`+(h?.total?`, ${h.total} finding${h.total===1?"":"s"}${h.unusual?`, ${h.unusual} unusual`:""}`:"")+" \u2014 open wafer"),C.tabIndex=0,C.addEventListener("mouseenter",()=>{C.style.background=$.bgHover}),C.addEventListener("mouseleave",()=>{C.style.background=""}),C.addEventListener("focus",()=>{C.style.background=$.bgHover}),C.addEventListener("blur",()=>{C.style.background=""}),C.addEventListener("click",()=>i(d)),C.addEventListener("keydown",y=>{(y.key==="Enter"||y.key===" ")&&(y.preventDefault(),i(d))}))}}function Vt(e){return Ft([...e].sort((t,i)=>t-i))}function Je(e,t){if(!e)return[];const i=e.waferMetadata??(e.perWaferMetadata?Fe(e.perWaferMetadata.map(n=>({metadata:n}))):void 0);return!i||!Object.keys(i).length?[]:$t({waferMetadata:i,waferKeys:"identity",waferPlacement:"csv",reservedLabels:t}).columns}function et(e){return e?.populationLabel?[{key:"population",label:"Population",scope:"wafer",constant:e.populationLabel,get:()=>e.populationLabel,csvOnly:!0}]:[]}export function buildTestSection(...e){return be(buildTestSectionSteps(...e))}export function*buildTestSectionSteps(e,t,i,n,o,u,s,m,p="full"){const r=e.filter(c=>!c.partial&&!c.edgeExcluded),a=new Map((n?.perTestStats??[]).map(c=>[c.testNumber,c])),b=new Map((n?.testSpecYield??[]).map(c=>[c.testNumber,c]));let f;if(t?.length?f=t.filter(dt).map(c=>({testNumber:c.testNumber,name:c.name,unit:c.unit,limitLow:c.limitLow,limitHigh:c.limitHigh,limitLowInclusive:c.limitLowInclusive,limitHighInclusive:c.limitHighInclusive,...we(c)})):f=Et(r).map(W=>({testNumber:W,name:ct(void 0,W)})),!f.length)return null;const w=f.filter(c=>r.some(W=>{const j=W.testValues?.[c.testNumber];return j!==void 0&&isFinite(j)}));if(!w.length)return null;const k=w.some(c=>c.limitLow!==void 0||c.limitHigh!==void 0),F=[],H=c=>!!c&&c.stddev!==void 0&&c.median!==void 0&&c.q1!==void 0&&c.q3!==void 0,I=c=>c.limitLow!==void 0||c.limitHigh!==void 0,O=!!(s?.length&&t?.length),C=(O||w.some(c=>!H(a.get(c.testNumber))||I(c)&&!b.has(c.testNumber)))&&t?.length?yield*Rt(s??[{dies:e}],t):void 0;for(const c of w){yield;const W=a.get(c.testNumber),j=C?.specTally.get(c.testNumber),ee=C?.stats.get(c.testNumber),K=()=>r.map(E=>E.testValues?.[c.testNumber]).filter(E=>E!==void 0&&isFinite(E));let Q;if(H(W)){const E=W;Q={testNumber:c.testNumber,min:E.min,max:E.max,mean:E.mean,count:E.count,stddev:E.stddev,median:E.median,q1:E.q1,q3:E.q3}}else if(ee)Q={testNumber:c.testNumber,...ee};else{const E=K();if(!E.length)continue;Q={testNumber:c.testNumber,...Vt(E)}}let M=null,B=0;if(I(c)){const E=b.get(c.testNumber);if(E)({yieldPercent:M,totalDies:B}=E);else if(j)B=j.n,M=B>0?(B-j.fail)/B*100:null;else{const X=K();let ue=0;for(const ie of X)Nt(Bt(ie,c))&&ue++;B=X.length,M=X.length>0?(X.length-ue)/X.length*100:null}}F.push({entry:c,stats:Q,specYieldPct:M,specN:B})}if(!F.length)return null;const d=new Map;for(const c of(O?C?.capability:void 0)??[])c.hasSpec&&d.set(c.testNumber,c.ppk);const v=d.size>0,h=new Set(F.map(c=>c.stats.count)).size===1?F[0].stats.count:null,y=l("div"),S=l("div",{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:x.sm}),L=h!==null?` \xB7 N=${h.toLocaleString()}`:"",A=u?.populationLabel?`Test Values (${F.length}) \u2014 ${u.populationLabel}${L}`:`Test Values (${F.length})${L}`,D=se(A);D.style.marginBottom="0",S.appendChild(D);const R=document.createElement("button");R.type="button",R.textContent="Test values CSV",Object.assign(R.style,{...me("outlined")}),G(R),R.addEventListener("click",()=>{const c=["Test","Unit","N","Min","Q1","Median","Mean","Q3","Max","StdDev"];v&&c.push("Ppk");const W=F.some(B=>B.specYieldPct!==null&&B.specN!==B.stats.count);k&&(c.push("Lo limit","Hi limit","Limit Yield %"),W&&c.push("Limit Yield N"));const j=F.some(B=>Te(B.entry));j&&c.push(De);const ee=[...Je(u,c),...et(u)],Q=[[...ee.map(B=>B.label),...c].map(he).join(",")],M=B=>Ae(B,void 0,"engineering");for(const{entry:B,stats:E,specYieldPct:X,specN:ue}of F){const ie=[...ee.map(le=>le.constant??""),B.name,B.unit??"",String(E.count),M(E.min),M(E.q1),M(E.median),M(E.mean),M(E.q3),M(E.max),M(E.stddev)];if(v){const le=d.get(B.testNumber);ie.push(le==null?"":le.toFixed(3))}k&&(ie.push(B.limitLow!==void 0?M(B.limitLow):"",B.limitHigh!==void 0?M(B.limitHigh):"",X!==null?X.toFixed(1):""),W&&ie.push(B.limitLow!==void 0||B.limitHigh!==void 0?String(ue):"")),j&&ie.push(Ne(B)),Q.push(ie.map(he).join(","))}_e(Q.join(`
2
+ `),"test-values.csv","text/csv",o)}),S.appendChild(R),y.appendChild(S);const P=l("table",{width:"100%",borderCollapse:"collapse",fontSize:T.body}),Z=document.createElement("thead"),J=document.createElement("tr"),_=["Test"];h===null&&_.push("N"),p==="full"&&_.push("Min","Q1","Median"),_.push("Mean"),p==="full"&&_.push("Q3","Max","StdDev"),v&&_.push("Ppk"),p==="full"&&k&&_.push("Lo limit","Hi limit"),k&&_.push("Limit yield");for(const c of _){const W=l("th",{textAlign:c==="Test"?"left":"right",fontWeight:"600",fontSize:T.meta,letterSpacing:fe,textTransform:"uppercase",color:Y,padding:"3px 8px 5px",borderBottom:`1px solid ${$.menuBorder}`,whiteSpace:"nowrap"},c);J.appendChild(W)}Z.appendChild(J),P.appendChild(Z);const U=document.createElement("tbody");for(const{entry:c,stats:W,specYieldPct:j,specN:ee}of F){const K=B=>Ae(B,c.unit,i),Q=document.createElement("tr"),M=(B,E="right")=>{const X=l("td",{textAlign:E,padding:`${x.xs} ${x.md}`,borderBottom:`1px solid ${$.menuBorder}`,color:$.text,whiteSpace:"nowrap"},B);Q.appendChild(X)};if(M(pt(c,c.testNumber),"left"),h===null&&M(`${W.count}`),p==="full"&&(M(K(W.min)),M(K(W.q1)),M(K(W.median))),M(K(W.mean)),p==="full"&&(M(K(W.q3)),M(K(W.max)),M(K(W.stddev))),v){const B=d.get(c.testNumber);M(B==null?"\u2014":B.toFixed(2))}p==="full"&&k&&(M(c.limitLow!==void 0?K(c.limitLow):"\u2014"),M(c.limitHigh!==void 0?K(c.limitHigh):"\u2014")),k&&M(j!==null?h!==null&&ee===h?`${j.toFixed(1)}%`:`${j.toFixed(1)}% (N=${ee})`:"\u2014"),U.appendChild(Q)}P.appendChild(U);const N=l("div",{overflowX:"auto"});N.appendChild(P);const V=ot(N,F.map(c=>({label:c.entry.name,...we(c.entry)})));if(!m)return y.appendChild(V),y;S.remove();const{outer:re}=oe(A,!0,void 0,{stateKey:"testValues",panel:m,render:c=>c.appendChild(V),control:()=>R});return re}export function buildFunctionalTestSection(e,t,i,n,o,u){const s=i??bt(e,t);if(!s?.length)return null;const p=new Set(s.map(g=>g.totalDies)).size===1?s[0].totalDies:null,r=p!==null?` \xB7 N=${p.toLocaleString()}`:"",a=l("div"),b=l("div",{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:x.sm}),f=o?.populationLabel?`Functional Tests (${s.length}) \u2014 ${o.populationLabel}${r}`:`Functional Tests (${s.length})${r}`,w=se(f);w.style.marginBottom="0",b.appendChild(w);const k=document.createElement("button");k.type="button",k.textContent="Functional CSV",Object.assign(k.style,{...me("outlined")}),G(k),k.addEventListener("click",()=>{const g=["Test","N","Pass","Fail","Pass Rate %"],h=s.some(L=>Te(L));h&&g.push(De);const y=[...Je(o,g),...et(o)],S=[[...y.map(L=>L.label),...g].map(he).join(",")];for(const L of s)S.push([...y.map(A=>A.constant??""),ut(L.label),String(L.totalDies),String(L.passDies),String(L.failDies),L.passRatePercent!==null?L.passRatePercent.toFixed(1):"",...h?[Ne(L)]:[]].map(he).join(","));_e(S.join(`
3
+ `),"functional-tests.csv","text/csv",n)}),b.appendChild(k),a.appendChild(b);const F=l("table",{width:"100%",borderCollapse:"collapse",fontSize:T.body}),H=document.createElement("thead"),I=document.createElement("tr"),O=p===null?["Test","N","Pass","Fail","Pass rate"]:["Test","Pass","Fail","Pass rate"];for(const g of O)I.appendChild(l("th",{textAlign:g==="Test"?"left":"right",fontWeight:"700",fontSize:T.body,letterSpacing:fe,textTransform:"uppercase",color:Y,padding:"3px 8px 5px",borderBottom:`1px solid ${$.menuBorder}`,whiteSpace:"nowrap"},g));H.appendChild(I),F.appendChild(H);const z=document.createElement("tbody");for(const g of s){const h=document.createElement("tr"),y=(S,L="right")=>{h.appendChild(l("td",{textAlign:L,padding:`${x.xs} ${x.md}`,borderBottom:`1px solid ${$.menuBorder}`,color:ne,whiteSpace:"nowrap"},S))};y(g.label,"left"),p===null&&y(String(g.totalDies)),y(String(g.passDies)),y(String(g.failDies)),y(g.passRatePercent!==null?`${g.passRatePercent.toFixed(1)}%`:"\u2014"),z.appendChild(h)}F.appendChild(z);const C=l("div",{overflowX:"auto"});C.appendChild(F);const d=ot(C,s);if(!u)return a.appendChild(d),a;b.remove();const{outer:v}=oe(f,!0,void 0,{stateKey:"functionalTests",panel:u,render:g=>g.appendChild(d),control:()=>k});return v}export function buildLotFunctionalSection(e,t,i,n,o){const u=i?.length?{perWaferMetadata:i.map(m=>m.wafer??{}),populationLabel:`${i.length} wafer${i.length===1?"":"s"} pooled`}:void 0,s=It(i);return s?buildFunctionalTestSection(e,t,s,n,u,o):buildFunctionalTestSection(e,t,void 0,n,u,o)}const ae={unusual:0,notable:1,info:2};function Be(e){return l("span",{display:"inline-block",width:"7px",height:"7px",borderRadius:q.pill,background:ge(e),flexShrink:"0"})}function tt(e,t){let i=e.summary;if(t){const n=[" has "," have "].find(o=>i.startsWith(`${t}${o}`));n?i=i.slice(t.length+n.length):i.startsWith(`${t} `)&&(i=i.slice(t.length+1))}return i=de(i),i.length?i[0].toUpperCase()+i.slice(1):i}function nt(e){return it(e.map(t=>({label:t.variable.label,...we(t.variable)})))}function it(e){const t=ft(e);return t?l("div",{fontSize:T.body,color:Y,marginTop:x.sm,lineHeight:te.base},t):null}function ot(e,t){const i=it(t);if(!i)return e;const n=l("div",{});return n.append(e,i),n}export function buildFindingsSection(e,t,i,n,o=!1){if(!e.length)return null;const u=e.some(d=>d.severity==="unusual"||d.severity==="notable"),s=u?e.filter(d=>d.severity!=="info").length.toString():void 0,{outer:m,content:p}=o?(()=>{const d=l("div");return{outer:d,content:d}})():oe(`Findings (${e.length})`,u,s);function r(d){return d.reduce((v,g)=>ae[g.severity]<ae[v]?g.severity:v,"info")}function a(d){const v=new Map;for(const h of d){const y=`${h.comparison.family}\0${h.comparison.left}`,S=v.get(y)??[];S.push(h),v.set(y,S)}const g=[...v.entries()].map(([h,y])=>{const S=[...y].sort((L,A)=>ae[L.severity]-ae[A.severity]);return{key:h,findings:S,worst:r(S)}});return g.sort((h,y)=>ae[h.worst]-ae[y.worst]),g}function b(d,v){const h={ring:"Ring",quadrant:"Quadrant",sector:"Sector","reticle-position":"Reticle","test-site":"Test site",cluster:"Cluster","edge-arc":"Edge arc",wafer:"Wafer"}[d]??d;return v.toLowerCase().startsWith(h.toLowerCase())?v:`${h}: ${v}`}const f=e.filter(d=>d.comparison.family==="spatial-pattern"),w=new Set([...f.flatMap(d=>d.relatedIds??[]),...e.flatMap(d=>d.absorbedIds??[])]),k=e.filter(d=>d.comparison.family!=="spatial-pattern"&&!w.has(d.id)),F=a(k),H=()=>[...F.flatMap(d=>d.findings),...f.flatMap(d=>e.filter(v=>d.relatedIds?.includes(v.id)))];function I(d,v=!1,g){const h=n===d.id,y=document.createElement("button");return y.type="button",y.dataset.wmapFinding=d.id,y.textContent=tt(d,g),ce(y,vt(d)),y.setAttribute("aria-current",h?"true":"false"),Object.assign(y.style,{border:`1px solid ${$.menuBorder}`,borderLeft:`3px solid ${ge(d.severity)}`,background:h?$.bgActive:$.menuBg,borderRadius:q.container,padding:v?"6px 10px":"8px 10px",textAlign:"left",fontSize:v?"10px":"11px",fontWeight:h?"600":"400",color:$.text,cursor:"pointer",width:"100%",marginBottom:x.xs}),G(y,"bare"),y.addEventListener("click",()=>i(d,y)),y}const O=de(Ct(e)??"");if(O){const d=l("div",{background:$.bgActive,borderRadius:q.container,padding:`${x.md} ${x.lg}`,marginBottom:x.md,display:"flex",gap:x.md,alignItems:"flex-start"}),v=l("span",{flex:"1",fontSize:T.body,lineHeight:te.base,color:$.text},O);d.appendChild(v);const g=l("button",{flexShrink:"0",border:"none",background:"none",fontSize:T.body,color:$.icon,cursor:"pointer",padding:"0",lineHeight:te.base,whiteSpace:"nowrap"},"Detail \u25B8");g.type="button",G(g,"bare"),g.addEventListener("click",()=>{const h=Oe({title:"Findings Summary",onClose:()=>{},anchor:g}),y=l("p",{fontSize:"16px",lineHeight:te.base,color:$.text,padding:`${x.xxl} ${x.xxxl} ${x.xxl}`,margin:"0",borderBottom:`1px solid ${$.menuBorder}`,flexShrink:"0"},O);h.contentWrap.appendChild(y);const S=l("div",{overflowY:"auto",padding:`${x.xxl} ${x.xxxl}`,flex:"1"}),L=(D,R)=>{const P=l("div",{display:"flex",alignItems:"center",gap:"7px",fontSize:T.sub,fontWeight:"600",color:ne,marginTop:x.lg,marginBottom:x.xs});return P.appendChild(Be(D)),P.appendChild(l("span",{},R)),P};for(const D of f){S.appendChild(L(D.severity,D.comparison.left)),S.appendChild(l("div",{fontSize:T.body,color:$.text,padding:"4px 0 4px 15px",marginBottom:x.xxs},de(D.summary)));const R=e.filter(P=>D.relatedIds?.includes(P.id));for(const P of R)S.appendChild(l("div",{fontSize:T.body,color:Y,padding:"2px 0 2px 23px",marginBottom:x.xxs},de(P.summary)))}for(const D of F){const[R,P]=D.key.split("\0");S.appendChild(L(D.worst,b(R,P)));for(const Z of D.findings)S.appendChild(l("div",{fontSize:T.body,color:$.text,padding:"4px 0 4px 15px",marginBottom:x.xxs},tt(Z,P)))}const A=nt(H());A&&S.appendChild(A),h.contentWrap.appendChild(S),Object.assign(h.contentWrap.style,{flexDirection:"column",overflow:"hidden"})}),d.appendChild(g),p.appendChild(d)}let z=!0;for(const d of f){z||p.appendChild(l("div",{height:"1px",background:$.separator,margin:"4px 0"})),z=!1;const v=e.filter(L=>d.relatedIds?.includes(L.id)),g=v.length>0,h=l("div",{position:"relative",marginBottom:g?"2px":"4px"}),y=n===d.id,S=document.createElement("button");if(S.type="button",S.dataset.wmapFinding=d.id,Object.assign(S.style,{border:`1px solid ${$.menuBorder}`,borderLeft:`3px solid ${ge(d.severity)}`,background:y?$.bgActive:$.menuBg,borderRadius:q.container,padding:"8px 32px 8px 10px",textAlign:"left",fontSize:T.body,fontWeight:y?"600":"500",color:$.text,cursor:"pointer",width:"100%"}),S.textContent=de(d.summary),ce(S,d.summary),S.setAttribute("aria-current",y?"true":"false"),G(S,"bare"),S.addEventListener("click",()=>i(d,S)),h.appendChild(S),g){const L=l("div",{display:"none",paddingLeft:"12px",marginBottom:x.xs});for(const R of v)L.appendChild(I(R,!0));h.appendChild(L);let A=!1;const D=l("button",{position:"absolute",top:"50%",right:"8px",transform:"translateY(-50%)",border:"none",background:"none",fontSize:T.body,color:$.icon,cursor:"pointer",padding:`${x.xxs} ${x.xs}`,lineHeight:te.none},"\u25B8");G(D,"bare"),D.type="button",ce(D),D.setAttribute("aria-label","Show supporting findings"),D.setAttribute("aria-expanded","false"),D.addEventListener("click",R=>{R.stopPropagation(),A=!A,L.style.display=A?"block":"none",D.textContent=A?"\u25BE":"\u25B8";const P=A?"Hide supporting findings":"Show supporting findings";D.setAttribute("aria-label",P),D.setAttribute("aria-expanded",String(A))}),h.appendChild(D)}p.appendChild(h)}for(const d of F){z||p.appendChild(l("div",{height:"1px",background:$.separator,margin:"4px 0"})),z=!1;const[v,g]=d.key.split("\0"),h=l("div",{display:"flex",alignItems:"center",gap:x.sm,fontSize:T.body,fontWeight:"600",color:ne,margin:"2px 0 4px"});h.appendChild(Be(d.worst)),h.appendChild(l("span",{},b(v,g))),p.appendChild(h);for(const y of d.findings)p.appendChild(I(y,!1,g))}const C=nt(H());return C&&p.appendChild(C),m}const qt=[{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"}],Gt=[{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"}],Ut=["unusual","notable","info"],Qt={unusual:"Unusual",notable:"Notable",info:"Info"};function Xt(e,t,i){const n=l("div",{display:"flex",flexWrap:"wrap",alignItems:"center",gap:x.lg,marginBottom:x.lg,paddingBottom:"10px",borderBottom:`1px solid ${$.separator}`}),o={unusual:0,notable:0,info:0};for(const r of e)o[r.severity]++;const u=Ut.filter(r=>o[r]>0),s=new Set(t.severity===void 0?u:Array.isArray(t.severity)?t.severity:[t.severity]),m=l("div",{display:"flex",gap:x.xs,flexWrap:"wrap"});for(const r of u){const a=document.createElement("button");a.type="button";const b=()=>{const f=s.has(r);a.setAttribute("aria-pressed",f?"true":"false"),Object.assign(a.style,{display:"inline-flex",alignItems:"center",gap:"5px",border:`1px solid ${$.menuBorder}`,borderRadius:q.pill,background:f?$.bgActive:"none",opacity:f?"1":"0.5",color:$.text,fontSize:T.body,padding:`${x.xxs} ${x.md}`,cursor:"pointer"}),a.dataset.on=f?"true":"false"};a.appendChild(Be(r)),a.appendChild(l("span",{},`${Qt[r]} ${o[r]}`)),ce(a,()=>s.has(r)?"Click to hide these findings":"Click to show these findings"),b(),G(a,"bare"),a.addEventListener("click",()=>{s.has(r)?s.delete(r):s.add(r),t.severity=s.size===u.length?void 0:[...s],i()}),m.appendChild(a)}n.appendChild(m);const p=t.kind!==void 0||t.family!==void 0;return(e.length>=Zt||p)&&(n.appendChild(Pe("Kind:",qt,t.kind??"",r=>{t.kind=r||void 0,i()},{maxWidth:"130px"})),n.appendChild(Pe("Region:",Gt,t.family??"",r=>{t.family=r||void 0,i()},{maxWidth:"150px"}))),n}const Zt=8;function Jt(e,t){const i=l("div",{display:"flex",flexDirection:"column",gap:x.xs,padding:x.md,marginBottom:x.md,background:$.bgHover,borderRadius:q.control},void 0,t);if(i.appendChild(l("div",{fontSize:T.body,color:$.value},e.message,t)),e.detail&&i.appendChild(l("div",{fontSize:T.meta,color:Y},e.detail,t)),e.actionLabel&&e.onAction){const n=l("button",me("outlined"),e.actionLabel,t);n.type="button",n.style.alignSelf="flex-start",n.style.marginTop=x.xs,G(n),n.addEventListener("click",e.onAction),i.appendChild(n)}return i}export function buildFindingsSectionWithFilter(e,t,i,n,o,u,s){if(!e.findings.length&&!s)return null;const m=e.findings.some(k=>k.severity==="unusual"||k.severity==="notable"),p=m?e.findings.filter(k=>k.severity!=="info").length.toString():void 0,r=u?.ownerDocument??document,{outer:a,content:b}=oe(`Findings (${e.findings.length})`,m||!!s,p,{stateKey:"findings",panel:u});if(s&&b.appendChild(Jt(s,r)),!e.findings.length)return a;b.appendChild(Xt(e.findings,n,o));const f=St(e,n);if(!f.length)return b.appendChild(l("div",{color:Y,fontSize:T.body,textAlign:"center",padding:`${x.xxl} ${x.md}`},"No findings match the current filter.")),a;const w=buildFindingsSection(f,e,t,i,!0);return w&&b.appendChild(w),a}export function buildLotTestSection(...e){return be(buildLotTestSectionSteps(...e))}export function*buildLotTestSectionSteps(e,t,i,n,o,u,s,m="full"){const p=n?.length?{perWaferMetadata:n.map(a=>a.wafer??{}),populationLabel:`${n.length} wafer${n.length===1?"":"s"} pooled`}:void 0;let r;if(n?.length&&n.every(a=>a.stats.perTestStats!==void 0)){const a=new Map;for(const b of n)for(const f of b.stats.perTestStats??[]){const w=a.get(f.testNumber);w?(w.n+=f.count,w.sum+=f.mean*f.count,w.min=Math.min(w.min,f.min),w.max=Math.max(w.max,f.max)):a.set(f.testNumber,{n:f.count,sum:f.mean*f.count,min:f.min,max:f.max})}r={perTestStats:[...a.entries()].map(([b,f])=>({testNumber:b,count:f.n,min:f.min,max:f.max,mean:f.sum/f.n}))}}if(n?.length&&n.every(a=>a.stats.testSpecYield!==void 0)){const a=new Map;for(const b of n)for(const f of b.stats.testSpecYield??[]){const w=a.get(f.testNumber);w?(w.passDies+=f.passDies,w.totalDies+=f.totalDies):a.set(f.testNumber,{passDies:f.passDies,totalDies:f.totalDies})}r={...r,testSpecYield:[...a.entries()].map(([b,f])=>({testNumber:b,totalDies:f.totalDies,yieldPercent:f.totalDies>0?f.passDies/f.totalDies*100:null}))}}return yield*buildTestSectionSteps(e,t,i,r,o,p,u,s,m)}export function createSummaryPanelEl(e,t,i=document){const n=e==="top"||e==="bottom",o=l("div",{background:Ot,color:$.text,fontVariantNumeric:"tabular-nums",border:je,borderRadius:q.container,padding:x.xl,overflowY:"auto",overflowX:n?"auto":"hidden",flexShrink:"0",boxSizing:"border-box",fontFamily:T.family,fontSize:T.sub,boxShadow:ze.panel},void 0,i);return e==="right"&&(o.style.marginRight=t),e==="left"&&(o.style.marginLeft=t),e==="bottom"&&(o.style.marginBottom=t),n?(o.style.height="180px",o.style.width="100%"):(o.style.width="300px",o.style.maxHeight="100%"),o}export function wrapWithSummaryPanel(e,t,i){const n=i==="top"||i==="bottom",o=l("div",{display:"flex",flexDirection:n?"column":"row",gap:x.md,width:"100%",height:"100%",flex:"1 1 0",minHeight:"0",boxSizing:"border-box",alignItems:n?"flex-start":"stretch"});return e.style.flex="1 1 0",e.style.minWidth="0",e.style.minHeight="0",i==="right"||i==="bottom"?(o.appendChild(e),o.appendChild(t)):(o.appendChild(t),o.appendChild(e)),o}function st(e){return l("div",{fontSize:T.heading,fontWeight:"600",color:ne,marginBottom:x.lg},e)}function Ce(e,t){const i=l("button",me("outlined"),e);return i.type="button",G(i),i.addEventListener("click",t),i}function at(...e){const t=e.filter(n=>!!n);if(!t.length)return null;const i=l("div",{display:"flex",flexWrap:"wrap",gap:x.sm,marginBottom:x.lg});for(const n of t)i.appendChild(n);return i}function rt(e,t,i,n,o,u,s,m,p,r,a){const b=e.ownerDocument,f=Oe({title:"Die list",onClose:()=>{},anchor:e,ownerDocument:b});f.contentWrap.style.padding="14px 16px";const w=At(t,i,{...s,title:n,onSaveText:m,waferMetadata:o,metadataFields:u,extraColumn:p,getWafer:r,ringCount:a,ownerDocument:b});w&&f.contentWrap.appendChild(w)}export function renderWaferSummaryContent(...e){be(renderWaferSummaryContentSteps(...e))}export function*renderWaferSummaryContentSteps(e,t){const i=e.scrollTop;e.innerHTML="";const{wafer:n,dies:o,yieldSummary:u,dataCoverage:s,hbinDefs:m,sbinDefs:p,testDefs:r,statsSummary:a,passBins:b=[1],ringCount:f=4,binColors:w,plotMode:k,fallbackFormat:F,onFindingClick:H,activeFindingId:I=null,findingsFilter:O,onFindingsFilterChange:z,findingsNotice:C,onSaveText:d,metadataFields:v,dieListOptions:g,metadataShownElsewhere:h}=t;e.appendChild(st("Wafer Summary"));const y=t.warnings??Ie({statsSummary:a});y.length&&e.appendChild($e(y,e.ownerDocument));const S=u&&s?Ce("Summary report",()=>{Ye(xt({wafer:n,dies:o,yield:u,dataCoverage:s,hbinDefs:m,sbinDefs:p,testDefs:r,passBins:b,ringCount:f},a),{anchor:e})}):null,L=(g?.enabled??!0)&&o.length?Ce("View die list",()=>{rt(e,o,r,`Die list \u2014 ${o.length} dies`,n.metadata,v,g,d,void 0,()=>n,f)}):null,A=at(S,L);A&&e.appendChild(A);let D=!0;const R=_=>{_&&(D||e.appendChild(Ge()),D=!1,e.appendChild(_))},P=a?.stats.isLotStack?a.stats:void 0,Z=P?.lotSize!==void 0?{lotSize:P.lotSize,aggrMethod:Dt(P.aggregationMethod)}:void 0;h||R(buildMetadataInfoSection([{metadata:n.metadata??void 0}],Z)),u&&s&&R(buildYieldSection(u,s,b)),yield,a&&H&&O&&z&&(R(buildFindingsSectionWithFilter(a,H,I,O,z,e,C)),yield),R(buildBinBreakdownSection({dies:o,hbinDefs:m,sbinDefs:p,binColors:w,hardCounts:a?.stats.hardBinCounts,softCounts:a?.stats.softBinCounts,plotMode:k,passBins:b,panel:e})),yield,R(buildRegionYieldPanelSection({diesByWafer:[o],allWafers:[n],ringCount:f,passBins:b,panel:e})),yield;const J=n.metadata?{waferMetadata:n.metadata}:void 0;R(yield*buildTestSectionSteps(o,r,F,a?.stats,d,J,[{dies:o}],e,"compact")),yield,R(buildFunctionalTestSection(o,r,a?.stats.functionalYield,d,J,e)),e.appendChild(l("div",{height:"12px",flexShrink:"0"})),e.scrollTop=i}export function reportMapsFromItems(e,t,i){const n=[];return e.forEach((o,u)=>{!o?.wafer||!o.dies||n.push({...o,wafer:o.wafer,dies:o.dies,label:ve(o,u),passBins:[...Se(o,t)],ringCount:o.ringCount??i})}),n}export function renderLotSummaryContent(...e){be(renderLotSummaryContentSteps(...e))}export function*renderLotSummaryContentSteps(e,t){const i=e.scrollTop;e.innerHTML="";const{lotSummary:n,items:o,hbinDefs:u,sbinDefs:s,testDefs:m,passBins:p=[1],ringCount:r=4,binColors:a,plotMode:b,fallbackFormat:f,onFindingClick:w,activeFindingId:k=null,onWaferClick:F,findingsFilter:H,onFindingsFilterChange:I,findingsNotice:O,onSaveText:z,dieListOptions:C,findingsFor:d}=t;e.appendChild(st(`Summary \u2014 ${Tt(Me(n.perWafer.map(N=>N.summary.wafer)))}`));const v=t.warnings??Ie({lotStatsSummary:n});v.length&&e.appendChild($e(v,e.ownerDocument));const g=Ce("Summary report",()=>{Ye(yt(reportMapsFromItems(o,p,r)),{anchor:e})}),h=[],y=[],S=[],L=[],A=[],D=new WeakMap,R=new WeakMap;for(let N=0;N<o.length;N++){yield;const V=o[N];if(!V){y.push([]);continue}V.wafer&&(h.push(V.wafer),L.push(V.dies??[]),A.push(Se(V,p)));const re=V.dies??[],c=ve(V,N);for(const W of re)D.set(W,c),V.wafer&&R.set(W,V.wafer);y.push(re);for(const W of re)S.push(W)}const P=C?.enabled??!0?Ce("View die list",()=>{S.length&&rt(e,S,m,`Die list \u2014 ${S.length} dies across ${o.length} wafer${o.length===1?"":"s"}`,Fe(o.filter(N=>!!N).map(N=>({metadata:N.wafer?.metadata}))),o.find(N=>N?.metadataFields?.length)?.metadataFields,C,z,{label:"Wafer",get:N=>D.get(N)},N=>R.get(N),r)}):null,Z=at(g,P);Z&&e.appendChild(Z);const J=o.map(N=>N?.statsSummary).filter(N=>!!N);let _=!0;const U=N=>{N&&(_||e.appendChild(Ge()),_=!1,e.appendChild(N))};U(buildLotOverviewSection(n,J)),yield,w&&H&&I&&(U(buildFindingsSectionWithFilter(n,w,k,H,I,e,O)),yield),U(buildPerWaferYieldSection(n,o,F,e,d)),yield,U(buildBinBreakdownSection({dies:S,hbinDefs:u,sbinDefs:s,plotMode:b,passBins:p,panel:e,binColors:a??kt(o.flatMap(N=>N?[{dies:N.dies??[],passBins:Se(N,p)}]:[]),{hbinDefs:u,sbinDefs:s}).colors})),yield,U(buildRegionYieldPanelSection({diesByWafer:L,allWafers:h,ringCount:r,passBins:N=>A[N],panel:e})),yield,m?.length&&(U(yield*buildLotTestSectionSteps(S,m,f,J,z,y.map(N=>({dies:N})),e,"compact")),yield,U(buildLotFunctionalSection(S,m,J,z,e))),e.appendChild(l("div",{height:"12px",flexShrink:"0"})),e.scrollTop=i}
@@ -1 +1 @@
1
- import{fontPx as Ct}from"./toolbar.js";import{findTestDef as _e,buildMapTitle as Be}from"../renderer/buildView.js";import{sortBinsForDisplay as eo}from"../stats/binPareto.js";import{affineInvert as oo,affineVector as Pe}from"../core/transforms.js";import{compareNatural as no,maxOf as Ut,minOf as De}from"../core/utils.js";import{resolveValueColorFn as so}from"../renderer/colorSchemes.js";import{NO_DATA_FILL as lo}from"../renderer/colorMap.js";import{SPEC_PASS_FILL as We,SPEC_FAIL_LOW as io,SPEC_FAIL_HIGH as Oe,contrastTextColor as Ie}from"../renderer/colorMap.js";import{fmt as Fe,fmtColorbarAxis as ao}from"../renderer/fmt.js";import{resolveCanvasTheme as co}from"./canvasTheme.js";import{hitGridDims as ro}from"./hitGrid.js";function Ne(g,e,P,M,r){g.moveTo(e,P+r*M),g.lineTo(e-M*.9,P-r*M*.6),g.lineTo(e+M*.9,P-r*M*.6),g.closePath()}const fo=new Set(["value","stackedValues","stackedBins","stackedSoftBins"]),ho=new Set(["hardBin","softBin","metadata"]),J=()=>`${Ct(-2)}px system-ui, sans-serif`,Ee=3,Kt=2,go=6,mo=20,uo=()=>`600 ${Ct()}px system-ui, sans-serif`,qt=()=>`${Ct(-1)}px system-ui, sans-serif`,po=()=>`600 ${Ct(-1)}px system-ui, sans-serif`,zt=128,xo=()=>`${Ct(-2)}px system-ui, sans-serif`,He=4,D=17,H=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 Jt=28,gt=5,ft=8,Ye=15,Ge=12,Xe=6;export function toCanvas(g,e,P={}){return drawMapCanvas(g,e,P)}export function drawMapCanvas(g,e,P={}){const M=co(g.parentElement??g),{padding:r=16,showColorbar:G=!0,markFailingDies:X=!1,colorbarWidth:B=16,background:mt=M.background,legendPosition:Z="default",legendOffset:ut,showAxes:pt=!1,diePitchMm:w,viewport:K,activeBin:N,hoverBin:q,fallbackFormat:Tt,minRightReserve:_t,hbinDefs:nt,sbinDefs:xt,metadataFields:yt,showTitle:U=!0}=P,W=Math.floor(g.clientWidth||g.width),Y=Math.floor(g.clientHeight||g.height),bt=W>=BIN_LEGEND_MIN_CANVAS_W&&Y>=BIN_LEGEND_MIN_CANVAS_H,St=G&&bt&&fo.has(e.plotMode)&&e.passFailDisplay==="off",Lt=G&&bt&&ho.has(e.plotMode),j=G&&bt&&e.plotMode==="value"&&(e.passFailDisplay==="spec"&&e.specCounts!==void 0||e.passFailDisplay==="test"&&e.passFailCounts!==void 0),Q=Lt||j,Bt=Lt&&e.plotMode!=="metadata"&&e.binCounts?eo(e.binCounts.entries(),e.plotMode==="softBin"?e.binColors.pass.soft:e.binColors.pass.hard):[],Pt=Lt&&e.plotMode==="metadata"&&e.metadataCounts?[...e.metadataColorMap?.keys()??[...e.metadataCounts.keys()].sort(no)].map(o=>[o,e.metadataCounts.get(o)??0]):[],v=[];if(j&&e.passFailDisplay==="test"){const o=e.passFailCounts;v.push({key:-1,color:We,label:"Pass",compactLabel:"Pass",count:o.pass,tooltip:`Pass \xB7 ${o.pass} dies`}),v.push({key:-2,color:Oe,label:"Fail",compactLabel:"Fail",count:o.fail,tooltip:`Fail \xB7 ${o.fail} dies`})}else if(j){const o=e.specCounts,f=_e(e.testDefs,e.activeTest);v.push({key:-1,color:We,label:"Pass",compactLabel:"Pass",count:o.pass,tooltip:`Pass \xB7 ${o.pass} dies`}),f?.limitHigh!==void 0&&v.push({key:-2,color:Oe,label:"Fail high",compactLabel:"Fail hi",count:o.failHigh,tooltip:`Fail high \xB7 ${o.failHigh} dies`}),f?.limitLow!==void 0&&v.push({key:-3,color:io,label:"Fail low",compactLabel:"Fail lo",count:o.failLow,tooltip:`Fail low \xB7 ${o.failLow} dies`})}const Dt=j?v.length:e.plotMode==="metadata"?Pt.length:Bt.length,y=(g.ownerDocument.defaultView??window).devicePixelRatio??1;if(W<=0||Y<=0)return{hitTarget:{getDieAtPoint:()=>null},viewport:{originX:0,originY:0,ppm:1,snapDist:1},binLegendRows:[]};const k=Z!=="default"?Z:W<BIN_LEGEND_ADAPT_FLOATING?"floating":W<BIN_LEGEND_ADAPT_COMPACT?"compact":"default",ht=k==="default"||k==="compact",de=k==="left",jt=k==="bottom",kt=k==="top",At=k==="floating",Wt=k==="compact"||k==="floating"?BIN_LEGEND_W_COMPACT:BIN_LEGEND_W,ge=Math.floor((Y-2*r)/D),me=Dt>ge?ge-1:Dt,ue=Q&&jt?me*D:0,Qt=Q&&kt?me*D:0,vt=e.plotMode==="stackedBins"||e.plotMode==="stackedSoftBins",te=_e(e.testDefs,e.activeTest),$e=vt?"Count":te?.name??(e.activeTest!=null?`Test ${e.activeTest}`:void 0),we=vt?void 0:te?.unit,[Ot,It]=e.valueRange,{tickFmt:Ve}=ao(It,$e,we,Tt),Ft=e.allIntegerValues?o=>String(Math.round(o)):Ve;let ee=0;if(St){const o=g.getContext("2d");o.font=J();const f=[Ft(Ot),Ft(It)];Ot<0&&f.push(Ft(-Math.max(Math.abs(Ot),Math.abs(It))));for(const s of f)ee=Math.max(ee,o.measureText(s).width)}const pe=Math.max(mo,Math.ceil(ee)+Ee+Kt+go-r),oe=St?B+pe+8:Q&&ht?Wt:0,ne=Q&&de?Wt:0,se=pt?32:0,Nt=pt?36:0,Ue=_t!=null?Math.max(oe,_t):oe,Ke=W-2*r-Ue-Nt-ne,xe=Y-2*r-se-ue-Qt;g.width=Math.round(W*y),g.height=Math.round(Y*y);const t=g.getContext("2d");t.scale(y,y),t.fillStyle=mt,t.fillRect(0,0,W,Y);const $=e.hoverPoints;if(!$.length)return{hitTarget:{getDieAtPoint:()=>null},viewport:{originX:0,originY:0,ppm:1,snapDist:1},binLegendRows:[]};const Et=e.rectangles[0],le=Et?Et.width/2:0,ie=Et?Et.height/2:0,Ht=e.dieBounds,ye=(Ht?.minX??0)-le,qe=(Ht?.maxX??0)+le,ze=(Ht?.minY??0)-ie,be=(Ht?.maxY??0)+ie,Me=qe-ye,Te=be-ze;let tt,et,A;if(K)({originX:tt,originY:et,ppm:A}=K);else{const f=e.notchDir,s=f&&f.y>.1?13:0,a=f&&f.y<-.1?13:0,c=f&&f.x<-.1?13:0,h=f&&f.x>.1?13:0,d=Ke-c-h,i=xe-s-a;A=Math.min(d/Me,i/Te),tt=r+Nt+ne+c+(d-Me*A)/2-ye*A,et=r+Qt+s+(i-Te*A)/2+be*A}const Yt=K?.snapDist??Math.max(le,ie,1)*1.5,Gt=tt+e.waferCenter.x*A,Je=et-e.waferCenter.y*A,ae=e.waferRadius*A,st=o=>{const f=Math.abs(o-Je);return f>=ae?0:Math.sqrt(ae*ae-f*f)},lt=(o,f,s,a,c,h,d=uo(),i=M.text)=>{if(!o)return;t.save(),t.fillStyle=i,t.font=d,t.textAlign=a,t.textBaseline=c;const b=a==="right"?f-h:h-f;let l=o;if(b>8&&t.measureText(l).width>b){for(;l.length>1&&t.measureText(l+"\u2026").width>b;)l=l.slice(0,-1);l=l+"\u2026"}b>8&&t.fillText(l,f,s),t.restore()};t.save(),t.setTransform(A*y,0,0,-A*y,tt*y,et*y);const dt=1/A,Ze=1/(A*y);if(e.rectangles.length>0){const o=new Map;for(const s of e.rectangles){const a=String(s.fill);let c=o.get(a);c||(c=[],o.set(a,c)),c.push(s)}for(const[s,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=s,t.fill()}t.strokeStyle="rgba(0,0,0,0.18)",t.lineWidth=.5*Ze,t.beginPath();for(const s of e.rectangles)t.rect(s.x-s.width/2,s.y-s.height/2,s.width,s.height);if(t.stroke(),X){const s=e.rectangles.filter(a=>a.binFail);if(s.length>0){const a=new Map;for(const c of s){const h=Ie(String(c.fill));let d=a.get(h);d||(d=[],a.set(h,d)),d.push(c)}t.save(),t.lineCap="butt";for(const[c,h]of a){t.strokeStyle=c,t.globalAlpha=.55,t.lineWidth=Math.max(.6*dt,Math.min(h[0].width,h[0].height)*.1);for(const d of h){const i=d.width/2,b=d.height/2,l=Math.max(1.6*dt,Math.min(d.width,d.height)/3);t.save(),t.beginPath(),t.rect(d.x-i,d.y-b,d.width,d.height),t.clip(),t.beginPath();for(let T=-(i+b);T<=i+b;T+=l)t.moveTo(d.x-i+T,d.y-b),t.lineTo(d.x-i+T+d.height,d.y+b);t.stroke(),t.restore()}}t.restore()}}const f=e.rectangles.filter(s=>s.specMark);if(f.length>0){const s=new Map;for(const c of f){const h=Ie(String(c.fill)),d=`${c.specMark}|${h}`;let i=s.get(d);i||(i=[],s.set(d,i)),i.push(c)}const a=2.5*dt;t.lineJoin="round";for(const[c,h]of s){const[d,i]=c.split("|"),b=i==="#000000"?"#ffffff":"#000000",l=d==="failHigh"?1:-1,T=Math.min(h[0].width,h[0].height),O=Math.max(2.5*dt,T*.3);t.beginPath();for(const R of h)Ne(t,R.x,R.y,O,l);t.strokeStyle=b,t.lineWidth=a,t.stroke(),t.fillStyle=i,t.fill()}}}for(const o of e.overlays){t.beginPath();for(const f of o.points){for(let s=0;s<f.length;s++)s===0?t.moveTo(f[s].x,f[s].y):t.lineTo(f[s].x,f[s].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*dt,t.stroke(),t.strokeStyle="rgba(255,255,255,0.8)",t.lineWidth=1*dt,t.stroke()):(t.strokeStyle=o.lineColor,t.lineWidth=o.lineWidth*dt,t.stroke())}if(t.restore(),e.notchDir){const o=tt+e.waferCenter.x*A,f=et-e.waferCenter.y*A,s=e.notchDir.x,a=-e.notchDir.y,c=e.waferRadius*A,h=4,d=9,i=5,b=o+s*(c+h),l=f+a*(c+h),T=b+s*d,O=l+a*d,R={x:-a,y:s},I=r+Nt+ne,E=W-r-oe,V=r+Qt,at=Y-r-ue-se;t.save(),t.beginPath(),t.rect(I,V,E-I,at-V),t.clip(),t.beginPath(),t.moveTo(b,l),t.lineTo(T+R.x*i,O+R.y*i),t.lineTo(T-R.x*i,O-R.y*i),t.closePath(),t.fillStyle=M.text,t.fill(),t.restore()}t.save(),t.textAlign="center",t.textBaseline="middle";let Se=0;if(e.rectangles.length>0){let o=1;for(const h of e.texts)h.role!=="indicator"&&h.text.length>o&&(o=h.text.length);const f=e.rectangles[0].width*A,s=e.rectangles[0].height*A,a=Math.min(f,s),c=f/o;Se=Math.max(8,Math.min(64,Math.round(Math.min(a*.55,c*1.8))))}for(const o of e.texts){const f=tt+o.x*A,s=et-o.y*A;t.font=`${o.role==="indicator"?o.fontSize:Se}px system-ui, sans-serif`,t.fillStyle=o.color,t.fillText(o.text,f,s)}if(t.restore(),pt&&yo(t,W,Y,tt,et,A,r,se,Nt,w,e.gridToScreen,e.rotation,M),St){const o=so(e.valueColorScheme,e.reverseValueScheme),f=pe,s=xe,a=Math.round(s*.75),c=r+Math.round((s-a)/2),h=W-r-B-f,d=Ot,i=It,b=i-d,l=e.plotMode==="stackedSoftBins"?xt:nt,{primary:T,secondary:O}=U?Be(e,Tt,l):{primary:"",secondary:""};if(U){const n=Math.max(c-6,r+11),m=Gt+Math.max(st(n),st(n-12))+8;lt(T,W-r,n,"right","bottom",m)}let R=c+a+4;const I=n=>Gt+Math.max(st(n),st(n+12))+8,E=e.logScale&&d>0?Math.log10(d):0,V=e.logScale&&i>0?Math.log10(i)-E:1,at=n=>e.logScale?(1-(Math.log10(n)-E)/V)*a:(1-(n-d)/b)*a;for(let n=0;n<zt;n++){const m=1-n/(zt-1),S=c+n/zt*a,p=a/zt+1;t.fillStyle=o(m),t.fillRect(h,S,B,p)}t.strokeStyle="rgba(0,0,0,0.25)",t.lineWidth=.5,t.strokeRect(h,c,B,a),t.fillStyle=M.text,t.font=J(),t.textAlign="left",t.strokeStyle="rgba(0,0,0,0.35)",t.lineWidth=.5;const L=Ee,F=36,ot=14,wt=e.logScale&&V>0?bo(d,i,a,F,ot,E,V):e.allIntegerValues?(()=>{const n=Math.max(1,Zt(b*F/a)),m=[],S=Math.ceil(d/n)*n;for(let p=S;p<=i+n*1e-6;p+=n){const u=(1-(p-d)/b)*a;u>ot&&u<a-ot&&m.push(Math.round(p))}return m})():(()=>{const n=b>0?Zt(b*F/a):0,m=[];if(n>0){const S=Math.ceil(d/n)*n;for(let p=S;p<=i+n*1e-6;p+=n){const u=(1-(p-d)/b)*a;u>ot&&u<a-ot&&m.push(p)}}return m})(),C=te,_=vt,x=Ft;t.textBaseline="middle";for(const n of wt){const m=c+at(n);t.beginPath(),t.moveTo(h+B,m),t.lineTo(h+B+L,m),t.stroke(),t.fillText(x(n),h+B+L+Kt,m)}if(t.beginPath(),t.moveTo(h+B,c),t.lineTo(h+B+L,c),t.stroke(),t.textBaseline="top",t.fillText(x(i),h+B+L+Kt,c),t.beginPath(),t.moveTo(h+B,c+a),t.lineTo(h+B+L,c+a),t.stroke(),t.textBaseline="bottom",t.fillText(x(d),h+B+L+Kt,c+a),!_&&C&&(C.limitLow!==void 0||C.limitHigh!==void 0)){const n=[],m=e.colorbarRangeMode==="spec";for(const[S,p]of[[C.limitLow,"LSL"],[C.limitHigh,"USL"]]){if(S===void 0||e.logScale&&S<=0)continue;const u=at(S);!m&&(u<-ot||u>a+ot)||n.push({value:S,label:p,py:Math.max(0,Math.min(a,u))})}if(n.length>0){t.save(),t.font=J(),t.textAlign="right",t.textBaseline="middle";for(const{py:S,label:p}of n){const u=c+S;m||(t.strokeStyle="rgba(255,255,255,0.85)",t.lineWidth=3,t.beginPath(),t.moveTo(h,u),t.lineTo(h+B,u),t.stroke(),t.strokeStyle="rgba(0,0,0,0.7)",t.lineWidth=1,t.beginPath(),t.moveTo(h,u),t.lineTo(h+B,u),t.stroke()),t.fillStyle=M.text,t.fillText(p,h-3,u);const ct=p==="USL"?-1:1,rt=4,Mt=h-3-t.measureText(p).width-rt-4;t.beginPath(),Ne(t,Mt,u,rt,ct),t.strokeStyle="#fff",t.lineWidth=2,t.lineJoin="round",t.stroke(),t.fillStyle=M.text,t.fill()}t.restore()}}if(!_){const n=e.logScale?"log\u2081\u2080":e.logScaleRequested?"linear \u2014 log n/a":null;n&&(t.save(),t.fillStyle=M.text,t.font=po(),t.textAlign="right",t.textBaseline="top",t.fillText(n,W-r,R),t.restore(),R+=15)}U&&O&&lt(O,W-r,R,"right","top",I(R),qt(),M.text)}const ce=[];let z;if(Q){const o=k==="compact",f=e.plotMode==="softBin"?xt:nt,s=f?new Map(f.map(l=>[l.bin,l])):null,a=e.plotMode==="softBin"?e.binColors.soft:e.binColors.hard,c=l=>a.get(l)??lo,h=(l,T)=>{const O=t.measureText(l).width,R=o?0:t.measureText(String(T)).width,I=H+gt+O+(R?Jt:0);return{labelWidth:O,countWidth:R,totalWidth:I}},d=yt?.find(l=>l.key===e.activeMetadataKey),i=j?v.map(l=>{const T=o?l.compactLabel:l.label;return{key:l.key,color:l.color,count:l.count,label:T,tooltipLabel:l.tooltip,...h(T,l.count)}}):e.plotMode==="metadata"?Pt.map(([l,T])=>{const R=d?.values?.find(V=>V.value===l)?.label??l,I=o?l:R,E=e.metadataColorMap?.get(l)??"#d6d9dd";return{key:l,color:E,count:T,label:I,tooltipLabel:`${R} \xB7 ${T} dies`,...h(I,T)}}):Bt.map(([l,T])=>{const O=s?.get(l),R=O?.name?`${l} \xB7 ${O.name}`:`Bin ${l}`,I=o?String(l):R;return{key:l,color:c(l),count:T,label:I,tooltipLabel:`${R} \xB7 ${T} dies`,...h(I,T)}}),b=W-r*2;if(i.length>0){const l=At?ft*2+(U?Ye:0):U&&!kt?Ge+Xe:0,T=Math.max(1,Math.floor((Y-2*r-l)/D));let O=i.length,R=1,I=O*D,E=[],V=0;const at=jt||kt;if(at){const C=Ut(i.map(x=>x.totalWidth)),_=Math.max(1,Math.min(i.length,Math.floor((b+8)/(C+8))));for(let x=_;x>=1;x--){const n=Math.ceil(i.length/x);if(n>T)continue;const m=new Array(x).fill(0);for(let p=0;p<i.length;p++){const u=p%x;m[u]=Math.max(m[u],i[p].totalWidth)}if(m.reduce((p,u)=>p+u,0)+(x-1)*8<=b||x===1){R=x,E=m,O=n,I=n*D;break}}}else O=Math.min(i.length,T),I=O*D,i.length>T&&(V=i.length-(T-1)),E=[Wt];let L,F;if(At){const C=Ut(i.map(u=>H+gt+t.measureText(u.label).width+Jt)),_=Math.ceil(C);E=[_];const x=U?Ye:0,n=_+ft*2,m=I+x+ft*2,S=ut?.x??0,p=ut?.y??0;L=Math.min(Math.max(W-r-n+S,r),W-r-n),F=Math.min(Math.max(Y-r-m+p,r),Y-r-m),z={x:L,y:F,w:n,h:m},t.save(),t.globalAlpha=.92,t.fillStyle=M.surface,t.fillRect(L,F,n,m),t.globalAlpha=1,t.strokeStyle=M.axisLine,t.lineWidth=1,t.strokeRect(L,F,n,m),t.restore(),L+=ft,F+=ft+x}else jt?(L=r,F=Y-r-I):kt?(L=r,F=r):de?(L=r+4,F=Math.max(r+Math.round((Y-2*r-I)/2),r+l)):(L=W-r-Wt+4,F=Math.max(r+Math.round((Y-2*r-I)/2),r+l));if(U){const C=e.plotMode==="softBin"?xt:nt,{primary:_,secondary:x}=Be(e,Tt,C),n=Xe,m=u=>Gt-Math.max(st(u),st(u-12))-8,S=u=>ht?W-r:m(u),p=At?z.y+z.h:F+I;if(kt){const u=p+n;lt(_,L,u,"left","top",Gt-Math.max(st(u),st(u+12))-8),x&&lt(x,L,u+16,"left","top",m(u+16),qt(),M.text)}else if(At)lt(_,z.x+ft,z.y+ft,"left","top",z.x+z.w-ft),x&&lt(x,z.x,p+n,"left","top",z.x+z.w,qt(),M.text);else{const u=Math.max(F-n,r+Ge);if(lt(_,L,u,"left","bottom",S(u)),x){const ct=p+n;lt(x,L,ct,"left","top",S(ct),qt(),M.text)}}}t.save(),t.font=J();const ot=at?8:0,wt=(C,_)=>{if(t.measureText(C).width<=_)return C;let x=0,n=C.length;const m="\u2026",S=t.measureText(m).width;for(;x<n;){const p=x+n+1>>1;t.measureText(C.slice(0,p)).width+S<=_?x=p:n=p-1}return x===0?m:C.slice(0,x)+m};if(at){let C=0;for(let _=0;_<O;_++){let x=L;for(let n=0;n<R;n++){const m=i[C++];if(!m)break;const S=m.key===N,p=!S&&m.key===q,u=x,ct=x+H+gt,rt=F+_*D+D/2,Mt=F+_*D+Math.round((D-H)/2),Vt=E[n]-H-gt-Jt;p&&(t.fillStyle=M.hoverRow,t.fillRect(x,F+_*D,E[n],D)),t.fillStyle=m.color,t.fillRect(u,Mt,H,H),t.strokeStyle=S?M.accent:"rgba(0,0,0,0.25)",t.lineWidth=S?2:.75,t.strokeRect(u,Mt,H,H),t.fillStyle=S?M.accent:M.text,t.font=S?`bold ${J()}`:J(),t.textAlign="left",t.textBaseline="middle",t.fillText(wt(m.label,Vt),ct,rt),t.fillStyle=M.textMuted,t.font=J(),t.textAlign="right",t.fillText(String(m.count),x+E[n]-2,rt),ce.push({bin:m.key,x,y:F+_*D,w:E[n],h:D,label:m.tooltipLabel}),x+=E[n]+ot}}}else{const C=E[0],_=k!=="compact",x=C-H-gt-(_?Jt:0),n=L+C-2;let m=i;V>0&&(m=i.slice(0,T-1));let S=F;for(const p of m){const u=p.key===N,ct=!u&&p.key===q,rt=L,Mt=L+H+gt,Vt=S+D/2,Ce=S+Math.round((D-H)/2),to=p.label;ct&&(t.fillStyle=M.hoverRow,t.fillRect(L-3,S,C+3,D)),t.fillStyle=p.color,t.fillRect(rt,Ce,H,H),t.strokeStyle=u?M.accent:"rgba(0,0,0,0.25)",t.lineWidth=u?2:.75,t.strokeRect(rt,Ce,H,H),t.fillStyle=u?M.accent:M.text,t.font=u?`bold ${J()}`:J(),t.textAlign="left",t.textBaseline="middle",t.fillText(wt(to,x),Mt,Vt),(_||At)&&(t.fillStyle=M.textMuted,t.font=J(),t.textAlign="right",t.fillText(String(p.count),n,Vt)),ce.push({bin:p.key,x:L,y:S,w:C,h:D,label:p.tooltipLabel}),S+=D}V>0&&(t.fillStyle=M.textMuted,t.font=J(),t.textAlign="left",t.textBaseline="middle",t.fillText(`+ ${V} more`,L+H+gt,S+D/2))}}i.length>0&&t.restore()}const je={originX:tt,originY:et,ppm:A,snapDist:Yt},Le=e.rectangles[0]?.width??1,ke=e.rectangles[0]?.height??1,it=e.dieBounds,Xt=it?it.minX:$.length?De($.map(o=>o.x)):0,$t=it?it.minY:$.length?De($.map(o=>o.y)):0,Qe=it?it.maxX:$.length?Ut($.map(o=>o.x)):1,ve=it?it.maxY:$.length?Ut($.map(o=>o.y)):1,{cellW:re,cellH:fe,nCols:Rt,nRows:he}=ro(Qe-Xt,ve-$t,Le*1.5,ke*1.5,$.length),Ae=Array.from({length:Rt*he},()=>[]);for(let o=0;o<$.length;o++){const f=Math.floor(($[o].x-Xt)/re),s=Math.floor(($[o].y-$t)/fe),a=Number.isFinite(f)?Math.max(0,Math.min(Rt-1,f)):0,c=Number.isFinite(s)?Math.max(0,Math.min(he-1,s)):0;Ae[c*Rt+a].push(o)}function Re(o,f,s){const a=Math.max(0,Math.floor((o-s-Xt)/re)),c=Math.min(Rt-1,Math.floor((o+s-Xt)/re)),h=Math.max(0,Math.floor((f-s-$t)/fe)),d=Math.min(he-1,Math.floor((f+s-$t)/fe)),i=[];for(let b=h;b<=d;b++)for(let l=a;l<=c;l++)for(const T of Ae[b*Rt+l])i.push(T);return i}return{hitTarget:{getDieAtPoint(o,f){const s=(o-tt)/A,a=(et-f)/A,c=Math.max(Le,ke);for(const i of Re(s,a,c)){const b=e.rectangles[i];if(b&&Math.abs(s-b.x)<=b.width/2&&Math.abs(a-b.y)<=b.height/2)return e.dies[i]??null}let h=null,d=Yt*Yt;for(const i of Re(s,a,Yt)){const b=$[i].x-s,l=$[i].y-a,T=b*b+l*l;T<d&&(d=T,h=e.dies[i]??null)}return h}},viewport:je,binLegendRows:ce,legendBox:z}}function yo(g,e,P,M,r,G,X,B,mt,Z,ut,pt,w){g.save(),g.font=xo(),g.fillStyle=w.text,g.strokeStyle=w.axisLine,g.lineWidth=.5;const K=P-B+4,N=X+mt-4,q=oo(ut),Tt=Pe(q,1,0),_t=Pe(q,0,1),nt=1e-9;function xt(y){return Z?Math.abs(y.y)<nt&&Math.abs(y.x)>nt?{pitch:Z.x,sign:Math.sign(y.x)}:Math.abs(y.x)<nt&&Math.abs(y.y)>nt?{pitch:Z.y,sign:Math.sign(y.y)}:null:null}const yt=xt(Tt),U=xt(_t);function W(y){return yt?Math.round(y*yt.sign/yt.pitch):y}function Y(y){return U?Math.round(y*U.sign/U.pitch):y}function bt(y){if(!y)return Zt(50/G);const k=50/(y*G);return Math.max(1,Math.round(k))*y}const St=yt?.pitch,Lt=U?.pitch,j=bt(St),Q=bt(Lt);g.textAlign="center",g.textBaseline="top";const Bt=Math.ceil((X-M)/G/j)*j,Pt=(e-X-M)/G;for(let y=Bt;y<=Pt;y+=j){const k=M+y*G;if(k<X||k>e-X)continue;g.beginPath(),g.moveTo(k,K-He),g.lineTo(k,K),g.stroke();const ht=Z?String(W(y)):Fe(y);g.fillText(ht,k,K+2)}g.textAlign="right",g.textBaseline="middle";const v=Math.ceil((r-(P-X))/G/Q)*Q,Dt=(r-X)/G;for(let y=v;y<=Dt;y+=Q){const k=r-y*G;if(k<X||k>P-X)continue;g.beginPath(),g.moveTo(N,k),g.lineTo(N+He,k),g.stroke();const ht=Z?String(Y(y)):Fe(y);g.fillText(ht,N-2,k)}g.restore()}function Zt(g){const e=Math.pow(10,Math.floor(Math.log10(g))),P=g/e;return(P<1.5?1:P<3.5?2:P<7.5?5:10)*e}function bo(g,e,P,M,r,G,X){const B=[],mt=X;if(mt<1){const w=Zt((e-g)*M/P);if(w>0){const K=Math.ceil(g/w)*w;for(let N=K;N<=e+w*1e-6;N+=w){if(N<=g||N>=e)continue;const q=(1-(Math.log10(N)-G)/X)*P;q>r&&q<P-r&&B.push(N)}}return B}const Z=mt<3?[1,2,5]:[1],ut=Math.floor(Math.log10(g)),pt=Math.ceil(Math.log10(e));for(let w=ut;w<=pt;w++)for(const K of Z){const N=K*Math.pow(10,w);if(N<=g||N>=e)continue;const q=(1-(Math.log10(N)-G)/X)*P;q>r&&q<P-r&&B.push(N)}return B}export{fmt,fmtColorbarAxis}from"../renderer/fmt.js";
1
+ import{fontPx as Wt}from"./toolbar.js";import{findTestDef as We,buildMapTitle as Fe}from"../renderer/buildView.js";import{niceStep as Ft}from"../renderer/axisTicks.js";import{sortBinsForDisplay as lo}from"../stats/binPareto.js";import{affineInvert as io,affineVector as Oe}from"../core/transforms.js";import{compareNatural as ao,maxOf as zt,minOf as Ne}from"../core/utils.js";import{resolveValueColorFn as co}from"../renderer/colorSchemes.js";import{NO_DATA_FILL as ro}from"../renderer/colorMap.js";import{SPEC_PASS_FILL as Ee,SPEC_FAIL_LOW as fo,SPEC_FAIL_HIGH as He,contrastTextColor as Ye}from"../renderer/colorMap.js";import{fmt as Xe,fmtColorbarAxis as Ge}from"../renderer/fmt.js";import{resolveCanvasTheme as ho}from"./canvasTheme.js";import{hitGridDims as go}from"./hitGrid.js";function $e(g,e,X,x,c){g.moveTo(e,X+c*x),g.lineTo(e-x*.9,X-c*x*.6),g.lineTo(e+x*.9,X-c*x*.6),g.closePath()}const mo=new Set(["value","stackedValues","stackedBins","stackedSoftBins"]),uo=new Set(["hardBin","softBin","metadata"]),v=()=>`${Wt(-2)}px system-ui, sans-serif`,we=3,Jt=2,po=6,xo=20,bo=()=>`600 ${Wt()}px system-ui, sans-serif`,Ve=36,ue=()=>`${Wt(-1)}px system-ui, sans-serif`,Mo=()=>`600 ${Wt(-1)}px system-ui, sans-serif`,Zt=128,yo=()=>`${Wt(-2)}px system-ui, sans-serif`,Ke=4,E=17,$=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 jt=28,ut=5,ht=8,Ue=15,qe=12,ze=6;export function toCanvas(g,e,X={}){return drawMapCanvas(g,e,X)}export function drawMapCanvas(g,e,X={}){const x=ho(g.parentElement??g),{padding:c=16,showColorbar:V=!0,markFailingDies:K=!1,colorbarWidth:D=16,background:pt=x.background,legendPosition:tt="default",legendOffset:xt,showAxes:bt=!1,diePitchMm:q,viewport:Z,activeBin:Y,hoverBin:j,fallbackFormat:Mt,minRightReserve:Ot,hbinDefs:ct,sbinDefs:yt,metadataFields:Tt,showTitle:z=!0}=X,F=Math.floor(g.clientWidth||g.width),G=Math.floor(g.clientHeight||g.height),St=F>=BIN_LEGEND_MIN_CANVAS_W&&G>=BIN_LEGEND_MIN_CANVAS_H,At=V&&St&&mo.has(e.plotMode)&&e.passFailDisplay==="off",Rt=V&&St&&uo.has(e.plotMode),ot=V&&St&&e.plotMode==="value"&&(e.passFailDisplay==="spec"&&e.specCounts!==void 0||e.passFailDisplay==="test"&&e.passFailCounts!==void 0),nt=Rt||ot,Nt=Rt&&e.plotMode!=="metadata"&&e.binCounts?lo(e.binCounts.entries(),e.plotMode==="softBin"?e.binColors.pass.soft:e.binColors.pass.hard):[],Et=Rt&&e.plotMode==="metadata"&&e.metadataCounts?[...e.metadataColorMap?.keys()??[...e.metadataCounts.keys()].sort(ao)].map(o=>[o,e.metadataCounts.get(o)??0]):[],st=[];if(ot&&e.passFailDisplay==="test"){const o=e.passFailCounts;st.push({key:-1,color:Ee,label:"Pass",compactLabel:"Pass",count:o.pass,tooltip:`Pass \xB7 ${o.pass} dies`}),st.push({key:-2,color:He,label:"Fail",compactLabel:"Fail",count:o.fail,tooltip:`Fail \xB7 ${o.fail} dies`})}else if(ot){const o=e.specCounts,f=We(e.testDefs,e.activeTest);st.push({key:-1,color:Ee,label:"Pass",compactLabel:"Pass",count:o.pass,tooltip:`Pass \xB7 ${o.pass} dies`}),f?.limitHigh!==void 0&&st.push({key:-2,color:He,label:"Fail high",compactLabel:"Fail hi",count:o.failHigh,tooltip:`Fail high \xB7 ${o.failHigh} dies`}),f?.limitLow!==void 0&&st.push({key:-3,color:fo,label:"Fail low",compactLabel:"Fail lo",count:o.failLow,tooltip:`Fail low \xB7 ${o.failLow} dies`})}const Ht=ot?st.length:e.plotMode==="metadata"?Et.length:Nt.length,u=(g.ownerDocument.defaultView??window).devicePixelRatio??1;if(F<=0||G<=0)return{hitTarget:{getDieAtPoint:()=>null},viewport:{originX:0,originY:0,ppm:1,snapDist:1},binLegendRows:[]};const k=tt!=="default"?tt:F<BIN_LEGEND_ADAPT_FLOATING?"floating":F<BIN_LEGEND_ADAPT_COMPACT?"compact":"default",dt=k==="default"||k==="compact",pe=k==="left",Qt=k==="bottom",_t=k==="top",Ct=k==="floating",Yt=k==="compact"||k==="floating"?BIN_LEGEND_W_COMPACT:BIN_LEGEND_W,xe=Math.floor((G-2*c)/E),be=Ht>xe?xe-1:Ht,Me=nt&&Qt?be*E:0,vt=nt&&_t?be*E:0,te=e.plotMode==="stackedBins"||e.plotMode==="stackedSoftBins",ee=We(e.testDefs,e.activeTest),ye=te?"Count":ee?.name??(e.activeTest!=null?`Test ${e.activeTest}`:void 0),Te=te?void 0:ee?.unit,[Bt,Pt]=e.valueRange,oe=o=>String(Math.round(o)),Je=e.logScale||e.allIntegerValues?void 0:Ft(Math.max(0,Pt-Bt)*Ve/Math.max(1,G))||void 0,ne=e.allIntegerValues?oe:Ge(Pt,ye,Te,Mt,Je).edgeFmt;let se=0;if(At){const o=g.getContext("2d");o.font=v();const f=[ne(Bt),ne(Pt)];Bt<0&&f.push(ne(-Math.max(Math.abs(Bt),Math.abs(Pt))));for(const n of f)se=Math.max(se,o.measureText(n).width)}const Se=Math.max(xo,Math.ceil(se)+we+Jt+po-c),le=At?D+Se+8:nt&&dt?Yt:0,ie=nt&&pe?Yt:0,ae=bt?32:0,Xt=bt?36:0,Ze=Ot!=null?Math.max(le,Ot):le,je=F-2*c-Ze-Xt-ie,Le=G-2*c-ae-Me-vt;g.width=Math.round(F*u),g.height=Math.round(G*u);const t=g.getContext("2d");t.scale(u,u),t.fillStyle=pt,t.fillRect(0,0,F,G);const U=e.hoverPoints;if(!U.length)return{hitTarget:{getDieAtPoint:()=>null},viewport:{originX:0,originY:0,ppm:1,snapDist:1},binLegendRows:[]};const Gt=e.rectangles[0],ce=Gt?Gt.width/2:0,re=Gt?Gt.height/2:0,$t=e.dieBounds,ke=($t?.minX??0)-ce,Qe=($t?.maxX??0)+ce,ve=($t?.minY??0)-re,Ae=($t?.maxY??0)+re,Re=Qe-ke,_e=Ae-ve;let lt,it,A;if(Z)({originX:lt,originY:it,ppm:A}=Z);else{const f=e.notchDir,n=f&&f.y>.1?13:0,a=f&&f.y<-.1?13:0,r=f&&f.x<-.1?13:0,h=f&&f.x>.1?13:0,d=je-r-h,s=Le-n-a;A=Math.min(d/Re,s/_e),lt=c+Xt+ie+r+(d-Re*A)/2-ke*A,it=c+vt+n+(s-_e*A)/2+Ae*A}const wt=Z?.snapDist??Math.max(ce,re,1)*1.5,Vt=lt+e.waferCenter.x*A,to=it-e.waferCenter.y*A,fe=e.waferRadius*A,rt=o=>{const f=Math.abs(o-to);return f>=fe?0:Math.sqrt(fe*fe-f*f)},gt=(o,f,n,a,r,h,d=bo(),s=x.text)=>{if(!o)return;t.save(),t.fillStyle=s,t.font=d,t.textAlign=a,t.textBaseline=r;const p=a==="right"?f-h:h-f;let l=o;if(p>8&&t.measureText(l).width>p){for(;l.length>1&&t.measureText(l+"\u2026").width>p;)l=l.slice(0,-1);l=l+"\u2026"}p>8&&t.fillText(l,f,n),t.restore()};t.save(),t.setTransform(A*u,0,0,-A*u,lt*u,it*u);const mt=1/A,eo=1/(A*u);if(e.rectangles.length>0){const o=new Map;for(const n of e.rectangles){const a=String(n.fill);let r=o.get(a);r||(r=[],o.set(a,r)),r.push(n)}for(const[n,a]of o){t.beginPath();for(const r of a)t.rect(r.x-r.width/2,r.y-r.height/2,r.width,r.height);t.fillStyle=n,t.fill()}t.strokeStyle="rgba(0,0,0,0.18)",t.lineWidth=.5*eo,t.beginPath();for(const n of e.rectangles)t.rect(n.x-n.width/2,n.y-n.height/2,n.width,n.height);if(t.stroke(),K){const n=e.rectangles.filter(a=>a.binFail);if(n.length>0){const a=new Map;for(const r of n){const h=Ye(String(r.fill));let d=a.get(h);d||(d=[],a.set(h,d)),d.push(r)}t.save(),t.lineCap="butt";for(const[r,h]of a){t.strokeStyle=r,t.globalAlpha=.55,t.lineWidth=Math.max(.6*mt,Math.min(h[0].width,h[0].height)*.1);for(const d of h){const s=d.width/2,p=d.height/2,l=Math.max(1.6*mt,Math.min(d.width,d.height)/3);t.save(),t.beginPath(),t.rect(d.x-s,d.y-p,d.width,d.height),t.clip(),t.beginPath();for(let M=-(s+p);M<=s+p;M+=l)t.moveTo(d.x-s+M,d.y-p),t.lineTo(d.x-s+M+d.height,d.y+p);t.stroke(),t.restore()}}t.restore()}}const f=e.rectangles.filter(n=>n.specMark);if(f.length>0){const n=new Map;for(const r of f){const h=Ye(String(r.fill)),d=`${r.specMark}|${h}`;let s=n.get(d);s||(s=[],n.set(d,s)),s.push(r)}const a=2.5*mt;t.lineJoin="round";for(const[r,h]of n){const[d,s]=r.split("|"),p=s==="#000000"?"#ffffff":"#000000",l=d==="failHigh"?1:-1,M=Math.min(h[0].width,h[0].height),H=Math.max(2.5*mt,M*.3);t.beginPath();for(const I of h)$e(t,I.x,I.y,H,l);t.strokeStyle=p,t.lineWidth=a,t.stroke(),t.fillStyle=s,t.fill()}}}for(const o of e.overlays){t.beginPath();for(const f of o.points){for(let n=0;n<f.length;n++)n===0?t.moveTo(f[n].x,f[n].y):t.lineTo(f[n].x,f[n].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*mt,t.stroke(),t.strokeStyle="rgba(255,255,255,0.8)",t.lineWidth=1*mt,t.stroke()):(t.strokeStyle=o.lineColor,t.lineWidth=o.lineWidth*mt,t.stroke())}if(t.restore(),e.notchDir){const o=lt+e.waferCenter.x*A,f=it-e.waferCenter.y*A,n=e.notchDir.x,a=-e.notchDir.y,r=e.waferRadius*A,h=4,d=9,s=5,p=o+n*(r+h),l=f+a*(r+h),M=p+n*d,H=l+a*d,I={x:-a,y:n},L=c+Xt+ie,w=F-c-le,J=c+vt,at=G-c-Me-ae;t.save(),t.beginPath(),t.rect(L,J,w-L,at-J),t.clip(),t.beginPath(),t.moveTo(p,l),t.lineTo(M+I.x*s,H+I.y*s),t.lineTo(M-I.x*s,H-I.y*s),t.closePath(),t.fillStyle=x.text,t.fill(),t.restore()}t.save(),t.textAlign="center",t.textBaseline="middle";let Ce=0;if(e.rectangles.length>0){let o=1;for(const h of e.texts)h.role!=="indicator"&&h.text.length>o&&(o=h.text.length);const f=e.rectangles[0].width*A,n=e.rectangles[0].height*A,a=Math.min(f,n),r=f/o;Ce=Math.max(8,Math.min(64,Math.round(Math.min(a*.55,r*1.8))))}for(const o of e.texts){const f=lt+o.x*A,n=it-o.y*A;t.font=`${o.role==="indicator"?o.fontSize:Ce}px system-ui, sans-serif`,t.fillStyle=o.color,t.fillText(o.text,f,n)}if(t.restore(),bt&&To(t,F,G,lt,it,A,c,ae,Xt,q,e.gridToScreen,e.rotation,x),At){const o=co(e.valueColorScheme,e.reverseValueScheme),f=Se,n=Le,a=Math.round(n*.75),r=c+Math.round((n-a)/2),h=F-c-D-f,d=Bt,s=Pt,p=s-d,l=e.plotMode==="stackedSoftBins"?yt:ct,{primary:M,secondary:H,note:I}=z?Fe(e,Mt,l):{primary:"",secondary:"",note:void 0};if(z){const i=Math.max(r-6,c+11),P=Vt+Math.max(rt(i),rt(i-12))+8;gt(M,F-c,i,"right","bottom",P)}let L=r+a+4;const w=i=>Vt+Math.max(rt(i),rt(i+12))+8,J=e.logScale&&d>0?Math.log10(d):0,at=e.logScale&&s>0?Math.log10(s)-J:1,_=i=>e.logScale?(1-(Math.log10(i)-J)/at)*a:(1-(i-d)/p)*a;for(let i=0;i<Zt;i++){const P=1-i/(Zt-1),W=r+i/Zt*a,m=a/Zt+1;t.fillStyle=o(P),t.fillRect(h,W,D,m)}t.strokeStyle="rgba(0,0,0,0.25)",t.lineWidth=.5,t.strokeRect(h,r,D,a),t.fillStyle=x.text,t.font=v(),t.textAlign="left",t.strokeStyle="rgba(0,0,0,0.35)",t.lineWidth=.5;const C=we,It=Ve,et=14;let O;const B=e.logScale&&at>0?So(d,s,a,It,et,J,at):e.allIntegerValues?(()=>{const i=Math.max(1,Ft(p*It/a)),P=[],W=Math.ceil(d/i)*i;for(let m=W;m<=s+i*1e-6;m+=i){const R=(1-(m-d)/p)*a;R>et&&R<a-et&&P.push(Math.round(m))}return P})():(()=>{const i=p>0?Ft(p*It/a):0,P=[];if(i>0){O=i;const W=Math.ceil(d/i)*i;for(let m=W;m<=s+i*1e-6;m+=i){const R=(1-(m-d)/p)*a;R>et&&R<a-et&&P.push(m)}}return P})(),b=ee,S=te,y=Ge(s,ye,Te,Mt,O),N=e.allIntegerValues?oe:y.tickFmt,T=e.allIntegerValues?oe:y.edgeFmt;t.textBaseline="middle";for(const i of B){const P=r+_(i);t.beginPath(),t.moveTo(h+D,P),t.lineTo(h+D+C,P),t.stroke(),t.fillText(N(i),h+D+C+Jt,P)}if(t.beginPath(),t.moveTo(h+D,r),t.lineTo(h+D+C,r),t.stroke(),t.textBaseline="top",t.fillText(T(s),h+D+C+Jt,r),t.beginPath(),t.moveTo(h+D,r+a),t.lineTo(h+D+C,r+a),t.stroke(),t.textBaseline="bottom",t.fillText(T(d),h+D+C+Jt,r+a),!S&&b&&(b.limitLow!==void 0||b.limitHigh!==void 0)){const i=[],P=e.colorbarRangeMode==="spec";for(const[W,m]of[[b.limitLow,"Lo limit"],[b.limitHigh,"Hi limit"]]){if(W===void 0||e.logScale&&W<=0)continue;const R=_(W);!P&&(R<-et||R>a+et)||i.push({value:W,label:m,py:Math.max(0,Math.min(a,R))})}if(i.length>0){t.save(),t.font=v(),t.textAlign="right",t.textBaseline="middle";for(const{py:W,label:m}of i){const R=r+W;P||(t.strokeStyle="rgba(255,255,255,0.85)",t.lineWidth=3,t.beginPath(),t.moveTo(h,R),t.lineTo(h+D,R),t.stroke(),t.strokeStyle="rgba(0,0,0,0.7)",t.lineWidth=1,t.beginPath(),t.moveTo(h,R),t.lineTo(h+D,R),t.stroke()),t.fillStyle=x.text,t.fillText(m,h-3,R);const Lt=m==="Hi limit"?-1:1,kt=4,qt=h-3-t.measureText(m).width-kt-4;t.beginPath(),$e(t,qt,R,kt,Lt),t.strokeStyle="#fff",t.lineWidth=2,t.lineJoin="round",t.stroke(),t.fillStyle=x.text,t.fill()}t.restore()}}if(!S){const i=e.logScale?"log\u2081\u2080":e.logScaleRequested?"linear \u2014 log n/a":null;i&&(t.save(),t.fillStyle=x.text,t.font=Mo(),t.textAlign="right",t.textBaseline="top",t.fillText(i,F-c,L),t.restore(),L+=15)}z&&H&&(gt(H,F-c,L,"right","top",w(L),ue(),x.text),L+=15),z&&I&&gt(I,F-c,L,"right","top",w(L),ue(),x.text)}const he=[];let Q;if(nt){const o=k==="compact",f=e.plotMode==="softBin"?yt:ct,n=f?new Map(f.map(l=>[l.bin,l])):null,a=e.plotMode==="softBin"?e.binColors.soft:e.binColors.hard,r=l=>a.get(l)??ro,h=(l,M)=>{const H=t.measureText(l).width,I=o?0:t.measureText(String(M)).width,L=$+ut+H+(I?jt:0);return{labelWidth:H,countWidth:I,totalWidth:L}},d=Tt?.find(l=>l.key===e.activeMetadataKey),s=ot?st.map(l=>{const M=o?l.compactLabel:l.label;return{key:l.key,color:l.color,count:l.count,label:M,tooltipLabel:l.tooltip,...h(M,l.count)}}):e.plotMode==="metadata"?Et.map(([l,M])=>{const I=d?.values?.find(J=>J.value===l)?.label??l,L=o?l:I,w=e.metadataColorMap?.get(l)??"#d6d9dd";return{key:l,color:w,count:M,label:L,tooltipLabel:`${I} \xB7 ${M} dies`,...h(L,M)}}):Nt.map(([l,M])=>{const H=n?.get(l),I=H?.name?`${l} \xB7 ${H.name}`:`Bin ${l}`,L=o?String(l):I;return{key:l,color:r(l),count:M,label:L,tooltipLabel:`${I} \xB7 ${M} dies`,...h(L,M)}}),p=F-c*2;if(s.length>0){const l=Ct?ht*2+(z?Ue:0):z&&!_t?qe+ze:0,M=Math.max(1,Math.floor((G-2*c-l)/E));let H=s.length,I=1,L=H*E,w=[],J=0;const at=Qt||_t;if(at){const O=zt(s.map(b=>b.totalWidth)),B=Math.max(1,Math.min(s.length,Math.floor((p+8)/(O+8))));for(let b=B;b>=1;b--){const S=Math.ceil(s.length/b);if(S>M)continue;const y=new Array(b).fill(0);for(let T=0;T<s.length;T++){const i=T%b;y[i]=Math.max(y[i],s[T].totalWidth)}if(y.reduce((T,i)=>T+i,0)+(b-1)*8<=p||b===1){I=b,w=y,H=S,L=S*E;break}}}else H=Math.min(s.length,M),L=H*E,s.length>M&&(J=s.length-(M-1)),w=[Yt];let _,C;if(Ct){const O=zt(s.map(i=>$+ut+t.measureText(i.label).width+jt)),B=Math.ceil(O);w=[B];const b=z?Ue:0,S=B+ht*2,y=L+b+ht*2,N=xt?.x??0,T=xt?.y??0;_=Math.min(Math.max(F-c-S+N,c),F-c-S),C=Math.min(Math.max(G-c-y+T,c),G-c-y),Q={x:_,y:C,w:S,h:y},t.save(),t.globalAlpha=.92,t.fillStyle=x.surface,t.fillRect(_,C,S,y),t.globalAlpha=1,t.strokeStyle=x.axisLine,t.lineWidth=1,t.strokeRect(_,C,S,y),t.restore(),_+=ht,C+=ht+b}else Qt?(_=c,C=G-c-L):_t?(_=c,C=c):pe?(_=c+4,C=Math.max(c+Math.round((G-2*c-L)/2),c+l)):(_=F-c-Yt+4,C=Math.max(c+Math.round((G-2*c-L)/2),c+l));if(z){const O=e.plotMode==="softBin"?yt:ct,{primary:B,secondary:b,note:S}=Fe(e,Mt,O),y=ze,N=[b,S].filter(m=>!!m),T=(m,R,Lt)=>N.forEach((kt,qt)=>gt(kt,m,R+qt*16,"left","top",Lt(R+qt*16),ue(),x.text)),i=m=>Vt-Math.max(rt(m),rt(m-12))-8,P=m=>dt?F-c:i(m),W=Ct?Q.y+Q.h:C+L;if(_t){const m=W+y;gt(B,_,m,"left","top",Vt-Math.max(rt(m),rt(m+12))-8),T(_,m+16,i)}else if(Ct)gt(B,Q.x+ht,Q.y+ht,"left","top",Q.x+Q.w-ht),T(Q.x,W+y,()=>Q.x+Q.w);else{const m=Math.max(C-y,c+qe);gt(B,_,m,"left","bottom",P(m)),T(_,W+y,P)}}t.save(),t.font=v();const It=at?8:0,et=(O,B)=>{if(t.measureText(O).width<=B)return O;let b=0,S=O.length;const y="\u2026",N=t.measureText(y).width;for(;b<S;){const T=b+S+1>>1;t.measureText(O.slice(0,T)).width+N<=B?b=T:S=T-1}return b===0?y:O.slice(0,b)+y};if(at){let O=0;for(let B=0;B<H;B++){let b=_;for(let S=0;S<I;S++){const y=s[O++];if(!y)break;const N=y.key===Y,T=!N&&y.key===j,i=b,P=b+$+ut,W=C+B*E+E/2,m=C+B*E+Math.round((E-$)/2),R=w[S]-$-ut-jt;T&&(t.fillStyle=x.hoverRow,t.fillRect(b,C+B*E,w[S],E)),t.fillStyle=y.color,t.fillRect(i,m,$,$),t.strokeStyle=N?x.accent:"rgba(0,0,0,0.25)",t.lineWidth=N?2:.75,t.strokeRect(i,m,$,$),t.fillStyle=N?x.accent:x.text,t.font=N?`bold ${v()}`:v(),t.textAlign="left",t.textBaseline="middle",t.fillText(et(y.label,R),P,W),t.fillStyle=x.textMuted,t.font=v(),t.textAlign="right",t.fillText(String(y.count),b+w[S]-2,W),he.push({bin:y.key,x:b,y:C+B*E,w:w[S],h:E,label:y.tooltipLabel}),b+=w[S]+It}}}else{const O=w[0],B=k!=="compact",b=O-$-ut-(B?jt:0),S=_+O-2;let y=s;J>0&&(y=s.slice(0,M-1));let N=C;for(const T of y){const i=T.key===Y,P=!i&&T.key===j,W=_,m=_+$+ut,R=N+E/2,Lt=N+Math.round((E-$)/2),kt=T.label;P&&(t.fillStyle=x.hoverRow,t.fillRect(_-3,N,O+3,E)),t.fillStyle=T.color,t.fillRect(W,Lt,$,$),t.strokeStyle=i?x.accent:"rgba(0,0,0,0.25)",t.lineWidth=i?2:.75,t.strokeRect(W,Lt,$,$),t.fillStyle=i?x.accent:x.text,t.font=i?`bold ${v()}`:v(),t.textAlign="left",t.textBaseline="middle",t.fillText(et(kt,b),m,R),(B||Ct)&&(t.fillStyle=x.textMuted,t.font=v(),t.textAlign="right",t.fillText(String(T.count),S,R)),he.push({bin:T.key,x:_,y:N,w:O,h:E,label:T.tooltipLabel}),N+=E}J>0&&(t.fillStyle=x.textMuted,t.font=v(),t.textAlign="left",t.textBaseline="middle",t.fillText(`+ ${J} more`,_+$+ut,N+E/2))}}s.length>0&&t.restore()}const oo={originX:lt,originY:it,ppm:A,snapDist:wt},Be=e.rectangles[0]?.width??1,Pe=e.rectangles[0]?.height??1,ft=e.dieBounds,Kt=ft?ft.minX:U.length?Ne(U.map(o=>o.x)):0,Ut=ft?ft.minY:U.length?Ne(U.map(o=>o.y)):0,no=ft?ft.maxX:U.length?zt(U.map(o=>o.x)):1,so=ft?ft.maxY:U.length?zt(U.map(o=>o.y)):1,{cellW:de,cellH:ge,nCols:Dt,nRows:me}=go(no-Kt,so-Ut,Be*1.5,Pe*1.5,U.length),De=Array.from({length:Dt*me},()=>[]);for(let o=0;o<U.length;o++){const f=Math.floor((U[o].x-Kt)/de),n=Math.floor((U[o].y-Ut)/ge),a=Number.isFinite(f)?Math.max(0,Math.min(Dt-1,f)):0,r=Number.isFinite(n)?Math.max(0,Math.min(me-1,n)):0;De[r*Dt+a].push(o)}function Ie(o,f,n){const a=Math.max(0,Math.floor((o-n-Kt)/de)),r=Math.min(Dt-1,Math.floor((o+n-Kt)/de)),h=Math.max(0,Math.floor((f-n-Ut)/ge)),d=Math.min(me-1,Math.floor((f+n-Ut)/ge)),s=[];for(let p=h;p<=d;p++)for(let l=a;l<=r;l++)for(const M of De[p*Dt+l])s.push(M);return s}return{hitTarget:{getDieAtPoint(o,f){const n=(o-lt)/A,a=(it-f)/A,r=Math.max(Be,Pe);for(const s of Ie(n,a,r)){const p=e.rectangles[s];if(p&&Math.abs(n-p.x)<=p.width/2&&Math.abs(a-p.y)<=p.height/2)return e.dies[s]??null}let h=null,d=wt*wt;for(const s of Ie(n,a,wt)){const p=U[s].x-n,l=U[s].y-a,M=p*p+l*l;M<d&&(d=M,h=e.dies[s]??null)}return h}},viewport:oo,binLegendRows:he,legendBox:Q}}function To(g,e,X,x,c,V,K,D,pt,tt,xt,bt,q){g.save(),g.font=yo(),g.fillStyle=q.text,g.strokeStyle=q.axisLine,g.lineWidth=.5;const Z=X-D+4,Y=K+pt-4,j=io(xt),Mt=Oe(j,1,0),Ot=Oe(j,0,1),ct=1e-9;function yt(u){return tt?Math.abs(u.y)<ct&&Math.abs(u.x)>ct?{pitch:tt.x,sign:Math.sign(u.x)}:Math.abs(u.x)<ct&&Math.abs(u.y)>ct?{pitch:tt.y,sign:Math.sign(u.y)}:null:null}const Tt=yt(Mt),z=yt(Ot);function F(u){return Tt?Math.round(u*Tt.sign/Tt.pitch):u}function G(u){return z?Math.round(u*z.sign/z.pitch):u}function St(u){if(!u)return Ft(50/V);const k=50/(u*V);return Math.max(1,Math.round(k))*u}const At=Tt?.pitch,Rt=z?.pitch,ot=St(At),nt=St(Rt);g.textAlign="center",g.textBaseline="top";const Nt=Math.ceil((K-x)/V/ot)*ot,Et=(e-K-x)/V;for(let u=Nt;u<=Et;u+=ot){const k=x+u*V;if(k<K||k>e-K)continue;g.beginPath(),g.moveTo(k,Z-Ke),g.lineTo(k,Z),g.stroke();const dt=tt?String(F(u)):Xe(u);g.fillText(dt,k,Z+2)}g.textAlign="right",g.textBaseline="middle";const st=Math.ceil((c-(X-K))/V/nt)*nt,Ht=(c-K)/V;for(let u=st;u<=Ht;u+=nt){const k=c-u*V;if(k<K||k>X-K)continue;g.beginPath(),g.moveTo(Y,k),g.lineTo(Y+Ke,k),g.stroke();const dt=tt?String(G(u)):Xe(u);g.fillText(dt,Y-2,k)}g.restore()}function So(g,e,X,x,c,V,K){const D=[],pt=K;if(pt<1){const q=Ft((e-g)*x/X);if(q>0){const Z=Math.ceil(g/q)*q;for(let Y=Z;Y<=e+q*1e-6;Y+=q){if(Y<=g||Y>=e)continue;const j=(1-(Math.log10(Y)-V)/K)*X;j>c&&j<X-c&&D.push(Y)}}return D}const tt=pt<3?[1,2,5]:[1],xt=Math.floor(Math.log10(g)),bt=Math.ceil(Math.log10(e));for(let q=xt;q<=bt;q++)for(const Z of tt){const Y=Z*Math.pow(10,q);if(Y<=g||Y>=e)continue;const j=(1-(Math.log10(Y)-V)/K)*X;j>c&&j<X-c&&D.push(Y)}return D}export{fmt,fmtColorbarAxis}from"../renderer/fmt.js";
@@ -433,7 +433,19 @@ export declare function reparentTooltip(parent?: HTMLElement): void;
433
433
  * be feasible: flip to just below it, else just above it. A large anchor keeps
434
434
  * exactly the previous cursor-following behaviour.
435
435
  */
436
- export declare function positionTooltip(tooltip: HTMLDivElement, anchor: Element, clientX: number, clientY: number): void;
436
+ /**
437
+ * Place the shared tooltip beside the pointer, inside the viewport.
438
+ *
439
+ * By default it also keeps off `anchor` when that is small enough to step
440
+ * around — a toolbar button's tip must not cover the button. Pass
441
+ * `followPointer` when the pointer is on the very content the tip describes (a
442
+ * die on the map): there is nothing to step around, and stepping clear of the
443
+ * whole canvas threw the tip hundreds of pixels from the die whenever the map
444
+ * was under 40% of the window tall — every gallery card.
445
+ */
446
+ export declare function positionTooltip(tooltip: HTMLDivElement, anchor: Element, clientX: number, clientY: number, opts?: {
447
+ followPointer?: boolean;
448
+ }): void;
437
449
  /**
438
450
  * Wire the shared themed tooltip onto a plain DOM element.
439
451
  *
@@ -464,7 +476,9 @@ export interface ToolbarHelpers {
464
476
  makeBtn(iconKey: string, label: string, onClick: () => void): HTMLButtonElement;
465
477
  setActive(btn: HTMLButtonElement, active: boolean): void;
466
478
  makeSep(): HTMLDivElement;
467
- makeMenuRow(label: string, active: boolean, indent: boolean, onClick: (e: MouseEvent) => void): HTMLDivElement;
479
+ makeMenuRow(label: string, active: boolean, indent: boolean, onClick: (e: MouseEvent) => void, mark?: {
480
+ derived: boolean;
481
+ }): HTMLDivElement;
468
482
  makeMenuSection(label: string): HTMLDivElement;
469
483
  makeDropdown<T extends string>(iconKey: string, title: string, getItems: () => Array<{
470
484
  value: T;
@@ -481,12 +495,15 @@ export interface ToolbarHelpers {
481
495
  getOpenMenu(): HTMLDivElement | null;
482
496
  setOpenMenu(menu: HTMLDivElement | null): void;
483
497
  }
498
+ /** `label` is the plain name (with unit); `derived` puts the † in front of it
499
+ * when the menu draws the row — kept apart so the menu can align it in a slot. */
484
500
  export type ModeEntry = {
485
501
  plotMode: PlotMode;
486
502
  activeTest?: number;
487
503
  activeMetadataKey?: string;
488
504
  label: string;
489
505
  logScale?: boolean;
506
+ derived?: true;
490
507
  };
491
508
  /**
492
509
  * Which plot modes the data actually supports — the single derivation shared by
@@ -681,6 +698,9 @@ export interface OverlayHandle {
681
698
  close: () => void;
682
699
  /** Raise this window above other open windows. No-op in 'modal' mode. */
683
700
  bringToFront: () => void;
701
+ /** Replace the box's natural height (see `OverlayOptions.boxSize`) — applied
702
+ * now unless maximized or minimized, and restored to from either. */
703
+ setNaturalHeight: (height: string) => void;
684
704
  }
685
705
  /** Create and open a resizable, maximizable, exclusive expand modal — dims and
686
706
  * blocks the rest of the page. See `openFloatingWindow` for the non-modal form. */
@@ -699,6 +719,14 @@ export interface ReparentModalOptions {
699
719
  };
700
720
  /** Called after the modal has closed and every element has been restored to its original position (or appended back, if its original spot is no longer available — see the stale-reference note above). Runs once, even if nothing was actually moved (the re-entrancy guard tripped). */
701
721
  onClosed?: () => void;
722
+ /**
723
+ * Where to resolve the modal's root from — see `OverlayOptions.anchor`.
724
+ * Defaults to `elements[0]`, which is right whenever that element is already
725
+ * on the page. Pass one explicitly when it is not: an element built only to
726
+ * be shown in the modal (a drilldown chart card) has no parent, so resolving
727
+ * from it lands on bare `doc.body`, behind a host's own `<dialog>`.
728
+ */
729
+ anchor?: Element;
702
730
  }
703
731
  /**
704
732
  * Reparent one or more elements (moved together, in the given order) into a