@wafertools/wafermap 0.26.1 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/AGENTS.md +10 -2
  2. package/CHANGELOG.md +670 -0
  3. package/README.md +46 -3
  4. package/dist/packages/canvas-adapter/canvasTheme.d.ts +4 -0
  5. package/dist/packages/canvas-adapter/canvasTheme.js +1 -1
  6. package/dist/packages/canvas-adapter/charts/barPanel.d.ts +16 -0
  7. package/dist/packages/canvas-adapter/charts/barPanel.js +1 -1
  8. package/dist/packages/canvas-adapter/charts/binCluster.d.ts +0 -1
  9. package/dist/packages/canvas-adapter/charts/binCluster.js +1 -1
  10. package/dist/packages/canvas-adapter/charts/boxplot.d.ts +21 -2
  11. package/dist/packages/canvas-adapter/charts/boxplot.js +1 -1
  12. package/dist/packages/canvas-adapter/charts/capability.d.ts +10 -11
  13. package/dist/packages/canvas-adapter/charts/capability.js +1 -1
  14. package/dist/packages/canvas-adapter/charts/chartShell.d.ts +337 -3
  15. package/dist/packages/canvas-adapter/charts/chartShell.js +1 -1
  16. package/dist/packages/canvas-adapter/charts/correlation.d.ts +8 -10
  17. package/dist/packages/canvas-adapter/charts/correlation.js +2 -1
  18. package/dist/packages/canvas-adapter/charts/groupedBarPlot.d.ts +50 -0
  19. package/dist/packages/canvas-adapter/charts/groupedBarPlot.js +1 -0
  20. package/dist/packages/canvas-adapter/charts/histogram.d.ts +12 -2
  21. package/dist/packages/canvas-adapter/charts/histogram.js +1 -1
  22. package/dist/packages/canvas-adapter/charts/regionYieldDiagram.d.ts +0 -1
  23. package/dist/packages/canvas-adapter/charts/regionYieldDiagram.js +1 -1
  24. package/dist/packages/canvas-adapter/charts/scatter.d.ts +6 -1
  25. package/dist/packages/canvas-adapter/charts/scatter.js +1 -1
  26. package/dist/packages/canvas-adapter/charts/testPassRate.d.ts +20 -0
  27. package/dist/packages/canvas-adapter/charts/testPassRate.js +1 -0
  28. package/dist/packages/canvas-adapter/charts/trend.d.ts +37 -0
  29. package/dist/packages/canvas-adapter/charts/trend.js +1 -0
  30. package/dist/packages/canvas-adapter/dieList.js +7 -7
  31. package/dist/packages/canvas-adapter/hitGrid.d.ts +22 -0
  32. package/dist/packages/canvas-adapter/hitGrid.js +1 -0
  33. package/dist/packages/canvas-adapter/identityHeader.d.ts +46 -0
  34. package/dist/packages/canvas-adapter/identityHeader.js +1 -0
  35. package/dist/packages/canvas-adapter/index.d.ts +2 -1
  36. package/dist/packages/canvas-adapter/index.js +1 -1
  37. package/dist/packages/canvas-adapter/insightsTab.d.ts +51 -5
  38. package/dist/packages/canvas-adapter/insightsTab.js +1 -1
  39. package/dist/packages/canvas-adapter/maplessSummary.d.ts +5 -1
  40. package/dist/packages/canvas-adapter/maplessSummary.js +1 -1
  41. package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +15 -2
  42. package/dist/packages/canvas-adapter/renderWaferGallery.js +1 -20
  43. package/dist/packages/canvas-adapter/renderWaferMap.d.ts +110 -18
  44. package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
  45. package/dist/packages/canvas-adapter/summaryPanel.d.ts +233 -37
  46. package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
  47. package/dist/packages/canvas-adapter/toCanvas.d.ts +6 -0
  48. package/dist/packages/canvas-adapter/toCanvas.js +1 -1
  49. package/dist/packages/canvas-adapter/toolbar.d.ts +352 -15
  50. package/dist/packages/canvas-adapter/toolbar.js +2 -2
  51. package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
  52. package/dist/packages/canvas-adapter/userGuideHtml.js +115 -41
  53. package/dist/packages/canvas-adapter/version.d.ts +2 -2
  54. package/dist/packages/canvas-adapter/version.js +1 -1
  55. package/dist/packages/canvas-adapter/warnings.d.ts +21 -0
  56. package/dist/packages/canvas-adapter/warnings.js +1 -1
  57. package/dist/packages/core/dies.d.ts +11 -0
  58. package/dist/packages/core/dies.js +1 -1
  59. package/dist/packages/core/utils.d.ts +11 -0
  60. package/dist/packages/core/utils.js +1 -1
  61. package/dist/packages/renderer/binColors.d.ts +80 -0
  62. package/dist/packages/renderer/binColors.js +1 -0
  63. package/dist/packages/renderer/buildView.d.ts +100 -6
  64. package/dist/packages/renderer/buildView.js +1 -1
  65. package/dist/packages/renderer/buildWaferMap.d.ts +112 -7
  66. package/dist/packages/renderer/buildWaferMap.js +1 -1
  67. package/dist/packages/renderer/colorMap.d.ts +16 -15
  68. package/dist/packages/renderer/colorMap.js +1 -1
  69. package/dist/packages/renderer/colorSchemes.d.ts +45 -26
  70. package/dist/packages/renderer/colorSchemes.js +1 -1
  71. package/dist/packages/renderer/index.d.ts +3 -1
  72. package/dist/packages/renderer/index.js +1 -1
  73. package/dist/packages/stats/analyzeWaferLot.js +1 -1
  74. package/dist/packages/stats/analyzeWaferMap.js +1 -1
  75. package/dist/packages/stats/binPareto.d.ts +15 -0
  76. package/dist/packages/stats/binPareto.js +1 -1
  77. package/dist/packages/stats/clusterDetection.js +1 -1
  78. package/dist/packages/stats/connectedComponents.d.ts +14 -0
  79. package/dist/packages/stats/connectedComponents.js +1 -0
  80. package/dist/packages/stats/correlation.d.ts +26 -0
  81. package/dist/packages/stats/correlation.js +1 -1
  82. package/dist/packages/stats/facets.js +1 -1
  83. package/dist/packages/stats/filterFindings.d.ts +18 -0
  84. package/dist/packages/stats/filterFindings.js +1 -1
  85. package/dist/packages/stats/findingsNarrative.js +1 -1
  86. package/dist/packages/stats/histogram.d.ts +47 -2
  87. package/dist/packages/stats/histogram.js +1 -1
  88. package/dist/packages/stats/index.d.ts +6 -0
  89. package/dist/packages/stats/index.js +1 -1
  90. package/dist/packages/stats/mergeTestDefs.d.ts +44 -0
  91. package/dist/packages/stats/mergeTestDefs.js +1 -0
  92. package/dist/packages/stats/metadataColumns.d.ts +20 -4
  93. package/dist/packages/stats/metadataColumns.js +1 -1
  94. package/dist/packages/stats/patternClassification.js +1 -1
  95. package/dist/packages/stats/population.d.ts +23 -0
  96. package/dist/packages/stats/population.js +1 -0
  97. package/dist/packages/stats/renderFindingsReport.js +15 -15
  98. package/dist/packages/stats/renderSummaryReport.js +35 -32
  99. package/dist/packages/stats/reportHtml.js +38 -13
  100. package/dist/packages/stats/testPassRate.d.ts +85 -0
  101. package/dist/packages/stats/testPassRate.js +1 -0
  102. package/dist/packages/stats/trend.d.ts +40 -0
  103. package/dist/packages/stats/trend.js +1 -0
  104. package/dist/packages/stats/types.d.ts +117 -6
  105. package/dist/packages/stats/yield.js +1 -1
  106. package/llms.txt +6 -0
  107. package/package.json +9 -5
  108. package/dist/packages/canvas-adapter/metadataBadge.d.ts +0 -30
  109. package/dist/packages/canvas-adapter/metadataBadge.js +0 -1
