@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.
- package/AGENTS.md +10 -2
- package/CHANGELOG.md +670 -0
- package/README.md +46 -3
- package/dist/packages/canvas-adapter/canvasTheme.d.ts +4 -0
- package/dist/packages/canvas-adapter/canvasTheme.js +1 -1
- package/dist/packages/canvas-adapter/charts/barPanel.d.ts +16 -0
- package/dist/packages/canvas-adapter/charts/barPanel.js +1 -1
- package/dist/packages/canvas-adapter/charts/binCluster.d.ts +0 -1
- package/dist/packages/canvas-adapter/charts/binCluster.js +1 -1
- package/dist/packages/canvas-adapter/charts/boxplot.d.ts +21 -2
- package/dist/packages/canvas-adapter/charts/boxplot.js +1 -1
- package/dist/packages/canvas-adapter/charts/capability.d.ts +10 -11
- package/dist/packages/canvas-adapter/charts/capability.js +1 -1
- package/dist/packages/canvas-adapter/charts/chartShell.d.ts +337 -3
- package/dist/packages/canvas-adapter/charts/chartShell.js +1 -1
- package/dist/packages/canvas-adapter/charts/correlation.d.ts +8 -10
- package/dist/packages/canvas-adapter/charts/correlation.js +2 -1
- package/dist/packages/canvas-adapter/charts/groupedBarPlot.d.ts +50 -0
- package/dist/packages/canvas-adapter/charts/groupedBarPlot.js +1 -0
- package/dist/packages/canvas-adapter/charts/histogram.d.ts +12 -2
- package/dist/packages/canvas-adapter/charts/histogram.js +1 -1
- package/dist/packages/canvas-adapter/charts/regionYieldDiagram.d.ts +0 -1
- package/dist/packages/canvas-adapter/charts/regionYieldDiagram.js +1 -1
- package/dist/packages/canvas-adapter/charts/scatter.d.ts +6 -1
- package/dist/packages/canvas-adapter/charts/scatter.js +1 -1
- package/dist/packages/canvas-adapter/charts/testPassRate.d.ts +20 -0
- package/dist/packages/canvas-adapter/charts/testPassRate.js +1 -0
- package/dist/packages/canvas-adapter/charts/trend.d.ts +37 -0
- package/dist/packages/canvas-adapter/charts/trend.js +1 -0
- package/dist/packages/canvas-adapter/dieList.js +7 -7
- package/dist/packages/canvas-adapter/hitGrid.d.ts +22 -0
- package/dist/packages/canvas-adapter/hitGrid.js +1 -0
- package/dist/packages/canvas-adapter/identityHeader.d.ts +46 -0
- package/dist/packages/canvas-adapter/identityHeader.js +1 -0
- package/dist/packages/canvas-adapter/index.d.ts +2 -1
- package/dist/packages/canvas-adapter/index.js +1 -1
- package/dist/packages/canvas-adapter/insightsTab.d.ts +51 -5
- package/dist/packages/canvas-adapter/insightsTab.js +1 -1
- package/dist/packages/canvas-adapter/maplessSummary.d.ts +5 -1
- package/dist/packages/canvas-adapter/maplessSummary.js +1 -1
- package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +15 -2
- package/dist/packages/canvas-adapter/renderWaferGallery.js +1 -20
- package/dist/packages/canvas-adapter/renderWaferMap.d.ts +110 -18
- package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
- package/dist/packages/canvas-adapter/summaryPanel.d.ts +233 -37
- package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
- package/dist/packages/canvas-adapter/toCanvas.d.ts +6 -0
- package/dist/packages/canvas-adapter/toCanvas.js +1 -1
- package/dist/packages/canvas-adapter/toolbar.d.ts +352 -15
- package/dist/packages/canvas-adapter/toolbar.js +2 -2
- package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
- package/dist/packages/canvas-adapter/userGuideHtml.js +115 -41
- package/dist/packages/canvas-adapter/version.d.ts +2 -2
- package/dist/packages/canvas-adapter/version.js +1 -1
- package/dist/packages/canvas-adapter/warnings.d.ts +21 -0
- package/dist/packages/canvas-adapter/warnings.js +1 -1
- package/dist/packages/core/dies.d.ts +11 -0
- package/dist/packages/core/dies.js +1 -1
- package/dist/packages/core/utils.d.ts +11 -0
- package/dist/packages/core/utils.js +1 -1
- package/dist/packages/renderer/binColors.d.ts +80 -0
- package/dist/packages/renderer/binColors.js +1 -0
- package/dist/packages/renderer/buildView.d.ts +100 -6
- package/dist/packages/renderer/buildView.js +1 -1
- package/dist/packages/renderer/buildWaferMap.d.ts +112 -7
- package/dist/packages/renderer/buildWaferMap.js +1 -1
- package/dist/packages/renderer/colorMap.d.ts +16 -15
- package/dist/packages/renderer/colorMap.js +1 -1
- package/dist/packages/renderer/colorSchemes.d.ts +45 -26
- package/dist/packages/renderer/colorSchemes.js +1 -1
- package/dist/packages/renderer/index.d.ts +3 -1
- package/dist/packages/renderer/index.js +1 -1
- package/dist/packages/stats/analyzeWaferLot.js +1 -1
- package/dist/packages/stats/analyzeWaferMap.js +1 -1
- package/dist/packages/stats/binPareto.d.ts +15 -0
- package/dist/packages/stats/binPareto.js +1 -1
- package/dist/packages/stats/clusterDetection.js +1 -1
- package/dist/packages/stats/connectedComponents.d.ts +14 -0
- package/dist/packages/stats/connectedComponents.js +1 -0
- package/dist/packages/stats/correlation.d.ts +26 -0
- package/dist/packages/stats/correlation.js +1 -1
- package/dist/packages/stats/facets.js +1 -1
- package/dist/packages/stats/filterFindings.d.ts +18 -0
- package/dist/packages/stats/filterFindings.js +1 -1
- package/dist/packages/stats/findingsNarrative.js +1 -1
- package/dist/packages/stats/histogram.d.ts +47 -2
- package/dist/packages/stats/histogram.js +1 -1
- package/dist/packages/stats/index.d.ts +6 -0
- package/dist/packages/stats/index.js +1 -1
- package/dist/packages/stats/mergeTestDefs.d.ts +44 -0
- package/dist/packages/stats/mergeTestDefs.js +1 -0
- package/dist/packages/stats/metadataColumns.d.ts +20 -4
- package/dist/packages/stats/metadataColumns.js +1 -1
- package/dist/packages/stats/patternClassification.js +1 -1
- package/dist/packages/stats/population.d.ts +23 -0
- package/dist/packages/stats/population.js +1 -0
- package/dist/packages/stats/renderFindingsReport.js +15 -15
- package/dist/packages/stats/renderSummaryReport.js +35 -32
- package/dist/packages/stats/reportHtml.js +38 -13
- package/dist/packages/stats/testPassRate.d.ts +85 -0
- package/dist/packages/stats/testPassRate.js +1 -0
- package/dist/packages/stats/trend.d.ts +40 -0
- package/dist/packages/stats/trend.js +1 -0
- package/dist/packages/stats/types.d.ts +117 -6
- package/dist/packages/stats/yield.js +1 -1
- package/llms.txt +6 -0
- package/package.json +9 -5
- package/dist/packages/canvas-adapter/metadataBadge.d.ts +0 -30
- 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&>!=="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")&>!=="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',
|
|
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
|
|
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',
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
|
124
|
-
/** Per-wafer
|
|
125
|
-
|
|
126
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
240
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|