@@ -1 +1 @@
1
- import{buildView as oi,buildHoverText as ai,findTestDef as it,resolveTestNumber as Dt}from"../renderer/buildView.js";import{toCanvas as si,BIN_LEGEND_W as wn,BIN_LEGEND_W_COMPACT as li,BIN_LEGEND_ADAPT_COMPACT as ri,BIN_LEGEND_ADAPT_FLOATING as di}from"./toCanvas.js";import{buildWaferMap as ci,getTestPassStatus as pi,isParametricTest as Bt}from"../renderer/buildWaferMap.js";import{analyzeWaferMap as fi}from"../stats/analyzeWaferMap.js";import{CLR as S,Z_BASE as ui,applyOverlayZ as gi,getTooltip as Sn,hideTooltip as ot,reparentTooltip as hi,positionTooltip as kt,createToolbarHelpers as mi,buildModeMenuEl as yi,openReparentedModal as bi,openUserGuideWindow as vi,makePaletteBtn as xi,makeLogScaleBtn as wi,makeLegendStyleBtn as Si,makeOverlaysBtn as Ci,makeOrientationBtn as Mi,menuLayerFor as Cn,saveImageBlob as Ei,markMenuTrigger as Ye,wireMenuA11y as Mn,wireExpandToggle as Di,nextFrame as Lt,passFailMenuRows as Bi,requestedPassFailDisplay as Tt,logWmapVersionOnce as ki,buildDataModeEntries as Li,metadataKeyHasData as Ti,metadataModeEntry as Pi}from"./toolbar.js";import{createSummaryPanelEl as Pt,wrapWithSummaryPanel as Ot,renderWaferSummaryContent as Oi}from"./summaryPanel.js";import{collectWarnings as Ri,buildWarningsMenuEl as Ii,severityOf as Fi}from"./warnings.js";import"../stats/facets.js";import{ICONS as En}from"./icons.js";import{hardBinColor as Wi,softBinColor as Hi,metadataValueColor as Ai}from"../renderer/colorMap.js";import{createInsightsTab as Yi}from"./insightsTab.js";import{createMetadataBadge as zi}from"./metadataBadge.js";import{getDieKey as Ee,hasPosition as Dn,isPositionedDie as Xi}from"../core/dies.js";import{buildDieListSection as Ni}from"./dieList.js";import{buildMaplessSummary as Ki}from"./maplessSummary.js";const Bn=new Set(["colorScheme","rotation","flipX","flipY","showDieLabels","showPartialDies","showRingBoundaries","showQuadrantBoundaries","showReticle","showXYIndicator","ringCount","legendPosition","logScale","colorbarRangeMode","markFailingDies"]);export function classifyChanged(N){const m=N.some(g=>Bn.has(g)),C=N.some(g=>!Bn.has(g));return m&&C?"mixed":m?"preference":"state"}export function renderWaferMap(N,m,C={}){ki();const g=N.ownerDocument,K=g.defaultView??window;K.getComputedStyle(N).position==="static"&&(N.style.position="relative");const kn=N.clientHeight;C.height!=null&&(N.style.height=typeof C.height=="number"?`${C.height}px`:C.height);const at=m.dataCoverage?m.dataCoverage.unpositionedDies:m.dies.filter(e=>!Dn(e)).length,ze=(m.dataCoverage?m.dataCoverage.totalDies:m.dies.length-at)===0&&at>0,q=g.createElement("div");Object.assign(q.style,{position:"relative",width:"100%",height:"100%"}),C.maxSize!=null&&(q.style.maxWidth=`${C.maxSize}px`,q.style.maxHeight=`${C.maxSize}px`),N.appendChild(q);const M=g.createElement("div");Object.assign(M.style,{position:"relative",width:"100%",height:"100%"});const a=g.createElement("canvas");Object.assign(a.style,{width:"100%",height:"100%",display:"block"}),M.appendChild(a),q.appendChild(M);let Xe=null;if(at>0){let n=function(){if(t){const u=Ni(e,m.testDefs,{...C.dieList,onSaveText:C.onSaveText,waferMetadata:J.metadata,metadataFields:le,ownerDocument:g});if(u)return u}const i=it(m.testDefs,l.activeTest??0),d=Bt(i)?l.logScale??i?.logScale??!1:!1;return Ki(e,m.testDefs,{plotMode:l.plotMode??"hardBin",activeTest:l.activeTest,hbinDefs:$,sbinDefs:G,colorScheme:l.colorScheme,logScale:d,colorbarRangeMode:l.colorbarRangeMode,valueRangeDies:m.dies})},o=function(i){const d=g.createElement("button");return d.type="button",Object.assign(d.style,{fontSize:"10px",padding:"2px 6px",borderRadius:"4px",flexShrink:"0",border:`1px solid ${S.menuBorder}`,background:S.menuBg,color:S.text,cursor:"pointer"}),d.textContent=t?"View chart":"View die list",d.addEventListener("click",u=>{u.stopPropagation(),t=!t,d.textContent=t?"View chart":"View die list",i()}),d};const e=m.dies.filter(i=>!Dn(i));let t=!1;if(ze){const i=g.createElement("div");Object.assign(i.style,{position:"absolute",inset:"0",background:S.panelBg,padding:"12px",paddingTop:C.showToolbar??!0?"44px":"12px",boxSizing:"border-box",overflow:"hidden",display:"flex",flexDirection:"column",gap:"8px",zIndex:"1"});const d=g.createElement("div");Object.assign(d.style,{display:"flex",justifyContent:"space-between",alignItems:"flex-start",gap:"8px"});const u=g.createElement("span");Object.assign(u.style,{fontSize:"11px",color:S.label,lineHeight:"1.4"}),u.textContent="No die position data for this wafer.",d.appendChild(u);const s=g.createElement("div");Object.assign(s.style,{flex:"1",minHeight:"0",overflow:"auto",display:"flex",flexDirection:"column"}),d.appendChild(o(()=>s.replaceChildren(n()))),i.appendChild(d),i.appendChild(s),M.appendChild(i),Xe=()=>{t||s.replaceChildren(n())}}else{const i=g.createElement("div");Object.assign(i.style,{position:"absolute",left:"0",right:"0",bottom:"0",zIndex:"3",background:S.panelBg,borderTop:`1px solid ${S.menuBorder}`,fontSize:"11px",color:S.text,cursor:"pointer",padding:"4px 8px",display:"flex",alignItems:"center",justifyContent:"space-between",gap:"8px"});const d=g.createElement("span");d.textContent=`+${e.length} ${e.length===1?"die":"dies"} without position data`,i.appendChild(d);const u=g.createElement("span");u.textContent="\u25BE",Object.assign(u.style,{fontSize:"12px",lineHeight:"1",color:S.label,flexShrink:"0"}),i.appendChild(u),i.setAttribute("aria-expanded","false"),i.setAttribute("aria-label",`${e.length} dies without position data. Click to view.`);let s=null,r=null;Di(i,p=>{if(u.textContent=p?"\u25B4":"\u25BE",i.setAttribute("aria-expanded",String(p)),p){s=g.createElement("div");const c=i.getBoundingClientRect().height||28;Object.assign(s.style,{position:"absolute",left:"0",right:"0",bottom:`${c}px`,top:"20%",zIndex:"2",background:S.panelBg,borderTop:`1px solid ${S.menuBorder}`,padding:"8px",boxSizing:"border-box",display:"flex",flexDirection:"column",gap:"8px"}),s.addEventListener("click",Q=>Q.stopPropagation());const h=g.createElement("div");Object.assign(h.style,{display:"flex",justifyContent:"flex-end"}),h.appendChild(o(()=>{r&&r.replaceChildren(n())})),s.appendChild(h),r=g.createElement("div"),Object.assign(r.style,{flex:"1",minHeight:"0",overflow:"auto",display:"flex",flexDirection:"column"}),r.appendChild(n()),s.appendChild(r),M.appendChild(s)}else s&&(s.remove(),s=null,r=null)}),M.appendChild(i),Xe=()=>{r&&!t&&r.replaceChildren(n())}}}const{onHover:Ne,onClick:Ln,onSelect:ce,onViewOptionsChange:Tn,showTooltip:Pn=!0,showToolbar:he=!0,showMetadataBadge:On=!0,toolbarControls:Rt="full",showPlotModeSelector:Rn=!0,minZoom:In=.4,maxZoom:Fn=20,summaryPanel:De,renderTooltip:It,passBins:st=[1],showHelpButton:Wn=!1,userGuideExtension:Hn,onExpand:Ft,showExpandButton:Wt=!0,zIndex:An,insights:Ht,warnings:At,viewOptions:Yn={},...ie}=C,zn=Ht?.enabled??!1,Yt=At?.display??!0,Xn=gi(An);let Be=ie.fallbackFormat,oe=C.statsSummary,ae=[],zt=!1;function Xt(){const e=Ri({result:m,statsSummary:oe}),t=e.length!==ae.length||e.some((n,o)=>n.code!==ae[o]?.code||n.message!==ae[o]?.message);ae=e,(t||!zt)&&(zt=!0,At?.onWarning?.(e))}Xt();let lt=m,J=m.wafer,se=m.dies,E=new Set,$=m.hbinDefs,G=m.sbinDefs,ee=m.testDefs,le=m.metadataFields,Nt=m.reticles?.length?m.reticles:void 0,Kt=m.reticleConfig,Vt=m.view?.dataAxisFlip,rt=m.isLotStack??!1,dt=m.aggrMethod,ct=m.lotSize,jt=!1,T=null;On&&(T=zi(J.metadata,{lotStack:_t(),ownerDocument:g}),T.isEmpty()||M.appendChild(T.el));function _t(){return rt?{lotSize:ct??1,aggrMethod:dt}:void 0}function Nn(){if(!T)return;T.update(J.metadata,_t());const e=T.el.isConnected;T.isEmpty()&&e?T.el.remove():!T.isEmpty()&&!e&&M.appendChild(T.el)}const $t=[...$??[],...G??[]].some(e=>e.color);let l={plotMode:"hardBin",colorScheme:$t?"custom":"default",showDieLabels:!1,showPartialDies:!0,showRingBoundaries:!1,showQuadrantBoundaries:!1,showReticle:!1,showXYIndicator:!1,ringCount:4,rotation:0,flipX:!1,flipY:!1,legendPosition:ie.legendPosition??"default",...Yn},re=null,me=!1;zn&&(re=Yi({getItems:()=>[{wafer:J,dies:se,hbinDefs:$,sbinDefs:G,testDefs:ee,label:String(J.metadata?.waferId??""),statsSummary:oe}],getColorSchemeName:()=>l.colorScheme??"default",passBins:st,getRingCount:()=>l.ringCount??4,onSaveImage:C.onSaveImage,onSaveText:C.onSaveText,defaultView:Ht?.defaultView,backTab:{label:"Map",onBack:()=>pt(!1)},ownerDocument:g}),Object.assign(re.el.style,{position:"absolute",inset:"0",background:S.panelBg,overflowY:"auto",paddingTop:he?"44px":"0"}),q.appendChild(re.el));function pt(e){re&&(me=e,re.el.style.display=e?"flex":"none",H&&(H.style.display=e||ze?"none":"flex"),k&&(k.style.display=e?"none":"flex"),Z&&(Z.style.display=e?"none":"flex"),T&&(T.el.style.display=e||jt?"none":""),Ze(),A&&(A.innerHTML=e?En.wafer:En.analysis,A.ariaLabel=e?"Back to wafer view":"Insights"),e&&re.render())}let y,Gt,W=null,P=null,ft=[],ke=null,ut=ie.legendOffset??{x:0,y:0},Le=!1,ye=!1,Te={x:0,y:0},gt={x:0,y:0},pe=!1,be=!1,Y="pan",ve={x:0,y:0},Ke={x:0,y:0},O={x:0,y:0},R={x:0,y:0},Pe=!1,xe=!1;function Oe(){const e=l;y=oi(J,se.filter(Xi),{plotMode:e.plotMode,colorScheme:e.colorScheme,passBins:st,showDieLabels:e.showDieLabels,showPartialDies:e.showPartialDies,showRingBoundaries:e.showRingBoundaries,showQuadrantBoundaries:e.showQuadrantBoundaries,showReticle:e.showReticle,showXYIndicator:e.showXYIndicator,reticles:Nt,ringCount:e.ringCount,highlightBin:e.highlightBin,highlightMetadataValue:e.highlightMetadataValue,activeTest:e.activeTest,activeMetadataKey:e.activeMetadataKey,testDefs:ee,valueRange:e.valueRange,logScale:e.logScale,isLotStack:rt,aggregationMethod:dt??e.aggregationMethod,lotSize:ct??e.lotSize,dataAxisFlip:Vt,colorbarRangeMode:e.colorbarRangeMode,passFailDisplay:e.passFailDisplay,fallbackFormat:Be,interactiveTransform:{rotation:e.rotation??0,flipX:e.flipX??!1,flipY:e.flipY??!1}},{hbinDefs:$,sbinDefs:G,metadataFields:le}),Gt=new Map(y.dies.map((t,n)=>[Ee(t),n]))}Oe();let z=null,fe=null,Ve=null,Kn={},I=null,te=null;function Vn(e){const{kind:t,index:n}=e.variable;x(t==="test"?{plotMode:"value",activeTest:n??0,highlightBin:void 0}:t==="softBin"?{plotMode:"softBin",highlightBin:void 0}:{plotMode:"hardBin",highlightBin:void 0});const o=e.highlight;o.kind==="bin"?(xt(o.dieKeys),x({highlightBin:o.bin})):(o.kind==="region"||o.kind==="dies")&&xt(o.dieKeys)}function Zt(e){Oi(e,{wafer:J,dies:se,yieldSummary:lt.yield,dataCoverage:lt.dataCoverage,hbinDefs:$,sbinDefs:G,testDefs:ee,statsSummary:oe,warnings:Yt?ae:[],passBins:st,ringCount:l.ringCount??4,colorScheme:l.colorScheme,fallbackFormat:Be,activeFindingId:Ve,findingsFilter:Kn,onFindingsFilterChange:ue,onSaveText:C.onSaveText,metadataFields:le,dieListOptions:C.dieList,onFindingClick:(t,n)=>{Ve===t.id?(Ve=null,xt([]),x({highlightBin:void 0})):(Ve=t.id,Vn(t)),ue()}})}function ue(){z&&Zt(z)}function je(){I&&Zt(I)}function Qt(e,t){he&&(t==="right"||t==="top")&&(e.style.paddingTop="44px")}if(De?.placement){const e=De.placement;z=Pt(e,g),Qt(z,e);const t=M.parentElement,n=M.nextSibling;fe=Ot(M,z,e),t?.insertBefore(fe,n),ue()}else if(oe){const e=De?.defaultOpen??!he;I=Pt("right",g),Qt(I,"right"),I.style.display=e?"block":"none";const t=M.parentElement,n=M.nextSibling;te=Ot(M,I,"right"),t?.insertBefore(te,n),je()}const v=Pn?Sn(g):null;let D=null,B=null,H=null,k=null,_e=null,F=null,Z=null,$e=null,A=null,ne=null,Ge=null;function Ut(){if(!ne||!Ge)return;const e=ae.length,t=e>0;if(ne.style.display=t?"flex":"none",Ge.style.display=t?"":"none",!t)return;const n=Fi(ae[0]),o=`${e} data ${e===1?"warning":"warnings"}`;ne.style.color=n==="error"?S.errText:S.warnText,ne.ariaLabel=n==="error"?`${o} \u2014 the map may be positionally wrong`:o}const jn=24;let we=null,ht=null,qt=null,mt=null,yt=null,bt=null,vt=null;function xt(e){E=new Set(e??[]),ce&&ce(Mt()),w()}function Ze(){if(!F)return;const e=!!(z??I);F.style.display=oe&&e&&!me?"flex":"none";const t=z??I,n=t?t.style.display!=="none":!1;oe?.hasNotableFindings&&!n?F.style.color=S.findingIndicator:F.dataset.active||(F.style.color=S.icon)}if(he){let Q=function(U){Y=U,o(V,U==="zoom"),o(Ie,U==="pan"),_e&&o(_e,U==="select"),a.style.cursor=U==="pan"?"grab":"crosshair"},qe=function(){we&&(clearTimeout(we),we=null),D&&(D.style.opacity="1")},Je=function(){we=setTimeout(()=>{D&&(D.style.opacity="0.35")},600)};D=g.createElement("div"),D.dataset.wmapToolbar="single",Object.assign(D.style,{position:"absolute",top:"4px",right:"4px",display:"flex",flexDirection:"row",alignItems:"center",flexWrap:"wrap-reverse",justifyContent:"flex-end",maxWidth:"calc(100% - 8px)",background:S.menuBg,border:`1px solid ${S.menuBorder}`,borderRadius:"4px",boxShadow:"0 1px 4px rgba(0,0,0,0.12)",zIndex:ui,opacity:"0.35",transition:"opacity 0.2s ease",pointerEvents:"auto"});const e=Sn(g),t=mi(e),{makeBtn:n,setActive:o,makeSep:i,makeMenuRow:d,makeMenuSection:u,makeDropdown:s,makeCheckMenuBtn:r,closeOpenMenu:p,getOpenMenu:c,setOpenMenu:h}=t;ht=p,qt=c,g.addEventListener("click",p,!0),H=g.createElement("div"),Object.assign(H.style,{display:ze?"none":"flex",alignItems:"center",gap:"0",flexWrap:"wrap",justifyContent:"flex-end"}),D.appendChild(H);const Me=n("download","Download PNG",Gn);H.appendChild(Me),H.appendChild(i());const V=n("zoomMode","Zoom (drag to zoom region)",()=>Q("zoom")),de=n("zoomIn","Zoom in",()=>Ce(a.clientWidth/2,a.clientHeight/2,1.2)),L=n("zoomOut","Zoom out",()=>Ce(a.clientWidth/2,a.clientHeight/2,1/1.2)),j=n("reset","Reset zoom (double-click)",()=>Ue());H.appendChild(V),H.appendChild(de),H.appendChild(L),H.appendChild(j),H.appendChild(i());const Ie=n("pan","Pan (drag to move)",()=>Q("pan"));if(H.appendChild(Ie),_e=n("boxSelect","Select (drag to select dies)",()=>Q("select")),H.appendChild(_e),o(Ie,!0),Rt!=="view-only"){let U=function(f){return f.plotMode!==(l.plotMode??"hardBin")?!1:f.plotMode==="value"?(l.activeTest??0)===(f.activeTest??0):f.plotMode==="metadata"?l.activeMetadataKey===f.activeMetadataKey:!0},Fe=function(f,b){f.activeTest!==void 0?x({plotMode:f.plotMode,activeTest:f.activeTest,activeMetadataKey:void 0,logScale:f.logScale}):f.activeMetadataKey!==void 0?x({plotMode:f.plotMode,activeTest:void 0,activeMetadataKey:f.activeMetadataKey,passFailDisplay:"off"}):x({plotMode:f.plotMode,activeTest:void 0,activeMetadataKey:void 0,passFailDisplay:"off"}),b.remove(),h(null),Ye(_,!1)};B=g.createElement("div"),Object.assign(B.style,{display:"flex",alignItems:"center",gap:"0",flexWrap:"wrap",justifyContent:"flex-end"}),D.appendChild(B),k=g.createElement("div"),Object.assign(k.style,{display:"flex",alignItems:"center",gap:"0"}),B.appendChild(k),k.appendChild(i());const et=()=>{const f=c();f&&(f.remove(),h(null)),Ye(_,!1)},_=n("mode","Plot mode",()=>{if(c()){et();return}const f=se,b=y.testDefs,{testEntries:nt,binEntries:ge,stackedEntries:We}=Li(f,b,{includeStacked:y.isLotStack}),Et=(le??[]).filter(Ae=>Ti(f,Ae.key)).map(Pi),He=yi(_.getBoundingClientRect(),nt,ge,We,U,Fe,{makeMenuRow:d,makeMenuSection:u},l.plotMode??"hardBin",_.ownerDocument.defaultView??window,Et);Cn(_).appendChild(He),h(He),Ye(_,!0),Mn(He,_,et)});Ye(_,!1);const{btn:Zn,sync:Qn}=xi(t,()=>l.plotMode??"hardBin",()=>l.colorScheme??"default",()=>$t,f=>x({colorScheme:f}),{get:()=>l.markFailingDies??!1,set:f=>x({markFailingDies:f})});yt=Qn,yt();const Un=Ci(t,()=>{const f=!!y.hasReticle,b=(l.plotMode??"hardBin")==="value",{testNumber:nt}=Dt(l.activeTest??0,y.testDefs),ge=it(y.testDefs,nt),We=b&&ge!==void 0&&!Bt(ge),Et=b&&!We&&(ge?.limitLow!==void 0||ge?.limitHigh!==void 0),He=b&&!We&&y.dies.some(Ae=>pi(Ae,nt,ge)!==void 0);return[{label:"Ring boundaries",active:!!l.showRingBoundaries,onClick:()=>x({showRingBoundaries:!l.showRingBoundaries})},{label:"Quadrant lines",active:!!l.showQuadrantBoundaries,onClick:()=>x({showQuadrantBoundaries:!l.showQuadrantBoundaries})},{label:"Die labels",active:!!l.showDieLabels,onClick:()=>x({showDieLabels:!l.showDieLabels})},{label:"Reticle grid",active:!!l.showReticle,enabled:f,onClick:()=>x({showReticle:!l.showReticle})},{label:"XY indicator",active:!!l.showXYIndicator,onClick:()=>x({showXYIndicator:!l.showXYIndicator})},...Bi({functionalActive:We,hasLimits:Et,hasRecorded:He,display:Tt(l)},Ae=>x({passFailDisplay:Ae}))]},()=>!!(l.showRingBoundaries||l.showQuadrantBoundaries||l.showDieLabels||l.showReticle||l.showXYIndicator||Tt(l)!=="off")),{btn:qn,sync:Jn}=Si(t,()=>l,f=>x({legendPosition:f}));mt=Jn,mt();const xn=()=>{const{testNumber:f}=Dt(l.activeTest??0,ee),b=it(ee,f);return b!==void 0&&!Bt(b)},{btn:ei,sync:ti}=wi(t,()=>({...l,functionalActive:xn()}),f=>x(f));bt=ti,bt();const ni=()=>{const{testNumber:f}=Dt(l.activeTest??0,ee),b=it(ee,f);return b!==void 0&&(b.limitLow!==void 0||b.limitHigh!==void 0)},tt=n("specRange","Colorbar range: spec limits",()=>{const f=l.colorbarRangeMode==="data"?"spec":"data";x({colorbarRangeMode:f})});vt=()=>{const f=l.plotMode==="value"&&ni()&&Tt(l)==="off"&&!xn();tt.style.display=f?"":"none";const b=(l.colorbarRangeMode??"spec")==="spec";o(tt,b),tt.ariaLabel=b?"Colorbar range: spec limits (click for data range)":"Colorbar range: data range (click for spec limits)"},vt();const ii=Mi(t,()=>l,f=>x(f));Rn&&k.appendChild(_),k.appendChild(Zn),k.appendChild(ei),k.appendChild(tt),ze||(k.appendChild(i()),k.appendChild(Un),k.appendChild(i()),k.appendChild(qn),k.appendChild(i()),k.appendChild(ii)),oe&&(F=n("findings","Summary panel",()=>{const f=z??I;if(!f)return;const b=f.style.display!=="none";f.style.display=b?"none":"block",o(F,!b),Ze()}),B.appendChild(i()),B.appendChild(F),I?.style.display!=="none"&&o(F,!0),Ze()),Yt&&(Ge=i(),ne=n("warning","Data warnings",()=>{const f=c();if(p(new MouseEvent("click")),f)return;const b=Ii(ne.getBoundingClientRect(),ae,K);Cn(q).appendChild(b),h(b),Mn(b,ne,()=>p(new MouseEvent("click")))}),Ye(ne,!1),B.appendChild(Ge),B.appendChild(ne),Ut()),Wt&&(B.appendChild(i()),Z=n("expand","Expand (E)",Ft??Jt),B.appendChild(Z)),re&&(B.appendChild(i()),A=n("analysis","Insights",()=>{pt(!me),o(A,me)}),A.dataset.wmapInsightsBtn="1",B.appendChild(A)),Wn&&(B.appendChild(i()),$e=n("help","User guide",()=>vn()),B.appendChild($e))}q.appendChild(D),a.addEventListener("mouseenter",qe),a.addEventListener("mouseleave",Je),D.addEventListener("mouseenter",qe),D.addEventListener("mouseleave",Je)}let Se=null;function Jt(){Se&&(Se.close(),Se=null);const e=fe??te??M;e.style.flex="1",e.style.minWidth="0",e.style.minHeight="0";const t=bi(D?[e,D]:[e],{ownerDocument:g,onClosed:()=>{Se=null,Z&&(Z.style.display="flex"),a.focus({preventScroll:!0})}});t&&(D&&(t.contentWrap.style.position="relative"),Se=t,Z&&(Z.style.display="none"))}function en(e){const t=l.plotMode;if(l={...l,...e},e.plotMode!==void 0&&e.plotMode!==t){W=null;const i=l.plotMode;(i==="hardBin"||i==="softBin")&&!new Set(["default","accessible","custom"]).has(l.colorScheme??"")&&(l={...l,colorScheme:"default"})}Object.keys(e).every(i=>i==="legendPosition"||i==="showLegend"||i==="markFailingDies")||Oe(),mt?.(),yt?.(),bt?.(),vt?.(),w();const o=e.plotMode!==void 0&&e.plotMode!==t;(e.colorScheme!==void 0||o)&&(ue(),je()),(e.colorScheme!==void 0||o||e.activeTest!==void 0||e.logScale!==void 0||e.colorbarRangeMode!==void 0)&&Xe?.()}function x(e){en(e);const t=Object.keys(e);Tn?.(l,t,classifyChanged(t))}let wt=!1;function St(){wt||(wt=!0,Lt(()=>{wt=!1,w()},K))}function w(){const e=P??void 0,t=y.dies[0],n=t?{x:t.width,y:t.height}:ie.diePitchMm,o=Math.floor(a.clientWidth||a.width),i=!!($?.length||G?.length||y.dies.some(h=>h.hbin!=null||h.sbin!=null)),d=l.legendPosition??"default",u=l.showLegend??!0,s=d==="default"||d==="compact",r=(ie.colorbarWidth??16)+28,p=i&&s?d!=="default"?Math.max(wn,r):o<di?0:o<ri?Math.max(li,r):Math.max(wn,r):void 0,c=si(a,y,{...ie,topClearance:he?jn:0,minRightReserve:u?p:0,showColorbar:u&&(ie.showColorbar??!0),markFailingDies:l.markFailingDies??!1,legendPosition:d,legendOffset:ut,diePitchMm:n,fallbackFormat:Be,showAxes:ie.showAxes??P!==null,viewport:e,activeBin:l.plotMode==="metadata"?l.highlightMetadataValue:l.highlightBin,hbinDefs:$,sbinDefs:G,metadataFields:le});ft=c.binLegendRows,ke=c.legendBox??null,W||(W=c.viewport),E.size>0&&_n(),be&&$n()}function _n(){const e=X();if(!e)return;const t=a.getContext("2d"),n=y.hoverPoints,o=y.rectangles[0],i=o?o.width/2*e.ppm:e.ppm*.5,d=o?o.height/2*e.ppm:e.ppm*.5,u=Math.max(1,Math.min(3,i*.08)),s=i-u,r=d-u,p=[];for(const c of E){const h=Gt.get(c);h!==void 0&&p.push({sx:e.originX+n[h].x*e.ppm,sy:e.originY-n[h].y*e.ppm})}if(p.length){t.save(),t.setLineDash([]),t.fillStyle="rgba(0,0,0,0.12)",t.beginPath();for(const{sx:c,sy:h}of p)t.rect(c-s,h-r,s*2,r*2);t.fill(),t.strokeStyle="rgba(255,255,255,0.9)",t.lineWidth=3,t.beginPath();for(const{sx:c,sy:h}of p)t.rect(c-s,h-r,s*2,r*2);t.stroke(),t.strokeStyle="rgba(0,0,0,0.9)",t.lineWidth=1.5,t.beginPath();for(const{sx:c,sy:h}of p)t.rect(c-s,h-r,s*2,r*2);t.stroke(),t.restore()}}function $n(){const e=a.getContext("2d"),t=Math.min(O.x,R.x),n=Math.min(O.y,R.y),o=Math.abs(R.x-O.x),i=Math.abs(R.y-O.y);e.save(),e.fillStyle="rgba(0,0,0,0.08)",e.fillRect(t,n,o,i),e.setLineDash([5,3]),e.strokeStyle="rgba(255,255,255,0.9)",e.lineWidth=3,e.strokeRect(t,n,o,i),e.strokeStyle="rgba(0,0,0,0.9)",e.lineWidth=1.5,e.strokeRect(t,n,o,i),e.restore()}function Gn(){a.toBlob(e=>{e&&Ei(e,C.downloadFilename??"wafermap",C.onSaveImage)})}function tn(e){return W?Math.max(W.ppm*In,Math.min(W.ppm*Fn,e)):e}function Ce(e,t,n){const o=P??W;if(!o)return;const i=tn(o.ppm*n),d=i/o.ppm,u=e-(e-o.originX)*d,s=t-(t-o.originY)*d,r=(W?.snapDist??1)/(i/(W?.ppm??i));P={originX:u,originY:s,ppm:i,snapDist:r},w()}function X(){return P??W}function nn(e){if(e.ctrlKey||e.metaKey){e.preventDefault();const t=a.getBoundingClientRect(),n=e.deltaY<0?1.12:1/1.12;Ce(e.clientX-t.left,e.clientY-t.top,n)}else if(P!==null){e.preventDefault();const t=X(),n=P.snapDist;P={originX:t.originX-e.deltaX,originY:t.originY-e.deltaY,ppm:t.ppm,snapDist:n},w()}}function on(e,t,n){return e>=n.x&&e<n.x+n.w&&t>=n.y&&t<n.y+n.h}function an(e){if(e.button!==0||!X())return;a.focus({preventScroll:!0}),a.setPointerCapture(e.pointerId);const t=a.getBoundingClientRect(),n=e.clientX-t.left,o=e.clientY-t.top;if(ke&&l.legendPosition==="floating"&&on(n,o,ke)){ye=!0,Te={x:n,y:o},gt={...ut};return}if(Pe&&Y!=="pan"){xe=!0,pe=!0,ve={x:n,y:o},Ke={x:X().originX,y:X().originY},a.style.cursor="grabbing";return}if(Y==="zoom"||Y==="select"){be=!0,O=R={x:n,y:o};return}pe=!0,ve={x:n,y:o},Ke={x:X().originX,y:X().originY},a.style.cursor="grabbing"}function sn(e){const t=a.getBoundingClientRect(),n=e.clientX-t.left,o=e.clientY-t.top;if(be){R={x:n,y:o},St();return}if(ye){const c=n-Te.x,h=o-Te.y;c*c+h*h>16&&(ye=!1,Le=!0,a.style.cursor="grabbing")}if(Le){ut={x:gt.x+(n-Te.x),y:gt.y+(o-Te.y)},St();return}if(pe){const c=X(),h=P?.snapDist??W?.snapDist??1;P={originX:Ke.x+(n-ve.x),originY:Ke.y+(o-ve.y),ppm:c.ppm,snapDist:h},St();return}const i=X();if(!i)return;const d=(n-i.originX)/i.ppm,u=(i.originY-o)/i.ppm,r=cn(d,u,i.snapDist)?.die??null,p=ft.find(c=>n>=c.x&&n<c.x+c.w&&o>=c.y&&o<c.y+c.h);if(p){a.style.cursor="pointer",v&&(v.innerHTML=p.label??`Bin ${p.bin}`,v.style.display="block",kt(v,a,e.clientX,e.clientY)),Ne?.(null,e);return}if(Y==="pan"&&(a.style.cursor=r?"crosshair":"grab"),v)if(r)if(It){const c=It(r);c===null?v.style.display="none":(typeof c=="string"?v.innerHTML=c:(v.innerHTML="",v.appendChild(c)),v.style.display="block",kt(v,a,e.clientX,e.clientY))}else v.innerHTML=ai(r,y.plotMode,{testDefs:ee,hbinDefs:$,sbinDefs:G,fallbackFormat:Be,aggrMethod:y.aggrMethod,lotSize:y.lotSize,waferMeta:J.metadata,metadataFields:le,activeTest:y.activeTest,reticleConfig:Kt}),v.style.display="block",kt(v,a,e.clientX,e.clientY);else v.style.display="none";Ne?.(r,e)}function ln(e){const t=a.getBoundingClientRect(),n=e.clientX-t.left,o=e.clientY-t.top,i=e.ctrlKey||e.metaKey;if(v&&(n<0||o<0||n>t.width||o>t.height)&&(v.style.display="none"),be){be=!1,R={x:n,y:o};const s=n-O.x,r=o-O.y,p=X();if(Y==="zoom"){if(s*s+r*r<25)Ce(n,o,2);else if(p){const c=Math.min(O.x,R.x),h=Math.max(O.x,R.x),Q=Math.min(O.y,R.y),Me=Math.max(O.y,R.y),V=h-c,de=Me-Q;if(V>4&&de>4){const L=a.clientWidth,j=a.clientHeight,Ie=L/V,qe=j/de,Je=Math.min(Ie,qe),U=tn(p.ppm*Je),Fe=U/p.ppm,et=(c+h)/2,_=(Q+Me)/2;P={originX:L/2-(et-p.originX)*Fe,originY:j/2-(_-p.originY)*Fe,ppm:U,snapDist:p.snapDist/Fe}}}w(),a.style.cursor="crosshair";return}if(s*s+r*r<25)Ct(n,o,i,e);else if(p){const c=(Math.min(O.x,R.x)-p.originX)/p.ppm,h=(Math.max(O.x,R.x)-p.originX)/p.ppm,Q=(p.originY-Math.max(O.y,R.y))/p.ppm,Me=(p.originY-Math.min(O.y,R.y))/p.ppm,V=y.hoverPoints,de=[];for(let L=0;L<V.length;L++)if(V[L].x>=c&&V[L].x<=h&&V[L].y>=Q&&V[L].y<=Me){const j=y.dies[L];j&&de.push(j)}if(i)for(const L of de){const j=Ee(L);E.has(j)?E.delete(j):E.add(j)}else E=new Set(de.map(L=>Ee(L)));ce?.(Mt())}w(),a.style.cursor="crosshair";return}if(ye){ye=!1,Ct(n,o,i,e);return}if(Le){Le=!1,a.style.cursor="grab";return}if(!pe)return;if(pe=!1,xe){xe=!1,a.style.cursor=Pe||Y==="pan"?"grab":"crosshair";return}a.style.cursor=Y==="pan"?"grab":"crosshair";const d=n-ve.x,u=o-ve.y;d*d+u*u<25&&Ct(n,o,i,e)}function Ct(e,t,n,o){for(const s of ft)if(e>=s.x&&e<s.x+s.w&&t>=s.y&&t<s.y+s.h){if(typeof s.bin=="number"){const r=l.highlightBin===s.bin?void 0:s.bin;x({highlightBin:r})}else{const r=l.highlightMetadataValue===s.bin?void 0:s.bin;x({highlightMetadataValue:r})}return}if(ke&&on(e,t,ke))return;const i=X();if(!i)return;const u=cn((e-i.originX)/i.ppm,(i.originY-t)/i.ppm,i.snapDist)?.die??null;if(u){Ln?.(u,o);const s=Ee(u);n?E.has(s)?E.delete(s):E.add(s):E=new Set([s]),ce?.(Mt()),w()}else n||(E=new Set,ce?.([]),w())}function Mt(){const e=[],t=y.hoverPoints;for(let n=0;n<t.length;n++){const o=y.dies[n];o&&E.has(Ee(o))&&e.push(o)}return e}function rn(){v&&ot(g),Ne?.(null,new MouseEvent("mouseleave")),a.style.cursor=Y==="pan"?"grab":"crosshair"}function dn(){pe=!1,xe=!1,be=!1,ye=!1,Le=!1,v&&ot(g),Ne?.(null,new MouseEvent("mouseleave")),a.style.cursor=Y==="pan"?"grab":"crosshair",w()}function cn(e,t,n){const o=y.hoverPoints,i=y.rectangles;for(let r=0;r<i.length;r++){const p=i[r];if(Math.abs(e-p.x)<=p.width/2&&Math.abs(t-p.y)<=p.height/2){const c=y.dies[r];return c?{die:c,index:r}:null}}let d=null,u=-1,s=n*n;for(let r=0;r<o.length;r++){const p=o[r].x-e,c=o[r].y-t,h=p*p+c*c;h<s&&(s=h,d=y.dies[r]??null,u=r)}return d?{die:d,index:u}:null}const pn=new K.ResizeObserver(()=>{W=null,P=null,w()});pn.observe(a),C.height==null&&kn<=0&&Lt(()=>Lt(()=>{N.clientHeight>0||console.warn("[wafermap] The map container has zero height, so the map cannot render. renderWaferMap fills its container \u2014 give the container a resolved height: an explicit CSS `height` (e.g. 600px), a height-resolved flex/grid parent, or `position:absolute; inset:0`. Alternatively pass `{ height: 600 }` in the render options and the library will size it for you.")}));let Re=K.matchMedia(`(resolution: ${K.devicePixelRatio}dppx)`);const Qe=()=>{Re.removeEventListener("change",Qe),Re=K.matchMedia(`(resolution: ${K.devicePixelRatio}dppx)`),Re.addEventListener("change",Qe),w()};Re.addEventListener("change",Qe);const fn=K.matchMedia("(prefers-color-scheme: dark)"),un=()=>w();fn.addEventListener("change",un);const gn=()=>{v&&ot(g)};K.addEventListener("blur",gn);function hn(e){if(e.key==="Escape"&&E.size>0&&(E=new Set,ce?.([]),w()),(e.key==="e"||e.key==="E")&&Rt!=="view-only"&&Wt&&!me&&(e.stopPropagation(),(Ft??Jt)()),(e.ctrlKey||e.metaKey)&&(e.key==="="||e.key==="+"?(e.preventDefault(),Ce(a.clientWidth/2,a.clientHeight/2,1.2)):e.key==="-"?(e.preventDefault(),Ce(a.clientWidth/2,a.clientHeight/2,1/1.2)):e.key==="0"&&(e.preventDefault(),Ue())),!e.ctrlKey&&!e.metaKey&&!e.shiftKey){const n=X();if(n){const o=P?.snapDist??W?.snapDist??1;let i=0,d=0;e.key==="ArrowLeft"&&(i=20,e.preventDefault()),e.key==="ArrowRight"&&(i=-20,e.preventDefault()),e.key==="ArrowUp"&&(d=20,e.preventDefault()),e.key==="ArrowDown"&&(d=-20,e.preventDefault()),(i||d)&&(P={originX:n.originX+i,originY:n.originY+d,ppm:n.ppm,snapDist:o},w())}e.key===" "&&!Pe&&(e.preventDefault(),Pe=!0,Y!=="pan"&&(a.style.cursor="grab"))}}function mn(e){e.key===" "&&(Pe=!1,xe&&(pe=!1,xe=!1),a.style.cursor=Y==="pan"?"grab":"crosshair")}a.style.cursor="grab",a.setAttribute("tabindex","0"),a.addEventListener("wheel",nn,{passive:!1}),a.addEventListener("pointerdown",an),a.addEventListener("pointermove",sn),a.addEventListener("pointerup",ln),a.addEventListener("pointerleave",rn),a.addEventListener("pointercancel",dn);const yn=()=>Ue();a.addEventListener("dblclick",yn),a.addEventListener("keydown",hn),a.addEventListener("keyup",mn);const bn=e=>{e.stopPropagation()};a.addEventListener("click",bn),a.clientWidth,w(),Xe?.();function Ue(){W=null,P=null,w()}function vn(){import("./userGuideHtml.js").then(e=>vi({buildWaferMap:ci,renderWaferMap,renderWaferGallery:void 0,analyzeWaferMap:fi},e.USER_GUIDE_HTML,Hn,N))}return{setDies(e){se=e,Oe(),w(),z&&ue()},setResult(e){lt=e,J=e.wafer,se=e.dies,$=e.hbinDefs,G=e.sbinDefs,ee=e.testDefs,le=e.metadataFields,Nt=e.reticles?.length?e.reticles:void 0,Kt=e.reticleConfig,Vt=e.view?.dataAxisFlip,rt=e.isLotStack,dt=e.aggrMethod,ct=e.lotSize,Nn(),Oe(),w(),z&&ue()},setOptions(e){en(e)},getOptions(){return{...l}},resetZoom:Ue,setSelection(e){E=new Set(e.map(t=>Ee(t))),w()},clearSelection(){E=new Set,ce?.([]),w()},setFallbackFormat(e){Be=e,Oe(),w()},setStatsSummary(e){if(oe=e,Xt(),Ut(),z)ue();else if(I)je();else if(e&&!De?.placement){const t=De?.defaultOpen??!he;I=Pt("right",g),I.style.display=t?"block":"none";const n=M.parentElement,o=M.nextSibling;te=Ot(M,I,"right"),n?.insertBefore(te,o),je()}Ze()},setSummaryVisible(e){F&&(F.style.display=e?"flex":"none")},setViewControlsVisible(e){B&&(B.style.display=e?"flex":"none")},setExpandVisible(e){Z&&(Z.style.display=e?"flex":"none")},setHelpButtonVisible(e){$e&&($e.style.display=e?"flex":"none")},setMetadataBadgeVisible(e){jt=!e,T&&(T.el.style.display=e&&!me?"":"none")},openUserGuide:vn,setInsightsOpen(e){pt(e),A&&(e?(A.dataset.active="1",A.style.background=S.bgActive,A.style.color=S.iconActive):(delete A.dataset.active,A.style.background="transparent",A.style.color=S.icon))},closeSummaryPanel(){const e=z??I;!e||e.style.display==="none"||(e.style.display="none",F&&(delete F.dataset.active,F.style.background="transparent",F.style.color=S.icon))},setTooltipParent(e){v&&hi(e)},getActiveLegend(){const e=l.plotMode;if(e==="metadata"){const i=l.activeMetadataKey;if(!i)return null;const d=le?.find(s=>s.key===i),u=[...new Set(se.map(s=>s.metadata?.[i]).filter(s=>s!=null&&(typeof s=="string"||typeof s=="number"||typeof s=="boolean")).map(String))].sort();return u.length?u.map((s,r)=>{const p=d?.values?.find(h=>h.value===s),c=p?.color??Ai(r);return{bin:s,name:p?.label??s,color:c}}):null}if(e!=="hardBin"&&e!=="softBin")return null;const t=e==="hardBin",n=t?$:G,o=[...new Set(se.map(i=>t?i.hbin:i.sbin).filter(i=>i!==void 0))].sort((i,d)=>i-d);return o.length?o.map(i=>{const d=n?.find(s=>s.bin===i),u=d?.color??(t?Wi(i):Hi(i));return{bin:i,name:d?.name??`Bin ${i}`,color:u}}):null},destroy(){Se?.close(),we&&clearTimeout(we),qt?.()?.remove(),ht&&g.removeEventListener("click",ht,!0),a.removeEventListener("wheel",nn),a.removeEventListener("pointerdown",an),a.removeEventListener("pointermove",sn),a.removeEventListener("pointerup",ln),a.removeEventListener("pointerleave",rn),a.removeEventListener("pointercancel",dn),a.removeEventListener("dblclick",yn),a.removeEventListener("keydown",hn),a.removeEventListener("keyup",mn),a.removeEventListener("click",bn),pn.disconnect(),Re.removeEventListener("change",Qe),fn.removeEventListener("change",un),K.removeEventListener("blur",gn),Xn(),v&&ot(g),D?.remove(),fe&&(fe.parentElement?.insertBefore(M,fe),fe.remove()),te&&(te.parentElement?.insertBefore(M,te),te.remove()),M.remove(),a.style.cursor="",re?.destroy(),T?.destroy(),q.remove()}}}
1
+ import{buildView as vi,buildHoverText as xi,findTestDef as ct,resolveTestNumber as Nt}from"../renderer/buildView.js";import{toCanvas as Ci,BIN_LEGEND_W as Fn,BIN_LEGEND_W_COMPACT as wi,BIN_LEGEND_ADAPT_COMPACT as Si,BIN_LEGEND_ADAPT_FLOATING as Mi}from"./toCanvas.js";import{buildWaferMap as Ei,getTestPassStatus as Di,isParametricTest as jt}from"../renderer/buildWaferMap.js";import{analyzeWaferMap as Bi}from"../stats/analyzeWaferMap.js";import{SHADOW as ki,LEADING as An,wireControlHover as zn,SPACE as W,EDGE_GUTTER as Li,RADIUS as Yn,FONT as ft,CLR as x,applyOverlayZ as Ti,getTooltip as Vn,hideTooltip as pt,reparentTooltip as Oi,positionTooltip as Kt,createToolbarHelpers as Pi,buildModeMenuEl as Ri,openReparentedModal as Ii,openUserGuideWindow as Wi,makePaletteBtn as Hi,makeLogScaleBtn as Fi,makeLegendStyleBtn as Ai,makeOverlaysBtn as zi,makeOrientationBtn as Yi,menuLayerFor as Xn,saveImageBlob as Vi,markMenuTrigger as _e,wireMenuA11y as Nn,wireExpandToggle as Xi,nextFrame as _t,requestedPassFailDisplay as Ni,overlayMenuRows as ji,anyOverlayActive as Ki,logWmapVersionOnce as _i,buildDataModeEntries as $i,metadataKeyHasData as Gi,metadataModeEntry as Ui}from"./toolbar.js";import{createSummaryPanelEl as $t,wrapWithSummaryPanel as Gt,renderWaferSummaryContent as Zi}from"./summaryPanel.js";import{collectWarnings as Qi,buildWarningsMenuEl as qi,severityOf as Ji}from"./warnings.js";import{ICONS as jn}from"./icons.js";import{metadataValueColor as eo,NO_DATA_FILL as to}from"../renderer/colorMap.js";import{createIdentityHeader as no,collapsedLabel as Ut}from"./identityHeader.js";import{getDieKey as Be,hasPosition as Kn,isPositionedDie as io}from"../core/dies.js";import{buildDieListSection as oo}from"./dieList.js";import{buildMaplessSummary as so}from"./maplessSummary.js";import{resolveBinColors as lo,binColorWarning as ao}from"../renderer/binColors.js";const _n=new Set(["binColorScheme","valueColorScheme","useDefinedBinColors","rotation","flipX","flipY","showDieLabels","showPartialDies","showRingBoundaries","showQuadrantBoundaries","showReticle","showXYIndicator","ringCount","legendPosition","logScale","colorbarRangeMode","markFailingDies"]);export const COLOR_KEYS=["binColorScheme","valueColorScheme","useDefinedBinColors","binColors"];export function classifyChanged(K){const y=K.some(g=>_n.has(g)),C=K.some(g=>!_n.has(g));return y&&C?"mixed":y?"preference":"state"}export function renderWaferMap(K,y,C={}){_i();const g=K.ownerDocument,H=g.defaultView??window;H.getComputedStyle(K).position==="static"&&(K.style.position="relative");const $n=K.clientHeight;C.height!=null&&(K.style.height=typeof C.height=="number"?`${C.height}px`:C.height);const ut=y.dataCoverage?y.dataCoverage.unpositionedDies:y.dies.filter(e=>!Kn(e)).length,$e=(y.dataCoverage?y.dataCoverage.totalDies:y.dies.length-ut)===0&&ut>0,ke=g.createElement("div");Object.assign(ke.style,{display:"flex",flexDirection:"column",width:"100%",height:"100%"}),K.appendChild(ke);const le=g.createElement("div");Object.assign(le.style,{position:"relative",width:"100%",flex:"1 1 auto",minHeight:"0"}),C.maxSize!=null&&(le.style.maxWidth=`${C.maxSize}px`,le.style.maxHeight=`${C.maxSize}px`);const ge=C.chromeInset??Li,_=g.createElement("div");Object.assign(_.style,{display:"flex",alignItems:"stretch",gap:W.sm,background:x.canvasBg,paddingBottom:W.lg,paddingTop:ge,paddingLeft:ge,paddingRight:ge,flexShrink:"0",minWidth:"0"}),ke.appendChild(_),ke.appendChild(le);function ve(){const e=[..._.children].some(t=>t.style.display!=="none");_.style.display=e?"flex":"none"}const S=g.createElement("div");Object.assign(S.style,{position:"relative",width:"100%",height:"100%"});const a=g.createElement("canvas");Object.assign(a.style,{width:"100%",height:"100%",display:"block"}),S.appendChild(a),le.appendChild(S);let Ge=null;if(ut>0){let n=function(){if(t){const d=oo(e,y.testDefs,{...C.dieList,onSaveText:C.onSaveText,waferMetadata:$.metadata,metadataFields:ce,ownerDocument:g});if(d)return d}const o=ct(y.testDefs,p.activeTest??0),s=jt(o)?p.logScale??o?.logScale??!1:!1;return so(e,y.testDefs,{plotMode:p.plotMode??"hardBin",activeTest:p.activeTest,hbinDefs:G,sbinDefs:U,binColors:m.binColors,valueColorScheme:p.valueColorScheme,logScale:s,colorbarRangeMode:p.colorbarRangeMode,valueRangeDies:y.dies})},i=function(o){const s=g.createElement("button");return s.type="button",zn(s),Object.assign(s.style,{fontSize:ft.body,padding:`${W.xxs} ${W.sm}`,borderRadius:Yn.control,flexShrink:"0",border:`1px solid ${x.menuBorder}`,background:x.menuBg,color:x.text,cursor:"pointer"}),s.textContent=t?"View chart":"View die list",s.addEventListener("click",d=>{d.stopPropagation(),t=!t,s.textContent=t?"View chart":"View die list",o()}),s};const e=y.dies.filter(o=>!Kn(o));let t=!1;if($e){const o=g.createElement("div");Object.assign(o.style,{position:"absolute",inset:"0",background:x.panelBg,padding:W.xl,boxSizing:"border-box",overflow:"hidden",display:"flex",flexDirection:"column",gap:W.md,zIndex:"1"});const s=g.createElement("div");Object.assign(s.style,{display:"flex",justifyContent:"space-between",alignItems:"flex-start",gap:W.md});const d=g.createElement("span");Object.assign(d.style,{fontSize:ft.body,color:x.label,lineHeight:An.base}),d.textContent="No die position data for this wafer.",s.appendChild(d);const r=g.createElement("div");Object.assign(r.style,{flex:"1",minHeight:"0",overflow:"auto",display:"flex",flexDirection:"column"}),s.appendChild(i(()=>r.replaceChildren(n()))),o.appendChild(s),o.appendChild(r),S.appendChild(o),Ge=()=>{t||r.replaceChildren(n())}}else{const o=g.createElement("div");Object.assign(o.style,{position:"absolute",left:"0",right:"0",bottom:"0",zIndex:"3",background:x.panelBg,borderTop:`1px solid ${x.menuBorder}`,fontSize:ft.body,color:x.text,cursor:"pointer",padding:`${W.xs} ${W.md}`,display:"flex",alignItems:"center",justifyContent:"space-between",gap:W.md}),zn(o,"bare");const s=g.createElement("span");s.textContent=`+${e.length} ${e.length===1?"die":"dies"} without position data`,o.appendChild(s);const d=g.createElement("span");d.textContent="\u25BE",Object.assign(d.style,{fontSize:ft.body,lineHeight:An.none,color:x.label,flexShrink:"0"}),o.appendChild(d),o.setAttribute("aria-expanded","false"),o.setAttribute("aria-label",`${e.length} dies without position data. Click to view.`);let r=null,l=null;Xi(o,c=>{if(d.textContent=c?"\u25B4":"\u25BE",o.setAttribute("aria-expanded",String(c)),c){r=g.createElement("div");const f=o.getBoundingClientRect().height||28;Object.assign(r.style,{position:"absolute",left:"0",right:"0",bottom:`${f}px`,top:"20%",zIndex:"2",background:x.panelBg,borderTop:`1px solid ${x.menuBorder}`,padding:W.md,boxSizing:"border-box",display:"flex",flexDirection:"column",gap:W.md}),r.addEventListener("click",Q=>Q.stopPropagation());const h=g.createElement("div");Object.assign(h.style,{display:"flex",justifyContent:"flex-end"}),h.appendChild(i(()=>{l&&l.replaceChildren(n())})),r.appendChild(h),l=g.createElement("div"),Object.assign(l.style,{flex:"1",minHeight:"0",overflow:"auto",display:"flex",flexDirection:"column"}),l.appendChild(n()),r.appendChild(l),S.appendChild(r)}else r&&(r.remove(),r=null,l=null)}),S.appendChild(o),Ge=()=>{l&&!t&&l.replaceChildren(n())}}}const{onHover:Ue,onClick:Gn,onSelect:he,onViewOptionsChange:Un,showTooltip:Zn=!0,showToolbar:Le=!0,showIdentity:Zt=!0,toolbarControls:gt="full",showPlotModeSelector:Qn=!0,minZoom:qn=.4,maxZoom:Jn=20,summaryPanel:Te,renderTooltip:Qt,passBins:Ze=[1],showHelpButton:qt=!1,userGuideExtension:ei,onExpand:Jt,showExpandButton:en=!0,zIndex:ti,insights:ht,warnings:tn,viewOptions:ni={},...ae}=C,Oe=ht?.enabled??!1,nn=tn?.display??!0,ii=Ti(ti);let Pe=ae.fallbackFormat,re=C.statsSummary,de=[],on=!1,Qe=null,sn=!1;function mt(){const e=Qi({result:y,statsSummary:re,extra:Qe?[Qe]:[]}),t=e.length!==de.length||e.some((n,i)=>n.code!==de[i]?.code||n.message!==de[i]?.message);de=e,(t||!on)&&(on=!0,tn?.onWarning?.(e))}mt();let yt=y,$=y.wafer,te=y.dies,E=new Set,G=y.hbinDefs,U=y.sbinDefs,ne=y.testDefs,ce=y.metadataFields,ln=y.reticles?.length?y.reticles:void 0,an=y.reticleConfig,rn=y.view?.dataAxisFlip,bt=y.isLotStack??!1,vt=y.aggrMethod,xt=y.lotSize;const oi=Le&&gt!=="view-only"&&Oe;let Ct=!1,Z=null,z=null,J=null;Zt&&(Z=no(Ut($.metadata??{},Re())??"",$.metadata,{lotStack:Re(),ownerDocument:g}),S.appendChild(Z.metaPanel),Z.isEmpty()||(z=dn(Z.wrap),_.insertBefore(z,_.firstChild),ve()));function dn(e){const t=g.createElement("div");return Object.assign(t.style,{display:"flex",alignItems:"center",padding:`${W.sm} ${W.lg}`,flex:"1 1 auto",minWidth:"0",overflow:"hidden",gap:W.sm}),Object.assign(e.style,{flex:"1",minWidth:"0"}),t.appendChild(e),t}function Re(){return bt?{lotSize:xt??1,aggrMethod:vt}:void 0}function si(){if(!Z)return;Z.update(Ut($.metadata??{},Re())??"",$.metadata,Re());const e=z?.isConnected??!1,t=!Z.isEmpty();!t&&e?(z.remove(),z=null):t&&!e&&(z=dn(Z.wrap),z.style.display=Ct?"none":"",_.insertBefore(z,_.firstChild),ve())}const li=[...G??[],...U??[]].some(e=>e.color);let p={plotMode:"hardBin",showDieLabels:!1,showPartialDies:!0,showRingBoundaries:!1,showQuadrantBoundaries:!1,showReticle:!1,showXYIndicator:!1,ringCount:4,rotation:0,flipX:!1,flipY:!1,legendPosition:ae.legendPosition??"default",...ni},Y=null,ie=!1,cn=null;function ai(){return Oe?Y?Promise.resolve(Y):(cn??=import("./insightsTab.js").then(({createInsightsTab:e})=>(Y=e({getItems:()=>[{wafer:$,dies:te,hbinDefs:G,sbinDefs:U,testDefs:ne,label:String($.metadata?.waferId??""),statsSummary:re}],getBinColors:()=>m.binColors,passBins:Ze,getRingCount:()=>p.ringCount??4,onSaveImage:C.onSaveImage,onSaveText:C.onSaveText,defaultView:ht?.defaultView,showMetadataStrip:!1,contentInset:ge,backTab:oi?void 0:{label:"Map",onBack:()=>Ie(!1)},onOpenGuide:qt&&!Le?()=>At():void 0,focusTest:t=>{D({plotMode:"value",activeTest:t,highlightBin:void 0}),Ie(!1)},ownerDocument:g}),Object.assign(Y.el.style,{position:"absolute",inset:"0",background:x.canvasBg,overflowY:"auto",paddingTop:"0"}),Y.el.style.display="none",le.appendChild(Y.el),Y)),cn):Promise.resolve(null)}let fn=0;const wt=["wheel","touchstart","pointerdown","keydown"];function ri(e,t){if(t<=0)return;let n=!1;const i=[],o=()=>{n=!0;for(const d of i)H.clearTimeout(d);i.length=0;for(const d of wt)e.removeEventListener(d,o)},s=()=>n||!ie?!0:(e.scrollTop=t,e.scrollTop>=t);if(!s()){for(const d of wt)e.addEventListener(d,o,{passive:!0,once:!0});for(const d of[0,50,150,300,600])i.push(H.setTimeout(()=>{s()},d));i.push(H.setTimeout(()=>{for(const d of wt)e.removeEventListener(d,o)},700))}}function Ie(e){if(!Oe)return;const t=ie;ie=e,Y&&!e&&t&&(fn=Y.el.scrollTop),Y&&(Y.el.style.display=e?"flex":"none"),F&&(F.style.display=e||$e?"none":"flex"),T&&(T.style.display=e?"none":"flex"),J&&(J.style.display=ye?"none":"flex"),oe&&(oe.style.display="flex"),z&&(z.style.display=Ct?"none":""),ve(),Z&&e&&Z.collapse(),it(),A&&(A.innerHTML=e?jn.wafer:jn.analysis,A.ariaLabel=e?"Back to wafer view":"Insights"),e&&ai().then(n=>{!n||!ie||(n.el.style.display="flex",n.render(),ri(n.el,fn))})}Oe&&ht?.defaultOpen&&queueMicrotask(()=>Ie(!0));let m,pn,V=null,k=null,St=[],Mt;function un(e){Mt!==e&&(Mt=e,v())}let We=null,Et=ae.legendOffset??{x:0,y:0},He=!1,xe=!1,Fe={x:0,y:0},Dt={x:0,y:0},me=!1,Ce=!1,X="pan",we={x:0,y:0},qe={x:0,y:0},O={x:0,y:0},P={x:0,y:0},Ae=!1,Se=!1;function ze(){const e=p;m=vi($,te.filter(io),{plotMode:e.plotMode,binColorScheme:e.binColorScheme,valueColorScheme:e.valueColorScheme,useDefinedBinColors:e.useDefinedBinColors,binColors:e.binColors??lo(te,{passBins:Ze,binColorScheme:e.binColorScheme,hbinDefs:G,sbinDefs:U,useDefinedBinColors:e.useDefinedBinColors}),passBins:Ze,showDieLabels:e.showDieLabels,showPartialDies:e.showPartialDies,showRingBoundaries:e.showRingBoundaries,showQuadrantBoundaries:e.showQuadrantBoundaries,showReticle:e.showReticle,showXYIndicator:e.showXYIndicator,reticles:ln,ringCount:e.ringCount,highlightBin:e.highlightBin,highlightMetadataValue:e.highlightMetadataValue,activeTest:e.activeTest,activeMetadataKey:e.activeMetadataKey,testDefs:ne,valueRange:e.valueRange,metadataValueOrder:e.metadataValueOrder,logScale:e.logScale,isLotStack:bt,aggregationMethod:vt??e.aggregationMethod,lotSize:xt??e.lotSize,dataAxisFlip:rn,colorbarRangeMode:e.colorbarRangeMode,passFailDisplay:e.passFailDisplay,fallbackFormat:Pe,interactiveTransform:{rotation:e.rotation??0,flipX:e.flipX??!1,flipY:e.flipY??!1}},{hbinDefs:G,sbinDefs:U,metadataFields:ce}),pn=new Map(m.dies.map((n,i)=>[Be(n),i]));const t=e.binColors?null:ao(m.binColors,m.plotMode);t?.message!==Qe?.message&&(Qe=t,mt(),sn&&Bt())}ze();let N=null,Me=null,Je=null,gn=C.findingsNotice,di={},R=null,fe=null;function ci(e){const{kind:t,index:n}=e.variable;D(t==="test"?{plotMode:"value",activeTest:n??0,highlightBin:void 0}:t==="softBin"?{plotMode:"softBin",highlightBin:void 0}:{plotMode:"hardBin",highlightBin:void 0});const i=e.highlight;i.kind==="bin"?(Rt(i.dieKeys),D({highlightBin:i.bin})):(i.kind==="region"||i.kind==="dies")&&Rt(i.dieKeys)}function hn(e){Zi(e,{wafer:$,dies:te,yieldSummary:yt.yield,dataCoverage:yt.dataCoverage,hbinDefs:G,sbinDefs:U,testDefs:ne,statsSummary:re,warnings:nn?de:[],passBins:Ze,ringCount:p.ringCount??4,binColors:m.binColors,plotMode:p.plotMode??"hardBin",metadataShownElsewhere:Zt,fallbackFormat:Pe,activeFindingId:Je,findingsFilter:di,findingsNotice:gn,onFindingsFilterChange:pe,onSaveText:C.onSaveText,metadataFields:ce,dieListOptions:C.dieList,onFindingClick:(t,n)=>{Je===t.id?(Je=null,Rt([]),D({highlightBin:void 0})):(Je=t.id,ci(t)),pe()}})}function pe(){N&&hn(N)}function Ye(){R&&hn(R)}if(Te?.placement){const e=Te.placement;N=$t(e,ge,g);const t=S.parentElement,n=S.nextSibling;Me=Gt(S,N,e),t?.insertBefore(Me,n),pe()}else if(re){const e=Te?.defaultOpen??!Le;R=$t("right",ge,g),R.style.display=e?"block":"none";const t=S.parentElement,n=S.nextSibling;fe=Gt(S,R,"right"),t?.insertBefore(fe,n),Ye()}const w=Zn?Vn(g):null;let oe=null,L=null,F=null,T=null,et=null,I=null,tt=null,A=null,se=null,nt=null;sn=!0;function Bt(){if(!se||!nt)return;const e=de.length,t=e>0;if(se.style.display=t?"flex":"none",nt.style.display=t?"":"none",!t)return;const n=Ji(de[0]),i=`${e} data ${e===1?"warning":"warnings"}`;se.style.color=n==="error"?x.errText:x.warnText,se.ariaLabel=n==="error"?`${i} \u2014 the map may be positionally wrong`:i}let kt=null,mn=null,Lt=null,Tt=null,Ot=null,Pt=null;function Rt(e){E=new Set(e??[]),he&&he(Ft()),v()}function it(){if(!I)return;const e=!!(N??R);I.style.display=re&&e&&!ie?"flex":"none";const t=N??R,n=t?t.style.display!=="none":!1;re?.hasNotableFindings&&!n?I.style.color=x.findingIndicator:I.dataset.active||(I.style.color=x.icon)}if(Le){let f=function(B){X=B,i(Q,B==="zoom"),i(M,B==="pan"),et&&i(et,B==="select"),a.style.cursor=B==="pan"?"grab":"crosshair"};oe=g.createElement("div"),oe.dataset.wmapToolbar="single",Object.assign(oe.style,{marginLeft:"auto",flexShrink:"0",display:"flex",flexDirection:"row",alignItems:"center",flexWrap:"wrap-reverse",justifyContent:"flex-end",maxWidth:"100%",background:x.menuBg,border:`1px solid ${x.menuBorder}`,borderRadius:Yn.control,boxShadow:ki.panel,pointerEvents:"auto"});const e=Vn(g),t=Pi(e),{makeBtn:n,setActive:i,makeSep:o,makeMenuRow:s,makeMenuSection:d,closeOpenMenu:r,getOpenMenu:l,setOpenMenu:c}=t;kt=r,mn=l,g.addEventListener("click",r,!0),F=g.createElement("div"),Object.assign(F.style,{display:$e?"none":"flex",alignItems:"center",gap:"0",flexWrap:"wrap",justifyContent:"flex-end"}),oe.appendChild(F);const h=n("download","Download PNG",ui);F.appendChild(h),F.appendChild(o());const Q=n("zoomMode","Zoom (drag to zoom region)",()=>f("zoom")),De=n("zoomIn","Zoom in",()=>Ee(a.clientWidth/2,a.clientHeight/2,1.2)),ee=n("zoomOut","Zoom out",()=>Ee(a.clientWidth/2,a.clientHeight/2,1/1.2)),ue=n("reset","Reset zoom (double-click)",()=>st());F.appendChild(Q),F.appendChild(De),F.appendChild(ee),F.appendChild(ue),F.appendChild(o());const M=n("pan","Pan (drag to move)",()=>f("pan"));if(F.appendChild(M),et=n("boxSelect","Select (drag to select dies)",()=>f("select")),F.appendChild(et),i(M,!0),gt!=="view-only"){let B=function(u){return u.plotMode!==(p.plotMode??"hardBin")?!1:u.plotMode==="value"?(p.activeTest??0)===(u.activeTest??0):u.plotMode==="metadata"?p.activeMetadataKey===u.activeMetadataKey:!0},zt=function(u,b){u.activeTest!==void 0?D({plotMode:u.plotMode,activeTest:u.activeTest,activeMetadataKey:void 0,logScale:u.logScale}):u.activeMetadataKey!==void 0?D({plotMode:u.plotMode,activeTest:void 0,activeMetadataKey:u.activeMetadataKey,passFailDisplay:"off"}):D({plotMode:u.plotMode,activeTest:void 0,activeMetadataKey:void 0,passFailDisplay:"off"}),b.remove(),c(null),_e(q,!1)};L=g.createElement("div"),Object.assign(L.style,{display:"flex",alignItems:"center",gap:"0",flexWrap:"wrap",justifyContent:"flex-end"}),oe.appendChild(L),T=g.createElement("div"),Object.assign(T.style,{display:"flex",alignItems:"center",gap:"0"}),L.appendChild(T),T.appendChild(o());const lt=()=>{const u=l();u&&(u.remove(),c(null)),_e(q,!1)},q=n("mode","Plot mode",()=>{if(l()){lt();return}const u=te,b=m.testDefs,{testEntries:dt,binEntries:be,stackedEntries:Ne}=$i(u,b,{includeStacked:m.isLotStack}),Xt=(ce??[]).filter(Ke=>Gi(u,Ke.key)).map(Ui),je=Ri(q.getBoundingClientRect(),dt,be,Ne,B,zt,{makeMenuRow:s,makeMenuSection:d},p.plotMode??"hardBin",q.ownerDocument.defaultView??window,Xt);Xn(q).appendChild(je),c(je),_e(q,!0),Nn(je,q,lt)});_e(q,!1);const{btn:at,sync:Xe}=Hi(t,()=>({...p,plotMode:p.plotMode??"hardBin"}),()=>li,D);Tt=Xe,Tt();const Yt=zi(t,()=>{const u=!!m.hasReticle,b=(p.plotMode??"hardBin")==="value",{testNumber:dt}=Nt(p.activeTest??0,m.testDefs),be=ct(m.testDefs,dt),Ne=b&&be!==void 0&&!jt(be),Xt=b&&!Ne&&(be?.limitLow!==void 0||be?.limitHigh!==void 0),je=b&&!Ne&&m.dies.some(Ke=>Di(Ke,dt,be)!==void 0);return ji(p,u,{functionalActive:Ne,hasLimits:Xt,hasRecorded:je,binMode:(p.plotMode??"hardBin")==="hardBin"||(p.plotMode??"hardBin")==="softBin"},Ke=>D(Ke))},()=>Ki(p)),{btn:Vt,sync:gi}=Ai(t,()=>p,u=>D({legendPosition:u}));Lt=gi,Lt();const Hn=()=>{const{testNumber:u}=Nt(p.activeTest??0,ne),b=ct(ne,u);return b!==void 0&&!jt(b)},{btn:hi,sync:mi}=Fi(t,()=>({...p,functionalActive:Hn()}),u=>D(u));Ot=mi,Ot();const yi=()=>{const{testNumber:u}=Nt(p.activeTest??0,ne),b=ct(ne,u);return b!==void 0&&(b.limitLow!==void 0||b.limitHigh!==void 0)},rt=n("specRange","Colorbar range: spec limits",()=>{const u=p.colorbarRangeMode==="data"?"spec":"data";D({colorbarRangeMode:u})});Pt=()=>{const u=p.plotMode==="value"&&yi()&&Ni(p)==="off"&&!Hn();rt.style.display=u?"":"none";const b=(p.colorbarRangeMode??"spec")==="spec";i(rt,b),rt.ariaLabel=b?"Colorbar range: spec limits (click for data range)":"Colorbar range: data range (click for spec limits)"},Pt();const bi=Yi(t,()=>p,u=>D(u));Qn&&T.appendChild(q),T.appendChild(at),T.appendChild(hi),T.appendChild(rt),$e||(T.appendChild(o()),T.appendChild(Yt),T.appendChild(o()),T.appendChild(Vt),T.appendChild(o()),T.appendChild(bi)),re&&(I=n("findings","Summary panel",()=>{const u=N??R;if(!u)return;const b=u.style.display!=="none";u.style.display=b?"none":"block",i(I,!b),it()}),L.appendChild(o()),L.appendChild(I),R?.style.display!=="none"&&i(I,!0),it()),nn&&(nt=o(),se=n("warning","Data warnings",()=>{const u=l();if(r(new MouseEvent("click")),u)return;const b=qi(se.getBoundingClientRect(),de,H);Xn(le).appendChild(b),c(b),Nn(b,se,()=>r(new MouseEvent("click")))}),_e(se,!1),L.appendChild(nt),L.appendChild(se),Bt()),Oe&&(L.appendChild(o()),A=n("analysis","Insights",()=>{Ie(!ie),i(A,ie)}),A.dataset.wmapInsightsBtn="1",L.appendChild(A)),en&&(L.appendChild(o()),J=n("expand","Expand (E)",Jt??yn),L.appendChild(J)),qt&&(L.appendChild(o()),tt=n("help","User guide",()=>At()),L.appendChild(tt))}_.appendChild(oe),ve()}let ye=null;function yn(){ye&&(ye.close(),ye=null);const e=le;e.style.flex="1",e.style.minWidth="0",e.style.minHeight="0";const t=e.style.maxWidth,n=e.style.maxHeight;e.style.maxWidth="none",e.style.maxHeight="none";const i=Ut($.metadata??{},Re()),o=_.childElementCount>0?[_,e]:[e],s=Ii(o,{ownerDocument:g,boxSize:ie?{width:"min(96vw, 1600px)",height:"min(92vh, 1000px)"}:void 0,onClosed:()=>{ye=null,e.style.maxWidth=t,e.style.maxHeight=n,J&&(J.style.display="flex"),ie||a.focus({preventScroll:!0})}});s&&(s.box.setAttribute("aria-label",i??"Expanded wafer map"),o.length>1&&(s.contentWrap.style.flexDirection="column"),ye=s,J&&(J.style.display="none"))}function bn(e){const t=p.plotMode;p={...p,...e},e.plotMode!==void 0&&e.plotMode,Object.keys(e).every(s=>s==="legendPosition"||s==="showLegend"||s==="markFailingDies")||ze(),Lt?.(),Tt?.(),Ot?.(),Pt?.(),v();const i=e.plotMode!==void 0&&e.plotMode!==t,o=COLOR_KEYS.some(s=>s in e);(o||i)&&(pe(),Ye()),(o||i||e.activeTest!==void 0||e.logScale!==void 0||e.colorbarRangeMode!==void 0)&&Ge?.()}function D(e){bn(e);const t=Object.keys(e);Un?.(p,t,classifyChanged(t))}let It=!1;function Wt(){It||(It=!0,_t(()=>{It=!1,v()},H))}function v(){const e=k??void 0,t=m.dies[0],n=t?{x:t.width,y:t.height}:ae.diePitchMm,i=Math.floor(a.clientWidth||a.width),o=!!(G?.length||U?.length||m.dies.some(h=>h.hbin!=null||h.sbin!=null)),s=p.legendPosition??"default",d=p.showLegend??!0,r=s==="default"||s==="compact",l=(ae.colorbarWidth??16)+28,c=o&&r?s!=="default"?Math.max(Fn,l):i<Mi?0:i<Si?Math.max(wi,l):Math.max(Fn,l):void 0,f=Ci(a,m,{...ae,topClearance:0,minRightReserve:d?c:0,showColorbar:d&&(ae.showColorbar??!0),markFailingDies:p.markFailingDies??!1,legendPosition:s,legendOffset:Et,diePitchMm:n,fallbackFormat:Pe,showAxes:ae.showAxes??k!==null,viewport:e,activeBin:p.plotMode==="metadata"?p.highlightMetadataValue:p.highlightBin,hoverBin:Mt,hbinDefs:G,sbinDefs:U,metadataFields:ce});St=f.binLegendRows,We=f.legendBox??null,k===null&&(V=f.viewport),E.size>0&&fi(),Ce&&pi()}function fi(){const e=j();if(!e)return;const t=a.getContext("2d"),n=m.hoverPoints,i=m.rectangles[0],o=i?i.width/2*e.ppm:e.ppm*.5,s=i?i.height/2*e.ppm:e.ppm*.5,d=Math.max(1,Math.min(3,o*.08)),r=o-d,l=s-d,c=[];for(const f of E){const h=pn.get(f);h!==void 0&&c.push({sx:e.originX+n[h].x*e.ppm,sy:e.originY-n[h].y*e.ppm})}if(c.length){t.save(),t.setLineDash([]),t.fillStyle="rgba(0,0,0,0.12)",t.beginPath();for(const{sx:f,sy:h}of c)t.rect(f-r,h-l,r*2,l*2);t.fill(),t.strokeStyle="rgba(255,255,255,0.9)",t.lineWidth=3,t.beginPath();for(const{sx:f,sy:h}of c)t.rect(f-r,h-l,r*2,l*2);t.stroke(),t.strokeStyle="rgba(0,0,0,0.9)",t.lineWidth=1.5,t.beginPath();for(const{sx:f,sy:h}of c)t.rect(f-r,h-l,r*2,l*2);t.stroke(),t.restore()}}function pi(){const e=a.getContext("2d"),t=Math.min(O.x,P.x),n=Math.min(O.y,P.y),i=Math.abs(P.x-O.x),o=Math.abs(P.y-O.y);e.save(),e.fillStyle="rgba(0,0,0,0.08)",e.fillRect(t,n,i,o),e.setLineDash([5,3]),e.strokeStyle="rgba(255,255,255,0.9)",e.lineWidth=3,e.strokeRect(t,n,i,o),e.strokeStyle="rgba(0,0,0,0.9)",e.lineWidth=1.5,e.strokeRect(t,n,i,o),e.restore()}function ui(){a.toBlob(e=>{e&&Vi(e,C.downloadFilename??"wafermap",C.onSaveImage)})}function vn(e){return V?Math.max(V.ppm*qn,Math.min(V.ppm*Jn,e)):e}function Ee(e,t,n){const i=k??V;if(!i)return;const o=vn(i.ppm*n),s=o/i.ppm,d=e-(e-i.originX)*s,r=t-(t-i.originY)*s,l=(V?.snapDist??1)/(o/(V?.ppm??o));k={originX:d,originY:r,ppm:o,snapDist:l},v()}function j(){return k??V}function xn(e){if(e.ctrlKey||e.metaKey){e.preventDefault();const t=a.getBoundingClientRect(),n=e.deltaY<0?1.12:1/1.12;Ee(e.clientX-t.left,e.clientY-t.top,n)}else if(k!==null){e.preventDefault();const t=j(),n=k.snapDist;k={originX:t.originX-e.deltaX,originY:t.originY-e.deltaY,ppm:t.ppm,snapDist:n},v()}}function Cn(e,t,n){return e>=n.x&&e<n.x+n.w&&t>=n.y&&t<n.y+n.h}function wn(e){if(e.button!==0||!j())return;a.focus({preventScroll:!0}),a.setPointerCapture(e.pointerId);const t=a.getBoundingClientRect(),n=e.clientX-t.left,i=e.clientY-t.top;if(We&&p.legendPosition==="floating"&&Cn(n,i,We)){xe=!0,Fe={x:n,y:i},Dt={...Et};return}if(Ae&&X!=="pan"){Se=!0,me=!0,we={x:n,y:i},qe={x:j().originX,y:j().originY},a.style.cursor="grabbing";return}if(X==="zoom"||X==="select"){Ce=!0,O=P={x:n,y:i};return}me=!0,we={x:n,y:i},qe={x:j().originX,y:j().originY},a.style.cursor="grabbing"}function Sn(e){const t=a.getBoundingClientRect(),n=e.clientX-t.left,i=e.clientY-t.top;if(Ce){P={x:n,y:i},Wt();return}if(xe){const f=n-Fe.x,h=i-Fe.y;f*f+h*h>16&&(xe=!1,He=!0,a.style.cursor="grabbing")}if(He){Et={x:Dt.x+(n-Fe.x),y:Dt.y+(i-Fe.y)},Wt();return}if(me){const f=j(),h=k?.snapDist??V?.snapDist??1;k={originX:qe.x+(n-we.x),originY:qe.y+(i-we.y),ppm:f.ppm,snapDist:h},Wt();return}const o=j();if(!o)return;const s=(n-o.originX)/o.ppm,d=(o.originY-i)/o.ppm,l=Bn(s,d,o.snapDist)?.die??null,c=St.find(f=>n>=f.x&&n<f.x+f.w&&i>=f.y&&i<f.y+f.h);if(un(c?c.bin:void 0),c){a.style.cursor="pointer",w&&(w.innerHTML=c.label??`Bin ${c.bin}`,w.style.display="block",Kt(w,a,e.clientX,e.clientY)),Ue?.(null,e);return}if(X==="pan"&&(a.style.cursor=l?"crosshair":"grab"),w)if(l)if(Qt){const f=Qt(l);f===null?w.style.display="none":(typeof f=="string"?w.innerHTML=f:(w.innerHTML="",w.appendChild(f)),w.style.display="block",Kt(w,a,e.clientX,e.clientY))}else w.innerHTML=xi(l,m.plotMode,{testDefs:ne,hbinDefs:G,sbinDefs:U,fallbackFormat:Pe,aggrMethod:m.aggrMethod,lotSize:m.lotSize,metadataFields:ce,activeTest:m.activeTest,reticleConfig:an}),w.style.display="block",Kt(w,a,e.clientX,e.clientY);else w.style.display="none";Ue?.(l,e)}function Mn(e){const t=a.getBoundingClientRect(),n=e.clientX-t.left,i=e.clientY-t.top,o=e.ctrlKey||e.metaKey;if(w&&(n<0||i<0||n>t.width||i>t.height)&&(w.style.display="none"),Ce){Ce=!1,P={x:n,y:i};const r=n-O.x,l=i-O.y,c=j();if(X==="zoom"){if(r*r+l*l<25)Ee(n,i,2);else if(c){const f=Math.min(O.x,P.x),h=Math.max(O.x,P.x),Q=Math.min(O.y,P.y),De=Math.max(O.y,P.y),ee=h-f,ue=De-Q;if(ee>4&&ue>4){const M=a.clientWidth,B=a.clientHeight,zt=M/ee,lt=B/ue,q=Math.min(zt,lt),at=vn(c.ppm*q),Xe=at/c.ppm,Yt=(f+h)/2,Vt=(Q+De)/2;k={originX:M/2-(Yt-c.originX)*Xe,originY:B/2-(Vt-c.originY)*Xe,ppm:at,snapDist:c.snapDist/Xe}}}v(),a.style.cursor="crosshair";return}if(r*r+l*l<25)Ht(n,i,o,e);else if(c){const f=(Math.min(O.x,P.x)-c.originX)/c.ppm,h=(Math.max(O.x,P.x)-c.originX)/c.ppm,Q=(c.originY-Math.max(O.y,P.y))/c.ppm,De=(c.originY-Math.min(O.y,P.y))/c.ppm,ee=m.hoverPoints,ue=[];for(let M=0;M<ee.length;M++)if(ee[M].x>=f&&ee[M].x<=h&&ee[M].y>=Q&&ee[M].y<=De){const B=m.dies[M];B&&ue.push(B)}if(o)for(const M of ue){const B=Be(M);E.has(B)?E.delete(B):E.add(B)}else E=new Set(ue.map(M=>Be(M)));he?.(Ft())}v(),a.style.cursor="crosshair";return}if(xe){xe=!1,Ht(n,i,o,e);return}if(He){He=!1,a.style.cursor="grab";return}if(!me)return;if(me=!1,Se){Se=!1,a.style.cursor=Ae||X==="pan"?"grab":"crosshair";return}a.style.cursor=X==="pan"?"grab":"crosshair";const s=n-we.x,d=i-we.y;s*s+d*d<25&&Ht(n,i,o,e)}function Ht(e,t,n,i){for(const r of St)if(e>=r.x&&e<r.x+r.w&&t>=r.y&&t<r.y+r.h){if(typeof r.bin=="number"){const l=p.highlightBin===r.bin?void 0:r.bin;D({highlightBin:l})}else{const l=p.highlightMetadataValue===r.bin?void 0:r.bin;D({highlightMetadataValue:l})}return}if(We&&Cn(e,t,We))return;const o=j();if(!o)return;const d=Bn((e-o.originX)/o.ppm,(o.originY-t)/o.ppm,o.snapDist)?.die??null;if(d){Gn?.(d,i);const r=Be(d);n?E.has(r)?E.delete(r):E.add(r):E=new Set([r]),he?.(Ft()),v()}else n||(E=new Set,he?.([]),v())}function Ft(){const e=[],t=m.hoverPoints;for(let n=0;n<t.length;n++){const i=m.dies[n];i&&E.has(Be(i))&&e.push(i)}return e}function En(){un(void 0),w&&pt(g),Ue?.(null,new MouseEvent("mouseleave")),a.style.cursor=X==="pan"?"grab":"crosshair"}function Dn(){me=!1,Se=!1,Ce=!1,xe=!1,He=!1,w&&pt(g),Ue?.(null,new MouseEvent("mouseleave")),a.style.cursor=X==="pan"?"grab":"crosshair",v()}function Bn(e,t,n){const i=m.hoverPoints,o=m.rectangles;for(let l=0;l<o.length;l++){const c=o[l];if(Math.abs(e-c.x)<=c.width/2&&Math.abs(t-c.y)<=c.height/2){const f=m.dies[l];return f?{die:f,index:l}:null}}let s=null,d=-1,r=n*n;for(let l=0;l<i.length;l++){const c=i[l].x-e,f=i[l].y-t,h=c*c+f*f;h<r&&(r=h,s=m.dies[l]??null,d=l)}return s?{die:s,index:d}:null}const kn=new H.ResizeObserver(()=>{V=null,k=null,v()});kn.observe(a),C.height==null&&$n<=0&&_t(()=>_t(()=>{K.clientHeight>0||console.warn("[wafermap] The map container has zero height, so the map cannot render. renderWaferMap fills its container \u2014 give the container a resolved height: an explicit CSS `height` (e.g. 600px), a height-resolved flex/grid parent, or `position:absolute; inset:0`. Alternatively pass `{ height: 600 }` in the render options and the library will size it for you.")}));let Ve=H.matchMedia(`(resolution: ${H.devicePixelRatio}dppx)`);const ot=()=>{Ve.removeEventListener("change",ot),Ve=H.matchMedia(`(resolution: ${H.devicePixelRatio}dppx)`),Ve.addEventListener("change",ot),v()};Ve.addEventListener("change",ot);const Ln=H.matchMedia("(prefers-color-scheme: dark)"),Tn=()=>v();Ln.addEventListener("change",Tn);const On=()=>{w&&pt(g)};H.addEventListener("blur",On);function Pn(e){if(e.key==="Escape"&&E.size>0&&(E=new Set,he?.([]),v()),(e.key==="e"||e.key==="E")&&gt!=="view-only"&&en&&(e.stopPropagation(),(Jt??yn)()),(e.ctrlKey||e.metaKey)&&(e.key==="="||e.key==="+"?(e.preventDefault(),Ee(a.clientWidth/2,a.clientHeight/2,1.2)):e.key==="-"?(e.preventDefault(),Ee(a.clientWidth/2,a.clientHeight/2,1/1.2)):e.key==="0"&&(e.preventDefault(),st())),!e.ctrlKey&&!e.metaKey&&!e.shiftKey){const n=j();if(n){const i=k?.snapDist??V?.snapDist??1;let o=0,s=0;e.key==="ArrowLeft"&&(o=20,e.preventDefault()),e.key==="ArrowRight"&&(o=-20,e.preventDefault()),e.key==="ArrowUp"&&(s=20,e.preventDefault()),e.key==="ArrowDown"&&(s=-20,e.preventDefault()),(o||s)&&(k={originX:n.originX+o,originY:n.originY+s,ppm:n.ppm,snapDist:i},v())}e.key===" "&&!Ae&&(e.preventDefault(),Ae=!0,X!=="pan"&&(a.style.cursor="grab"))}}function Rn(e){e.key===" "&&(Ae=!1,Se&&(me=!1,Se=!1),a.style.cursor=X==="pan"?"grab":"crosshair")}a.style.cursor="grab",a.setAttribute("tabindex","0"),a.addEventListener("wheel",xn,{passive:!1}),a.addEventListener("pointerdown",wn),a.addEventListener("pointermove",Sn),a.addEventListener("pointerup",Mn),a.addEventListener("pointerleave",En),a.addEventListener("pointercancel",Dn);const In=()=>st();a.addEventListener("dblclick",In),a.addEventListener("keydown",Pn),a.addEventListener("keyup",Rn);const Wn=e=>{e.stopPropagation()};a.addEventListener("click",Wn),a.clientWidth,v(),Ge?.();function st(){V=null,k=null,v()}function At(){import("./userGuideHtml.js").then(e=>Wi({buildWaferMap:Ei,renderWaferMap,renderWaferGallery:void 0,analyzeWaferMap:Bi},e.USER_GUIDE_HTML,ei,K))}return ve(),{setDies(e){te=e,ze(),v(),N&&pe()},setResult(e){yt=e,$=e.wafer,te=e.dies,G=e.hbinDefs,U=e.sbinDefs,ne=e.testDefs,ce=e.metadataFields,ln=e.reticles?.length?e.reticles:void 0,an=e.reticleConfig,rn=e.view?.dataAxisFlip,bt=e.isLotStack,vt=e.aggrMethod,xt=e.lotSize,si(),ze(),v(),N&&pe()},setOptions(e){bn(e)},getOptions(){return{...p}},resetZoom:st,setSelection(e){E=new Set(e.map(t=>Be(t))),v()},clearSelection(){E=new Set,he?.([]),v()},setFallbackFormat(e){Pe=e,ze(),v()},setFindingsNotice(e){gn=e,N?pe():R&&Ye()},setStatsSummary(e){if(re=e,mt(),Bt(),N)pe();else if(R)Ye();else if(e&&!Te?.placement){const t=Te?.defaultOpen??!Le;R=$t("right",ge,g),R.style.display=t?"block":"none";const n=S.parentElement,i=S.nextSibling;fe=Gt(S,R,"right"),n?.insertBefore(fe,i),Ye()}it()},setSummaryVisible(e){I&&(I.style.display=e?"flex":"none")},setViewControlsVisible(e){L&&(L.style.display=e?"flex":"none")},setExpandVisible(e){J&&(J.style.display=e?"flex":"none")},setHelpButtonVisible(e){tt&&(tt.style.display=e?"flex":"none")},setIdentityVisible(e){Ct=!e,z&&(z.style.display=e?"":"none"),ve()},openUserGuide:At,setInsightsOpen(e){Ie(e),A&&(e?(A.dataset.active="1",A.style.background=x.bgActive,A.style.color=x.iconActive):(delete A.dataset.active,A.style.background="transparent",A.style.color=x.icon))},closeSummaryPanel(){const e=N??R;!e||e.style.display==="none"||(e.style.display="none",I&&(delete I.dataset.active,I.style.background="transparent",I.style.color=x.icon))},setTooltipParent(e){w&&Oi(e)},getActiveLegend(){const e=p.plotMode;if(e==="metadata"){const s=p.activeMetadataKey;if(!s)return null;const d=ce?.find(l=>l.key===s),r=[...new Set(te.map(l=>l.metadata?.[s]).filter(l=>l!=null&&(typeof l=="string"||typeof l=="number"||typeof l=="boolean")).map(String))].sort();return r.length?r.map((l,c)=>{const f=d?.values?.find(Q=>Q.value===l),h=f?.color??eo(c);return{bin:l,name:f?.label??l,color:h}}):null}if(e!=="hardBin"&&e!=="softBin")return null;const t=e==="hardBin",n=t?G:U,i=[...new Set(te.map(s=>t?s.hbin:s.sbin).filter(s=>s!==void 0))].sort((s,d)=>s-d);if(!i.length)return null;const o=t?m.binColors.hard:m.binColors.soft;return i.map(s=>{const d=n?.find(r=>r.bin===s);return{bin:s,name:d?.name??`Bin ${s}`,color:o.get(s)??to}})},destroy(){ye?.close(),mn?.()?.remove(),kt&&g.removeEventListener("click",kt,!0),a.removeEventListener("wheel",xn),a.removeEventListener("pointerdown",wn),a.removeEventListener("pointermove",Sn),a.removeEventListener("pointerup",Mn),a.removeEventListener("pointerleave",En),a.removeEventListener("pointercancel",Dn),a.removeEventListener("dblclick",In),a.removeEventListener("keydown",Pn),a.removeEventListener("keyup",Rn),a.removeEventListener("click",Wn),kn.disconnect(),Ve.removeEventListener("change",ot),Ln.removeEventListener("change",Tn),H.removeEventListener("blur",On),ii(),w&&pt(g),oe?.remove(),Me&&(Me.parentElement?.insertBefore(S,Me),Me.remove()),fe&&(fe.parentElement?.insertBefore(S,fe),fe.remove()),S.remove(),a.style.cursor="",Y?.destroy(),Z?.destroy(),ke.remove()}}}
@@ -3,10 +3,14 @@ import type { Die } from '../core/dies.js';
3
3
  import { type BinDef, type TestDef, type YieldSummary, type MetadataFieldDef } from '../renderer/buildWaferMap.js';
4
4
  import type { StatsFinding, StatsSummary, LotStatsSummary } from '../stats/types.js';
5
5
  import { type FindingsFilter } from '../stats/filterFindings.js';
6
+ import { type BinColors } from '../renderer/binColors.js';
6
7
  import { buildWarningsBanner, type WaferWarning } from './warnings.js';
7
8
  export { buildWarningsBanner };
9
+ import type { PlotMode } from '../renderer/buildView.js';
10
+ import { type CapabilityItem } from '../stats/capability.js';
8
11
  import { type SaveTextHandler } from './toolbar.js';
9
12
  import { type DieListDisplayOptions } from './dieList.js';
13
+ export { csvField } from '../core/utils.js';
10
14
  import type { WaferMetadata } from '../core/metadata.js';
11
15
  export interface SummaryPanelOptions {
12
16
  /** Which side of the content area to place the panel. Default 'right'. */
@@ -20,19 +24,6 @@ export interface SummaryPanelOptions {
20
24
  * metadata badge and `buildMetadataStripRow`/`buildFacetSummaryChips` so
21
25
  * they can't drift on what they consider "no metadata". */
22
26
  export declare function metadataEntries(meta: Record<string, unknown>): Array<[string, string]>;
23
- /** Single-line, wrap-when-needed "Label: value1, value2 [+N more]" chips —
24
- * the gallery's top-of-grid strip summary over a whole (possibly multi-lot)
25
- * population, one chip per `FacetField` from `stats/facets.ts`'s
26
- * `buildFacetTable`. Shows every distinct value a field takes
27
- * across the population — so a mixed-lot gallery still surfaces "Lot:
28
- * LOT123, LOT456" instead of silently dropping a field the moment it
29
- * varies. `field.values` is already sorted by coverage (`buildFacetTable`),
30
- * so the values shown inline are the most common ones. Labels use
31
- * `prettyKey(field.key)`, not `field.label` — `DEFAULT_FACET_CURATION`'s own
32
- * labels (e.g. "Program") differ from the `prettyKey` convention every other
33
- * metadata surface in this library uses ("Test Program"), and this strip
34
- * must read as the same field as those surfaces, not a differently-named one.
35
- * Returns `null` for an empty table. */
36
27
  export declare function buildFacetSummaryChips(table: Array<{
37
28
  key: string;
38
29
  values: Array<{
@@ -97,36 +88,117 @@ export declare function buildYieldSection(yieldSummary: YieldSummary, dataCovera
97
88
  edgeExcludedDies: number;
98
89
  ratio: number;
99
90
  }, passBins?: number[]): HTMLDivElement;
100
- export declare function buildBinSection(dies: Die[], binDefs: BinDef[] | undefined, mode: 'hard' | 'soft', colorScheme?: string,
91
+ export declare function buildBinSection(dies: Die[], binDefs: BinDef[] | undefined, mode: 'hard' | 'soft',
92
+ /**
93
+ * The map's resolved colours for this bin type (`View.binColors.hard`/`.soft`),
94
+ * so bars match die fills. Omitted ⇒ resolved here from `dies` with the
95
+ * default palette — never a second colour rule.
96
+ */
97
+ binColors?: ReadonlyMap<number, string>,
101
98
  /**
102
99
  * Precomputed counts (e.g. `StatsSummary.stats.hardBinCounts`/`.softBinCounts`,
103
100
  * already scoped to the yield-eligible population) — used directly instead
104
101
  * of re-walking `dies` when supplied.
105
102
  */
106
- precomputedCounts?: Record<number, number>): HTMLDivElement | null;
103
+ precomputedCounts?: Record<number, number>,
104
+ /** Pass bins, pinned to the top of the pareto ordering. See `binRows`. */
105
+ passBins?: number[]): HTMLDivElement | null;
106
+ /**
107
+ * The panels' bin section: identical bars to `buildBinSection`, wrapped in a
108
+ * collapsible shell with a Hard/Soft selector.
109
+ *
110
+ * Which bin type it opens on is derived from the map's own plot mode, not from
111
+ * which bin data happens to exist. The old rule (`hasHbin ? 'hard' : 'soft'`) meant
112
+ * a soft-bin map always sat beside a hard-bin breakdown whenever both bin types
113
+ * were present — the panel silently describing a different population from the one
114
+ * on screen. `maplessSummary.ts` already derived this correctly from `plotMode`;
115
+ * this makes all three call sites agree.
116
+ *
117
+ * The selector only appears when both bin types actually have data — a one-option
118
+ * toggle is noise.
119
+ */
120
+ export declare function buildBinBreakdownSection(params: {
121
+ dies: Die[];
122
+ hbinDefs?: BinDef[];
123
+ sbinDefs?: BinDef[];
124
+ /** The map's resolved bin colours (`View.binColors`). Omitted ⇒ resolved from `dies`. */
125
+ binColors?: BinColors;
126
+ hardCounts?: Record<number, number>;
127
+ softCounts?: Record<number, number>;
128
+ /** The map's active plot mode. `hardBin`/`softBin` pick the matching bin type. */
129
+ plotMode?: PlotMode;
130
+ passBins?: number[];
131
+ panel?: HTMLElement;
132
+ }): HTMLDivElement | null;
107
133
  /** Aggregate bin counts across all wafers in the lot — no lot-pooled
108
134
  * precomputed bin counts exist (`hardBinCounts`/`softBinCounts` are
109
135
  * per-wafer only), so this scans the pooled `Die[]` directly, same as
110
136
  * `buildBinSection` does when it has no `precomputedCounts`. */
111
- export declare function buildLotBinSection(allDies: Die[], binDefs: BinDef[] | undefined, mode: 'hard' | 'soft', colorScheme?: string): HTMLDivElement | null;
112
- export declare function buildRingSection(dies: Die[], wafer: Wafer, ringCount: number, passBins: number[]): HTMLDivElement | null;
113
- export declare function buildQuadrantSection(dies: Die[], wafer: Wafer, ringCount: number, passBins: number[]): HTMLDivElement | null;
114
- /** Aggregate ring yield across all wafers in the lot. */
115
- export declare function buildLotRingSection(diesByWafer: Die[][], allWafers: Wafer[], ringCount: number, passBins: number[]): HTMLDivElement | null;
116
- /** Aggregate quadrant yield across all wafers in the lot. */
117
- export declare function buildLotQuadrantSection(diesByWafer: Die[][], allWafers: Wafer[], ringCount: number, passBins: number[]): HTMLDivElement | null;
137
+ export declare function buildLotBinSection(allDies: Die[], binDefs: BinDef[] | undefined, mode: 'hard' | 'soft', binColors?: ReadonlyMap<number, string>): HTMLDivElement | null;
138
+ /**
139
+ * Region yield as ONE section with a Ring/Quadrant selector, replacing the two
140
+ * always-on stacked sections (`Ring Yield` + `Quadrant Yield`) this used to render.
141
+ *
142
+ * Ring is the default and quadrant is opt-in because they are not equally
143
+ * informative. Ring yield tracks the edge roll-off that dominates real wafer maps
144
+ * (85% core → 70% edge is routine, and is what the edge-local findings are about).
145
+ * Quadrant yield averages over half the wafer twice and lands within a point or two
146
+ * of the wafer mean on almost every lot — and in the case where a quadrant really
147
+ * is asymmetric, the findings engine already reports it with a significance test
148
+ * behind it, which four bare bars do not have. Between them they were consuming
149
+ * eight rows of a 260px column to say one thing.
150
+ *
151
+ * Works uniformly for a single wafer (`diesByWafer: [dies]`, `allWafers: [wafer]`)
152
+ * or a whole lot.
153
+ */
154
+ export declare function buildRegionYieldPanelSection(params: {
155
+ diesByWafer: Die[][];
156
+ allWafers: Wafer[];
157
+ ringCount: number;
158
+ passBins: number[];
159
+ panel?: HTMLElement;
160
+ }): HTMLDivElement | null;
118
161
  /** Lot overview — wafer count and mean (unweighted arithmetic mean of each
119
162
  * wafer's own yield%, not a die-weighted lot yield — see CLAUDE.md on
120
163
  * correctly labelling aggregation methods) wafer yield. Metadata is a
121
164
  * separate section (`buildMetadataInfoSection`) built from the lot's own
122
165
  * items, not from `lotSummary.lot` — see that function's doc comment. */
123
- export declare function buildLotOverviewSection(lotSummary: LotStatsSummary): HTMLDivElement;
124
- /** Per-wafer yield bars, absolute 0–100% scale, with a median marker line and
125
- * below-median wafers muted — lets an engineer spot outlier wafers within
126
- * the lot at a glance. */
166
+ export declare function buildLotOverviewSection(lotSummary: LotStatsSummary,
167
+ /** Per-wafer summaries, for the analysed/excluded die tally below. */
168
+ perWaferSummaries?: StatsSummary[]): HTMLDivElement;
169
+ /** Per-wafer yield bars, absolute 0–100% scale, with a visible median marker and
170
+ * low outliers named in the row label — lets an engineer spot outlier wafers
171
+ * within the lot at a glance.
172
+ *
173
+ * "Outlier" is the Tukey rule (below `Q1 − 1.5 × IQR`), not "below the median".
174
+ * The median splits every lot in half by construction, so half the bars always
175
+ * got flagged — including on a lot where every wafer is within a point of every
176
+ * other. The Tukey fence flags nothing on a tight lot, which is the correct
177
+ * answer, and it is stated in text rather than encoded only in a colour.
178
+ *
179
+ * Slot order is the default and must stay so: wafer-number order is what makes a
180
+ * slot-correlated pattern (a bad cassette position, a chuck issue) visible at all,
181
+ * and sorting by yield destroys it. Yield order is opt-in via the header control. */
127
182
  export declare function buildPerWaferYieldSection(lotSummary: LotStatsSummary, items: Array<{
128
183
  label?: string;
129
- } | null>, onWaferClick?: (waferIndex: number) => void): HTMLDivElement | null;
184
+ } | null>, onWaferClick?: (waferIndex: number) => void,
185
+ /** Panel element owning the sort/collapse state. Omit for a stateless render (reports). */
186
+ panel?: HTMLElement,
187
+ /**
188
+ * Per-wafer findings tally, badged onto each row.
189
+ *
190
+ * This is what let the gallery's separate "Findings" tab go away. That tab was
191
+ * a second per-wafer list — a SUBSET (only wafers that have findings), with the
192
+ * same row idiom as this one but a different click action, and no findings text
193
+ * on it at all. Folding the count in here gives one list instead of two, and
194
+ * shows the case the subset list structurally could not: a low-yielding wafer
195
+ * with no findings, sitting next to the flagged ones.
196
+ */
197
+ findingsFor?: (waferIndex: number) => {
198
+ total: number;
199
+ unusual: number;
200
+ notable: number;
201
+ } | undefined): HTMLDivElement | null;
130
202
  /**
131
203
  * Identity context stamped onto a per-test CSV export, so a file that has
132
204
  * left the app still says which wafer(s) it describes. Die-level metadata is
@@ -147,8 +219,6 @@ export interface CsvExportContext {
147
219
  * section title and emitted as a `Population` CSV column. */
148
220
  populationLabel?: string;
149
221
  }
150
- /** CSV field escaper — exported so `dieList.ts` shares it rather than a second copy. */
151
- export declare function csvField(value: string): string;
152
222
  export declare function buildTestSection(dies: Die[], testDefs: TestDef[] | undefined, fallbackFormat?: 'si' | 'engineering',
153
223
  /**
154
224
  * When supplied, each test's descriptive stats and spec-yield are read from
@@ -182,7 +252,27 @@ precomputedTestStats?: {
182
252
  /** Optional host hook for the "Export CSV" button — see `saveTextFile` (toolbar.ts). */
183
253
  onSaveText?: SaveTextHandler,
184
254
  /** Wafer identity to stamp on the CSV export. See {@link CsvExportContext}. */
185
- csv?: CsvExportContext): HTMLDivElement | null;
255
+ csv?: CsvExportContext,
256
+ /**
257
+ * Population for the Ppk column — one item per wafer (see `stats/capability.ts`).
258
+ * Passing `[{ dies }]` for a single wafer is correct. Omit to drop the column.
259
+ */
260
+ capabilityItems?: CapabilityItem[],
261
+ /** Panel element owning the collapsed state. Omit for a stateless render. */
262
+ panel?: HTMLElement,
263
+ /**
264
+ * On-screen column set.
265
+ *
266
+ * `'compact'` (Test / N / Mean / Ppk / Spec yield) exists for the docked Summary
267
+ * panel's 260px column, where the full set meant four visible columns and a
268
+ * horizontal scrollbar nested inside a vertical one. `'full'` adds the
269
+ * descriptive statistics back and is the DEFAULT, because every other consumer
270
+ * — the Insights Overview card above all — has the width to show them, and
271
+ * silently trimming a full-width table to a narrow surface's budget loses real
272
+ * information for no benefit. The CSV export always carries every column
273
+ * regardless.
274
+ */
275
+ columns?: 'compact' | 'full'): HTMLDivElement | null;
186
276
  /**
187
277
  * "Functional Tests" section — one row per functional (`testType: 'F'`) test
188
278
  * with pass/fail counts and pass rate. The functional counterpart of
@@ -199,14 +289,18 @@ export declare function buildFunctionalTestSection(dies: Die[], testDefs: TestDe
199
289
  /** Optional host hook for the "Export CSV" button — see `saveTextFile` (toolbar.ts). */
200
290
  onSaveText?: SaveTextHandler,
201
291
  /** Wafer identity to stamp on the CSV export. See {@link CsvExportContext}. */
202
- csv?: CsvExportContext): HTMLDivElement | null;
292
+ csv?: CsvExportContext,
293
+ /** Panel element owning the collapsed state. Omit for a stateless render. */
294
+ panel?: HTMLElement): HTMLDivElement | null;
203
295
  /**
204
296
  * Lot-pooled "Functional Tests" section: when every wafer's
205
297
  * `StatsSummary.stats.functionalYield` is available, sums pass/fail/total
206
298
  * counts across wafers (exact — counts pool losslessly, unlike quantiles);
207
299
  * otherwise recomputes from the pooled dies.
208
300
  */
209
- export declare function buildLotFunctionalSection(allDies: Die[], testDefs: TestDef[] | undefined, perWaferSummaries?: StatsSummary[], onSaveText?: SaveTextHandler): HTMLDivElement | null;
301
+ export declare function buildLotFunctionalSection(allDies: Die[], testDefs: TestDef[] | undefined, perWaferSummaries?: StatsSummary[], onSaveText?: SaveTextHandler,
302
+ /** Panel element owning the collapsed state. Omit for a stateless render. */
303
+ panel?: HTMLElement): HTMLDivElement | null;
210
304
  export declare function buildFindingsSection(findings: StatsFinding[], statsSummary: StatsSummary | LotStatsSummary, onFindingClick: (finding: StatsFinding, row: HTMLButtonElement) => void, activeFindingId: string | null,
211
305
  /**
212
306
  * When true, skips the collapsible "Findings" header/badge and renders
@@ -215,6 +309,16 @@ export declare function buildFindingsSection(findings: StatsFinding[], statsSumm
215
309
  * Default false (collapsible-section behavior, used when this is one
216
310
  * section among several, e.g. inside the Insights Overview tab's legacy
217
311
  * embedding).
312
+ *
313
+ * NOTE — the `false` path has no in-repo caller today, and it is the only
314
+ * collapsible section in this panel that does NOT persist its collapsed
315
+ * state: `collapsibleSection` needs both a `stateKey` and the owning `panel`
316
+ * to do that, and this function is not given a `panel`. Every section that
317
+ * actually renders (bins, regionYield, waferYield, testValues,
318
+ * functionalTests, findings) does persist, so the panel is consistent as
319
+ * shipped. If you revive this path — or call it from outside the library —
320
+ * thread a `panel` through and give it a `stateKey`, or it will be the one
321
+ * section that forgets whether the reader collapsed it.
218
322
  */
219
323
  standalone?: boolean): HTMLDivElement | null;
220
324
  /**
@@ -223,7 +327,35 @@ standalone?: boolean): HTMLDivElement | null;
223
327
  * true)` in its own collapsible "Findings" header + filter row). Returns
224
328
  * `null` when the source has no findings at all (nothing to filter).
225
329
  */
226
- export declare function buildFindingsSectionWithFilter(source: StatsSummary | LotStatsSummary, onFindingClick: (finding: StatsFinding, row: HTMLButtonElement) => void, activeFindingId: string | null, filter: FindingsFilter, onFilterChange: () => void): HTMLDivElement | null;
330
+ /**
331
+ * A host-supplied row at the top of the Findings section, for stating that a
332
+ * category of finding is NOT present and offering to compute it.
333
+ *
334
+ * This exists because the absence of a finding category is invisible: a reader
335
+ * looking at a Findings list has no way to tell that a whole class of analysis
336
+ * was skipped. Advertising the *control* elsewhere (a menu item) does not fix
337
+ * that — the reader has no reason to go looking. The notice belongs where the
338
+ * gap is.
339
+ *
340
+ * wmap does not decide when to show this. The host knows what it did and did
341
+ * not compute, and what recomputing would cost; it passes a notice or it does
342
+ * not. Keep `detail` honest about that cost — an unpriced "Analyse" button on a
343
+ * lot where the answer takes half a minute is worse than no button.
344
+ */
345
+ export interface FindingsNotice {
346
+ /** What is missing, stated plainly. Not a question, not an exhortation. */
347
+ message: string;
348
+ /** Supporting detail — normally the size of the job and its expected cost. */
349
+ detail?: string;
350
+ /** Action label. Omit (with `onAction`) for a message-only notice. */
351
+ actionLabel?: string;
352
+ onAction?: () => void;
353
+ }
354
+ export declare function buildFindingsSectionWithFilter(source: StatsSummary | LotStatsSummary, onFindingClick: (finding: StatsFinding, row: HTMLButtonElement) => void, activeFindingId: string | null, filter: FindingsFilter, onFilterChange: () => void,
355
+ /** Panel element owning the collapsed state. Omit for a stateless render. */
356
+ panel?: HTMLElement,
357
+ /** See {@link FindingsNotice}. Rendered above the filter row. */
358
+ notice?: FindingsNotice): HTMLDivElement | null;
227
359
  /**
228
360
  * Aggregate test value stats across all wafers in the lot. When every
229
361
  * wafer's `StatsSummary.stats.perTestStats`/`.testSpecYield` is available
@@ -236,8 +368,37 @@ export declare function buildFindingsSectionWithFilter(source: StatsSummary | Lo
236
368
  */
237
369
  export declare function buildLotTestSection(allDies: Die[], testDefs: TestDef[] | undefined, fallbackFormat?: 'si' | 'engineering', perWaferSummaries?: StatsSummary[],
238
370
  /** Optional host hook for the "Export CSV" button — see `saveTextFile` (toolbar.ts). */
239
- onSaveText?: SaveTextHandler): HTMLDivElement | null;
240
- export declare function createSummaryPanelEl(placement: 'right' | 'left' | 'top' | 'bottom', ownerDocument?: Document): HTMLDivElement;
371
+ onSaveText?: SaveTextHandler,
372
+ /**
373
+ * One item per wafer for the Ppk column — the lot is the population Ppk is
374
+ * meant to describe, so every wafer must be passed, not the pooled die array.
375
+ */
376
+ capabilityItems?: CapabilityItem[],
377
+ /** Panel element owning the collapsed state. Omit for a stateless render. */
378
+ panel?: HTMLElement,
379
+ /** See `buildTestSection`'s `columns`. Defaults to the full set. */
380
+ columns?: 'compact' | 'full'): HTMLDivElement | null;
381
+ export declare function createSummaryPanelEl(placement: 'right' | 'left' | 'top' | 'bottom',
382
+ /**
383
+ * Inset from the edge this panel docks against — stated by the caller,
384
+ * because only the caller knows what that edge IS.
385
+ *
386
+ * `renderWaferMap` docks it inside `mapBox` and passes `EDGE_GUTTER`, the
387
+ * same inset its chrome row uses, so the panel lines up with the toolbar
388
+ * above it. It used to pass a smaller inset so the panel would align with a
389
+ * toolbar FLOATING in the map's own corner; that toolbar now sits in a row
390
+ * above the map, and the small inset aligned with nothing.
391
+ * `renderWaferGallery` docks it at the edge of the gallery region itself,
392
+ * where it is a bounded surface against the window and takes `EDGE_GUTTER`
393
+ * like every other card there.
394
+ *
395
+ * Internal (not exported from the package), so this is a positional
396
+ * parameter rather than an options bag. It has NO default on purpose: a
397
+ * default is a guess about context, and guessing wrong is what put a 4px
398
+ * gutter on the gallery's panel where 12 belonged, and 12px inside a map card
399
+ * where 4 belonged.
400
+ */
401
+ edgeInset: string, ownerDocument?: Document): HTMLDivElement;
241
402
  /** Wrap the canvas + panel in a flex container according to placement. */
242
403
  export declare function wrapWithSummaryPanel(content: HTMLElement, panel: HTMLDivElement, placement: 'right' | 'left' | 'top' | 'bottom'): HTMLDivElement;
243
404
  /** Render all wafer-level sections into a panel element. Clears existing content. */
@@ -264,17 +425,40 @@ export declare function renderWaferSummaryContent(panel: HTMLDivElement, params:
264
425
  warnings?: WaferWarning[];
265
426
  passBins?: number[];
266
427
  ringCount?: number;
267
- colorScheme?: string;
428
+ /** The map's resolved bin colours (`View.binColors`), so bars match die fills. */
429
+ binColors?: BinColors;
430
+ /**
431
+ * The map's active plot mode. Selects which bin type the bin breakdown opens
432
+ * on, so the panel describes the population actually on screen — see
433
+ * `buildBinBreakdownSection`. Omitting it falls back to whichever bin type
434
+ * has data, which is the old (wrong-for-soft-bin-maps) behaviour.
435
+ */
436
+ plotMode?: PlotMode;
268
437
  fallbackFormat?: 'si' | 'engineering';
269
438
  onFindingClick?: (finding: StatsFinding, row: HTMLButtonElement) => void;
270
439
  activeFindingId?: string | null;
271
440
  findingsFilter?: FindingsFilter;
272
441
  onFindingsFilterChange?: () => void;
442
+ /** See {@link FindingsNotice} — a host row at the top of the Findings section. */
443
+ findingsNotice?: FindingsNotice;
273
444
  onSaveText?: SaveTextHandler;
274
445
  /** Label/order hints for die metadata columns, e.g. `WaferMapResult.metadataFields`. */
275
446
  metadataFields?: MetadataFieldDef[];
276
447
  /** See `RenderOptions.dieList` — gates the "View die list" link below. */
277
448
  dieListOptions?: DieListDisplayOptions;
449
+ /**
450
+ * Suppress the "Wafer Info" metadata section because the caller already
451
+ * renders this metadata elsewhere — `renderWaferMap` passes true whenever its
452
+ * identity header is mounted (`showIdentity`, the default), since that
453
+ * header's expandable panel is built from the very same
454
+ * `metadataEntries`/`buildCompactMetadataRows` helpers.
455
+ *
456
+ * This mirrors a decision the lot panel already makes for itself: it omits
457
+ * the section unconditionally because the gallery's top strip covers it (see
458
+ * `renderLotSummaryContent`). Two copies of the same fields in a 260px column,
459
+ * authoritative in neither place, is the thing being avoided in both cases.
460
+ */
461
+ metadataShownElsewhere?: boolean;
278
462
  }): void;
279
463
  /** Render lot-level content into the panel. Clears existing content. */
280
464
  export declare function renderLotSummaryContent(panel: HTMLDivElement, params: {
@@ -291,17 +475,29 @@ export declare function renderLotSummaryContent(panel: HTMLDivElement, params: {
291
475
  testDefs?: TestDef[];
292
476
  passBins?: number[];
293
477
  ringCount?: number;
294
- colorScheme?: string;
478
+ /** The gallery-wide bin colours, so bars match every card. */
479
+ binColors?: BinColors;
480
+ /** The gallery's active plot mode — see the wafer panel's `plotMode`. */
481
+ plotMode?: PlotMode;
295
482
  fallbackFormat?: 'si' | 'engineering';
296
483
  onFindingClick?: (finding: StatsFinding, row: HTMLButtonElement) => void;
297
484
  activeFindingId?: string | null;
298
485
  onWaferClick?: (waferIndex: number) => void;
299
486
  findingsFilter?: FindingsFilter;
300
487
  onFindingsFilterChange?: () => void;
488
+ /** See {@link FindingsNotice} — a host row at the top of the Findings section. */
489
+ findingsNotice?: FindingsNotice;
301
490
  onSaveText?: SaveTextHandler;
302
491
  /** See the wafer panel's `warnings` — collected by the renderer so every surface agrees. */
303
492
  warnings?: WaferWarning[];
304
493
  /** See `RenderOptions.dieList` — gates the "View die list" link below. */
305
494
  dieListOptions?: DieListDisplayOptions;
495
+ /** Per-wafer findings tally badged onto the Wafer Yield rows — see
496
+ * `buildPerWaferYieldSection`. */
497
+ findingsFor?: (waferIndex: number) => {
498
+ total: number;
499
+ unusual: number;
500
+ notable: number;
501
+ } | undefined;
306
502
  }): void;
307
503
  //# sourceMappingURL=summaryPanel.d.ts.map